diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..4185375 --- /dev/null +++ b/404.html @@ -0,0 +1,15 @@ + FRC Java Programming

404

Page not found

\ No newline at end of file diff --git a/assets/css/codehilite.css b/assets/css/codehilite.css new file mode 100644 index 0000000..a4dcdea --- /dev/null +++ b/assets/css/codehilite.css @@ -0,0 +1,114 @@ +/* +///////////////// +// Inline Code // +///////////////// +*/ + +.md-typeset code { + background-color: #424242; + color: #F5F5F5; + margin: 0; + padding: 0.07353em 0.29412em; + box-shadow: none; +} + +/* +///////////////// +// Code Blocks // +///////////////// +*/ + +/* +line number +*/ +.linenos { + color: #F5F5F5 !important; + background-color: #313131 !important; +} + +/* +code block background +*/ +.codehilite { + background-color: #424242 !important; +} + +/* +scroll bar size +*/ + +.md-typeset .codehilite::-webkit-scrollbar { + height: 1rem !important; +} + +/* +actual syntax highlighting +*/ +.codehilite pre { color: #FAFAFA !important; background-color: transparent !important; } +.codehilite .hll { background-color: #272822 !important; } +.codehilite .c { color: #a1a1b6 !important } /* Comment */ +.codehilite .err { color: #960050 !important; background-color: #1e0010 !important } /* Error */ +.codehilite .k { color: #66d9ef !important } /* Keyword */ +.codehilite .l { color: #ae81ff !important } /* Literal */ +.codehilite .n { color: #f8f8f2 !important } /* Name */ +.codehilite .o { color: #f92672 !important } /* Operator */ +.codehilite .p { color: #f8f8f2 !important } /* Punctuation */ +.codehilite .cm { color: #a1a1b6 !important } /* Comment.Multiline */ +.codehilite .cp { color: #a1a1b6 !important } /* Comment.Preproc */ +.codehilite .c1 { color: #a1a1b6 !important } /* Comment.Single */ +.codehilite .cs { color: #a1a1b6 !important } /* Comment.Special */ +.codehilite .ge { font-style: italic !important } /* Generic.Emph */ +.codehilite .gs { font-weight: bold !important } /* Generic.Strong */ +.codehilite .kc { color: #66d9ef !important } /* Keyword.Constant */ +.codehilite .kd { color: #66d9ef !important } /* Keyword.Declaration */ +.codehilite .kn { color: #f92672 !important } /* Keyword.Namespace */ +.codehilite .kp { color: #66d9ef !important } /* Keyword.Pseudo */ +.codehilite .kr { color: #66d9ef !important } /* Keyword.Reserved */ +.codehilite .kt { color: #66d9ef !important } /* Keyword.Type */ +.codehilite .ld { color: #e6db74 !important } /* Literal.Date */ +.codehilite .m { color: #ae81ff !important } /* Literal.Number */ +.codehilite .s { color: #e6db74 !important } /* Literal.String */ +.codehilite .na { color: #a6e22e !important } /* Name.Attribute */ +.codehilite .nb { color: #f8f8f2 !important } /* Name.Builtin */ +.codehilite .nc { color: #a6e22e !important } /* Name.Class */ +.codehilite .no { color: #66d9ef !important } /* Name.Constant */ +.codehilite .nd { color: #a6e22e !important } /* Name.Decorator */ +.codehilite .ni { color: #f8f8f2 !important } /* Name.Entity */ +.codehilite .ne { color: #a6e22e !important } /* Name.Exception */ +.codehilite .nf { color: #a6e22e !important } /* Name.Function */ +.codehilite .nl { color: #f8f8f2 !important } /* Name.Label */ +.codehilite .nn { color: #f8f8f2 !important } /* Name.Namespace */ +.codehilite .nx { color: #a6e22e !important } /* Name.Other */ +.codehilite .py { color: #f8f8f2 !important } /* Name.Property */ +.codehilite .nt { color: #f92672 !important } /* Name.Tag */ +.codehilite .nv { color: #f8f8f2 !important } /* Name.Variable */ +.codehilite .ow { color: #f92672 !important } /* Operator.Word */ +.codehilite .w { color: #f8f8f2 !important } /* Text.Whitespace */ +.codehilite .mf { color: #ae81ff !important } /* Literal.Number.Float */ +.codehilite .mh { color: #ae81ff !important } /* Literal.Number.Hex */ +.codehilite .mi { color: #ae81ff !important } /* Literal.Number.Integer */ +.codehilite .mo { color: #ae81ff !important } /* Literal.Number.Oct */ +.codehilite .sb { color: #e6db74 !important } /* Literal.String.Backtick */ +.codehilite .sc { color: #e6db74 !important } /* Literal.String.Char */ +.codehilite .sd { color: #e6db74 !important } /* Literal.String.Doc */ +.codehilite .s2 { color: #e6db74 !important } /* Literal.String.Double */ +.codehilite .se { color: #ae81ff !important } /* Literal.String.Escape */ +.codehilite .sh { color: #e6db74 !important } /* Literal.String.Heredoc */ +.codehilite .si { color: #e6db74 !important } /* Literal.String.Interpol */ +.codehilite .sx { color: #e6db74 !important } /* Literal.String.Other */ +.codehilite .sr { color: #e6db74 !important } /* Literal.String.Regex */ +.codehilite .s1 { color: #e6db74 !important } /* Literal.String.Single */ +.codehilite .ss { color: #e6db74 !important } /* Literal.String.Symbol */ +.codehilite .bp { color: #f8f8f2 !important } /* Name.Builtin.Pseudo */ +.codehilite .vc { color: #f8f8f2 !important } /* Name.Variable.Class */ +.codehilite .vg { color: #f8f8f2 !important } /* Name.Variable.Global */ +.codehilite .vi { color: #f8f8f2 !important } /* Name.Variable.Instance */ +.codehilite .il { color: #ae81ff !important } /* Literal.Number.Integer.Long */ + +.codehilite .gh { } /* Generic Heading & Diff Header */ +.codehilite .gu { color: #a1a1b6 !important ; } /* Generic.Subheading & Diff Unified/Comment? */ +.codehilite .gd { color: #f92672 !important ; } /* Generic.Deleted & Diff Deleted */ +.codehilite .gi { color: #a6e22e !important ; } /* Generic.Inserted & Diff Inserted */ + +.codehilite .md-clipboard:before { color: rgba(255, 255, 255, 0.07) } /* Clipboard button (no hover) */ +.codehilite:hover .md-clipboard:before { color: rgba(255, 255, 255, 0.54) } /* Clipboard button (hovered) */ diff --git a/assets/css/dark_theme.css b/assets/css/dark_theme.css new file mode 100644 index 0000000..9113529 --- /dev/null +++ b/assets/css/dark_theme.css @@ -0,0 +1,145 @@ +/* +////////////////// +// Main content // +////////////////// +*/ + +/* +Default text color +and background color +*/ +.md-main { + color: #F5F5F5 !important; + background-color: #212121 !important; +} + +/* +Main headlines +*/ +.md-main h1 { + color: white !important; +} + +/* +Tables +*/ +table { + background-color: #616161 !important; +} + +tbody { + background-color: #484848 !important; +} + +/* +Blockquotes +*/ +.md-typeset blockquote { + color: rgba(255,255,255,0.8) !important; + border-color: rgba(255,255,255,0.54) !important; +} + +/* +//////////////////// +// Navigation bar // +//////////////////// +*/ + +/* +Left and right toc scrollbar +*/ +.md-sidebar__scrollwrap::-webkit-scrollbar-thumb { + background-color: #E0E0E0 !important; +} + + + +.md-nav { + color: #F5F5F5 !important; + background-color: #212121 !important; +} + +/* +Arrow Left Icon +*/ +html .md-nav--primary .md-nav__title:before { + color: #FAFAFA !important; +} + +.md-nav__title { + color: rgba(255,255,255,1) !important; + background-color: #212121 !important; +} + +/* +Arrow Right Icon +*/ +.md-nav--primary .md-nav__link:after { + color: #FAFAFA !important; +} + +.md-nav__list { + color: rgba(255,255,255,1) !important; + background-color: #212121 !important; +} + +.md-nav__item { + color: rgba(255,255,255,1) !important; + background-color: #212121 !important; +} + +.md-nav__link[data-md-state=blur] { + color: rgba(255,255,255,0.54) !important; +} + +/* +//////////// +// Search // +//////////// +*/ + +/* +scroll bar + +attention: +background is scroll handle color! +*/ +.md-search__scrollwrap::-webkit-scrollbar-thumb { + background-color: #E0E0E0 !important; +} +/* +scroll bar background color +*/ +.md-search__scrollwrap { + background-color: #424242 !important; +} + +/* +Icon color +*/ +.md-search-result__article--document:before { + color: #EEEEEE !important; +} + +/* +headline color and +result list background +*/ +.md-search-result__list { + color: #EEEEEE !important; + background-color: #212121 !important; +} + +/* +result info/count +*/ +.md-search-result__meta { + background-color: #EEEEEE !important; +} + +/* +article preview text color +*/ +.md-search-result__teaser { + color: #BDBDBD !important; +} diff --git a/assets/css/sn.css b/assets/css/sn.css new file mode 100644 index 0000000..6c658b5 --- /dev/null +++ b/assets/css/sn.css @@ -0,0 +1,6 @@ +.md-header{ + background-color: #053B6C +} +.md-tabs{ + background-color: #053B6C +} \ No newline at end of file diff --git a/assets/favicon.png b/assets/favicon.png new file mode 100644 index 0000000..020a857 Binary files /dev/null and b/assets/favicon.png differ diff --git a/assets/images/contributing/edit_icon.png b/assets/images/contributing/edit_icon.png new file mode 100644 index 0000000..79aa1c3 Binary files /dev/null and b/assets/images/contributing/edit_icon.png differ diff --git a/assets/images/deploying/deploy_command.png b/assets/images/deploying/deploy_command.png new file mode 100644 index 0000000..75914da Binary files /dev/null and b/assets/images/deploying/deploy_command.png differ diff --git a/assets/images/deploying/w_icon.png b/assets/images/deploying/w_icon.png new file mode 100644 index 0000000..6850a7f Binary files /dev/null and b/assets/images/deploying/w_icon.png differ diff --git a/assets/images/driving_robot/constants/step_1.png b/assets/images/driving_robot/constants/step_1.png new file mode 100644 index 0000000..f1aab14 Binary files /dev/null and b/assets/images/driving_robot/constants/step_1.png differ diff --git a/assets/images/driving_robot/constants/step_2.png b/assets/images/driving_robot/constants/step_2.png new file mode 100644 index 0000000..3b1d27d Binary files /dev/null and b/assets/images/driving_robot/constants/step_2.png differ diff --git a/assets/images/driving_robot/constants/step_3.png b/assets/images/driving_robot/constants/step_3.png new file mode 100644 index 0000000..16c0fb2 Binary files /dev/null and b/assets/images/driving_robot/constants/step_3.png differ diff --git a/assets/images/driving_robot/constants/step_4.png b/assets/images/driving_robot/constants/step_4.png new file mode 100644 index 0000000..c764e96 Binary files /dev/null and b/assets/images/driving_robot/constants/step_4.png differ diff --git a/assets/images/driving_robot/e1.png b/assets/images/driving_robot/e1.png new file mode 100644 index 0000000..302edc3 Binary files /dev/null and b/assets/images/driving_robot/e1.png differ diff --git a/assets/images/driving_robot/e2.png b/assets/images/driving_robot/e2.png new file mode 100644 index 0000000..4d10033 Binary files /dev/null and b/assets/images/driving_robot/e2.png differ diff --git a/assets/images/driving_robot/e3.png b/assets/images/driving_robot/e3.png new file mode 100644 index 0000000..0088b54 Binary files /dev/null and b/assets/images/driving_robot/e3.png differ diff --git a/assets/images/driving_robot/kitbot.jpg b/assets/images/driving_robot/kitbot.jpg new file mode 100644 index 0000000..2255f9f Binary files /dev/null and b/assets/images/driving_robot/kitbot.jpg differ diff --git a/assets/images/driving_robot/roboRIO_port.png b/assets/images/driving_robot/roboRIO_port.png new file mode 100644 index 0000000..13552e8 Binary files /dev/null and b/assets/images/driving_robot/roboRIO_port.png differ diff --git a/assets/images/install_software/wifi.png b/assets/images/install_software/wifi.png new file mode 100644 index 0000000..ccf827a Binary files /dev/null and b/assets/images/install_software/wifi.png differ diff --git a/assets/images/logos/code.png b/assets/images/logos/code.png new file mode 100644 index 0000000..a4a0b43 Binary files /dev/null and b/assets/images/logos/code.png differ diff --git a/assets/images/logos/first.png b/assets/images/logos/first.png new file mode 100644 index 0000000..273f93e Binary files /dev/null and b/assets/images/logos/first.png differ diff --git a/assets/images/logos/first_white.png b/assets/images/logos/first_white.png new file mode 100644 index 0000000..fd52b36 Binary files /dev/null and b/assets/images/logos/first_white.png differ diff --git a/assets/images/logos/java.png b/assets/images/logos/java.png new file mode 100644 index 0000000..44d5a7e Binary files /dev/null and b/assets/images/logos/java.png differ diff --git a/assets/images/logos/java_logo.png b/assets/images/logos/java_logo.png new file mode 100644 index 0000000..946867c Binary files /dev/null and b/assets/images/logos/java_logo.png differ diff --git a/assets/images/logos/ni.png b/assets/images/logos/ni.png new file mode 100644 index 0000000..b1e1046 Binary files /dev/null and b/assets/images/logos/ni.png differ diff --git a/assets/images/logos/sn_banner.png b/assets/images/logos/sn_banner.png new file mode 100644 index 0000000..acd5794 Binary files /dev/null and b/assets/images/logos/sn_banner.png differ diff --git a/assets/images/logos/sn_banner_white.png b/assets/images/logos/sn_banner_white.png new file mode 100644 index 0000000..730d773 Binary files /dev/null and b/assets/images/logos/sn_banner_white.png differ diff --git a/assets/images/logos/wpilib.png b/assets/images/logos/wpilib.png new file mode 100644 index 0000000..68773bf Binary files /dev/null and b/assets/images/logos/wpilib.png differ diff --git a/assets/images/new_project/command/step_1.png b/assets/images/new_project/command/step_1.png new file mode 100644 index 0000000..2b51cac Binary files /dev/null and b/assets/images/new_project/command/step_1.png differ diff --git a/assets/images/new_project/command/step_2.png b/assets/images/new_project/command/step_2.png new file mode 100644 index 0000000..83978cc Binary files /dev/null and b/assets/images/new_project/command/step_2.png differ diff --git a/assets/images/new_project/command/step_3.png b/assets/images/new_project/command/step_3.png new file mode 100644 index 0000000..abe4d14 Binary files /dev/null and b/assets/images/new_project/command/step_3.png differ diff --git a/assets/images/new_project/command/step_4.png b/assets/images/new_project/command/step_4.png new file mode 100644 index 0000000..0b4deae Binary files /dev/null and b/assets/images/new_project/command/step_4.png differ diff --git a/assets/images/new_project/command/step_5.png b/assets/images/new_project/command/step_5.png new file mode 100644 index 0000000..93b29d7 Binary files /dev/null and b/assets/images/new_project/command/step_5.png differ diff --git a/assets/images/new_project/default_contents.png b/assets/images/new_project/default_contents.png new file mode 100644 index 0000000..92fe2eb Binary files /dev/null and b/assets/images/new_project/default_contents.png differ diff --git a/assets/images/new_project/project/step_1.png b/assets/images/new_project/project/step_1.png new file mode 100644 index 0000000..c5d294b Binary files /dev/null and b/assets/images/new_project/project/step_1.png differ diff --git a/assets/images/new_project/project/step_2.png b/assets/images/new_project/project/step_2.png new file mode 100644 index 0000000..c6402a7 Binary files /dev/null and b/assets/images/new_project/project/step_2.png differ diff --git a/assets/images/new_project/project/step_3.png b/assets/images/new_project/project/step_3.png new file mode 100644 index 0000000..b0b82ac Binary files /dev/null and b/assets/images/new_project/project/step_3.png differ diff --git a/assets/images/new_project/project/step_4.png b/assets/images/new_project/project/step_4.png new file mode 100644 index 0000000..aa1e8af Binary files /dev/null and b/assets/images/new_project/project/step_4.png differ diff --git a/assets/images/new_project/project/step_5.png b/assets/images/new_project/project/step_5.png new file mode 100644 index 0000000..1fa8a8d Binary files /dev/null and b/assets/images/new_project/project/step_5.png differ diff --git a/assets/images/new_project/project/step_6.png b/assets/images/new_project/project/step_6.png new file mode 100644 index 0000000..a8fe00e Binary files /dev/null and b/assets/images/new_project/project/step_6.png differ diff --git a/assets/images/new_project/subsystem/step_1.png b/assets/images/new_project/subsystem/step_1.png new file mode 100644 index 0000000..2b51cac Binary files /dev/null and b/assets/images/new_project/subsystem/step_1.png differ diff --git a/assets/images/new_project/subsystem/step_2.png b/assets/images/new_project/subsystem/step_2.png new file mode 100644 index 0000000..4038265 Binary files /dev/null and b/assets/images/new_project/subsystem/step_2.png differ diff --git a/assets/images/new_project/subsystem/step_3.png b/assets/images/new_project/subsystem/step_3.png new file mode 100644 index 0000000..28ad156 Binary files /dev/null and b/assets/images/new_project/subsystem/step_3.png differ diff --git a/assets/images/new_project/subsystem/step_4.png b/assets/images/new_project/subsystem/step_4.png new file mode 100644 index 0000000..f9cf94d Binary files /dev/null and b/assets/images/new_project/subsystem/step_4.png differ diff --git a/assets/images/new_project/subsystem/step_5.png b/assets/images/new_project/subsystem/step_5.png new file mode 100644 index 0000000..e8ab0dc Binary files /dev/null and b/assets/images/new_project/subsystem/step_5.png differ diff --git a/assets/images/new_project/subsystem/step_6.png b/assets/images/new_project/subsystem/step_6.png new file mode 100644 index 0000000..98dbea7 Binary files /dev/null and b/assets/images/new_project/subsystem/step_6.png differ diff --git a/assets/images/pneumatics/piston.png b/assets/images/pneumatics/piston.png new file mode 100644 index 0000000..8b072bd Binary files /dev/null and b/assets/images/pneumatics/piston.png differ diff --git a/assets/images/roboRIO/roboRIO_io.png b/assets/images/roboRIO/roboRIO_io.png new file mode 100644 index 0000000..2deee6f Binary files /dev/null and b/assets/images/roboRIO/roboRIO_io.png differ diff --git a/assets/images/roboRIO/roboRio.png b/assets/images/roboRIO/roboRio.png new file mode 100644 index 0000000..aa8c9c8 Binary files /dev/null and b/assets/images/roboRIO/roboRio.png differ diff --git a/assets/images/sensors/camera.png b/assets/images/sensors/camera.png new file mode 100644 index 0000000..007d369 Binary files /dev/null and b/assets/images/sensors/camera.png differ diff --git a/assets/images/sensors/encoder.png b/assets/images/sensors/encoder.png new file mode 100644 index 0000000..6bbfb5e Binary files /dev/null and b/assets/images/sensors/encoder.png differ diff --git a/assets/images/sensors/limit_switch.png b/assets/images/sensors/limit_switch.png new file mode 100644 index 0000000..6bd4ea2 Binary files /dev/null and b/assets/images/sensors/limit_switch.png differ diff --git a/assets/images/sensors/navX_micro.png b/assets/images/sensors/navX_micro.png new file mode 100644 index 0000000..38ccc7a Binary files /dev/null and b/assets/images/sensors/navX_micro.png differ diff --git a/assets/images/vscode_tips/light_bulb.png b/assets/images/vscode_tips/light_bulb.png new file mode 100644 index 0000000..356517d Binary files /dev/null and b/assets/images/vscode_tips/light_bulb.png differ diff --git a/assets/images/why_software/automation.png b/assets/images/why_software/automation.png new file mode 100644 index 0000000..9d63782 Binary files /dev/null and b/assets/images/why_software/automation.png differ diff --git a/assets/images/why_software/pi.png b/assets/images/why_software/pi.png new file mode 100644 index 0000000..d455282 Binary files /dev/null and b/assets/images/why_software/pi.png differ diff --git a/assets/images/why_software/programming_header.png b/assets/images/why_software/programming_header.png new file mode 100644 index 0000000..15dae45 Binary files /dev/null and b/assets/images/why_software/programming_header.png differ diff --git a/assets/images/why_software/smart-light.png b/assets/images/why_software/smart-light.png new file mode 100644 index 0000000..37d746e Binary files /dev/null and b/assets/images/why_software/smart-light.png differ diff --git a/basics/driverstation_tips.html b/basics/driverstation_tips.html new file mode 100644 index 0000000..f1ce2ad --- /dev/null +++ b/basics/driverstation_tips.html @@ -0,0 +1,20 @@ + Driverstation tips - FRC Java Programming

\ No newline at end of file diff --git a/basics/java_basics.html b/basics/java_basics.html new file mode 100644 index 0000000..f2f4666 --- /dev/null +++ b/basics/java_basics.html @@ -0,0 +1,51 @@ + Java Programming Basics - FRC Java Programming

Java Programming Basics#

Learning What's What

Java

Overview#

  • Objects, variables, and classes (in Java) make up our programs. We define, modify, and use these variables and objects to make our programs run.
  • Programs use key words to define characteristics of variables or objects. Basic keywords:
    • public - an object accessible by other classes (files)
    • private - an object only accessible by its containing class (file).
    • protected - like private but can be seen by subclasses
    • return - value to return or give back after method execution (run).
    • void - a method that returns no value
    • null - a value that means empty or nothing

IMPORTANT NOTE

Java is case sensitive, meaning capitalization matters!


Classes#

  • Classes are the files that contain our programming
  • A program can be made up of one class but can also be made up of many classes
    • All programs run a main class that can optionally load additional classes either directly or indirectly
    • Example

      main loads class1, class1 loads class2
  • Classes are made up of variables and methods and are often used to separate and organize your code.
  • Classes can also call (use) variables or methods of other classes if those have been set to public.

Constructors#

  • Classes can also have a constructor which is a special type of method that has the same name (case sensitive) as the class file
    • Constructors are always called when the class is loaded into the program for the first time. This is often the only time they are called.
    • Constructors are called when trying to access the class in other files.
    • They can be called again if the class is programmed to be unloaded (destroyed) and reloaded.
    • Calls to methods, and assignment of values, within the constructor will run as soon as the class is called (loaded) in the code.
    • The new operator creates an object of a type of class using a constructor
    • Example

      classObject = new className();

Methods#

  • Methods, also known as functions, can be thought of as subprograms or routines that run inside of your main program.
  • Methods are used when you want to run the same code multiple times. Copying and pasting code is BAD! Use methods instead!
  • Methods are also useful to access only certain parts or functions of another class.
  • Methods can also have their own variables (local) or use variables available throughout the whole class (global variables), this will be explained more in the scope section.
  • Methods can call (use) other methods, even multiple times.
Example
int value;
+void increment(){
+    value++;
+}

Parameters#

  • Parameters are variables that are passed (sent) to a method for it to use.
  • You can pass more than one parameter but order matters when calling the method.
Example
// Example of a method with a parameter
+double half(int num1){ 
+    double multiplier = 0.5;
+    return num1*multiplier; 
+}
+
+int newNumber = half(12); // <---- Method being called (used) in code

Variables#

  • Variables are objects that contain data, they are characterized by data types
  • Variables are assigned names and data types on creation
    • Names can be anything with the exception of pre-existing keywords such as public or int
  • Data types define what type of data is being stored in the variables:
    • int - integers (whole numbers)
    • double - double precision floating point (fractional/decimal values)
    • boolean - true or false (true = 1 or false = 0) values.
    • string - text values contained in parentheses
    • Example: int sum;

      A variable that can hold whole number values
    • Example: boolean isFull = true;

      A variable can either hold a true or false value and is being assigned a true value

Constants#

Most variables can have their values assigned or reassigned at any point elsewhere in your program. To avoid having a variable change its value during runtime you can make it a constant

  • In Java you can create constants using the static final keywords together in front of the data type of the variable
    • The static modifier causes the variable to be available without loading the class where it is defined.
    • The final modifier causes the variable to be unchangeable.
    • Java constants are normally declared in ALL CAPS. Words in Java constants are normally separated by underscores.
    • Example: public static final double PI_VALUE = 3.14159;

      A variable that cannot be modified during code run time.

Scope#

  • When creating a variable, where you create it matters. This is known as the scope of a variable.
  • The scope is where a variable can be seen within a class
    • A variable created in a method can only be seen in that method. This is a local variable.
    • A variable created outside a method can be seen in all methods of that class (file). This is a global variable.
      • It is good practice to put them all at the top before your first method.
Example of a Local Variable
public int testMethod() {
+    int example = 12; // Inside of method
+    example = example + 1;
+    return example
+}
Example of a Global Variable
int example = 12; // Outside of method
+public void testMethod() {
+    example = example + 1;
+    return example
+}

Comments#

  • Comments are a programmer-readable explanation or annotation in the source code of a program.
  • Comments do not affect what the code does.
  • Comments are often used to leave notes or explanations of what methods or classes are doing so that it is easier to understand the code.
Example: Single Line Comments
// This is what a single line comment looks like
+
+// You can also have multiple
+// single line comments in a row
Example: Multi Line Comments
/* 
+This is what a
+multiline comment
+looks like 
+*/
Example: Doc Comments
/**
+ * This is a doc comment
+ * 
+ * <ul>
+ * <li>They can be viewed by hovering over code they are attached to</li>
+ * <li>They can be formatted with HTML</li>
+ * </ul>
+*/

Conventions#

  • There are also many different conventions when programming, this ensures that programs are readable between different people.
  • A common naming convention:
    • Programming is often done in CamelCase or lowerCamelCase
    • Instead of adding spaces, capitalize the first letter of each word
    • Example

      ThreeMotorDrive, driveForward, setSpeed

Info

There are other naming conventions, but for this tutorial we will use the camel cases

« Previous Next »
\ No newline at end of file diff --git a/basics/roboRIO.html b/basics/roboRIO.html new file mode 100644 index 0000000..a14e8d3 --- /dev/null +++ b/basics/roboRIO.html @@ -0,0 +1,20 @@ + roboRIO - FRC Java Programming

roboRIO#

The Brains of the Bot!

roboRIO

The roboRIO Basics#

  • The roboRIO is the brain of an FRC robot.
  • It is the main processing unit and is where the code is stored and run.
  • It is very similar to something like a Raspberry Pi, it’s a mini computer!
  • The roboRIO can connect to many different devices such as motor controllers, servos, and sensors through its various interface connections such as:
    • Digital I/O, PWM, CAN Bus, Ethernet, USB, MXP

The roboRIO IO#

  • Digital IO (DIO) used for sensors and switches
  • PWM used for motor controllers and servos
  • CAN used for motor controllers and sensors
  • MXP used for functionality expansion
  • Check the roboRIO user manual for more details

roboRIO IO

« Previous Next »
\ No newline at end of file diff --git a/basics/sensors.html b/basics/sensors.html new file mode 100644 index 0000000..44cc015 --- /dev/null +++ b/basics/sensors.html @@ -0,0 +1,20 @@ + Sensors - FRC Java Programming

Sensors#

How does the robot see?

camera

Some types of sensors#

  • Limit Switches - detects contact
  • Camera - provides sight
  • Encoders - measures rotational or linear motion
  • Ultrasonic - measures distances
  • Gyroscope - measures orientation
  • Processed Vision - measures target's distance, angle, and offset from robot
  • For more info on sensors see: High Tech High Top Hat Technicians - Electrical Tutorial
Limit Switch Grayhill brand Quadrature Encoder Kauai Labs navX Gyro/ Accelerometer
Limit Switch Grayhill Encoder navX
« Previous Next »
\ No newline at end of file diff --git a/basics/vscode_tips.html b/basics/vscode_tips.html new file mode 100644 index 0000000..db2ffae --- /dev/null +++ b/basics/vscode_tips.html @@ -0,0 +1,31 @@ + Visual Studio Code Tips - FRC Java Programming

Visual Studio Code Tips#

Making life easier

Image Title

💡 Using the light bulb (quick fixes)#

  • When auto complete is available, click enter on the correct completion to auto import classes.
  • If this is not done an error can occur denoted by an underline on what you just typed. To fix this, click on the error and click the light bulb that pops up. Then click import.
  • From this point on this tutorial will assume you are doing this on your own so if errors occur, click the light bulb and see if it suggest importing something.
  • The light bulb can also be used to make programming easier by auto creating things for us. This tutorial will go over that in future sections
« Previous Next »
\ No newline at end of file diff --git a/basics/wpilib.html b/basics/wpilib.html new file mode 100644 index 0000000..c7e8564 --- /dev/null +++ b/basics/wpilib.html @@ -0,0 +1,34 @@ + WPILib Programming Basics - FRC Java Programming

WPILib Programming Basics#

Making FRC Programming Easy

WPI Lib

What is WPILib#

  • The WPI Robotics library (WPILib) is a set of software classes that interfaces with the hardware and software in your FRC RoboRIO.
    • There are classes to handle sensors, motor speed controllers, the driver station, and a number of other utility functions.
  • Documentation is available at http://first.wpi.edu/FRC/roborio/release/docs/java
  • WPILib adds those sensors and controllers as additional data types (like int or double) and classes.
    • Examples

      Talon, Solenoid, Encoder...

Command Based Robot#

  • For our programming tutorial we will be creating a Command based robot
  • Command Based Robots are much like Lego, with very basic pieces you can make something simple like a small house or complicated like an entire Lego city.
  • A command based robot is broken down into subsystem classes and command classes.
  • In the code, a command based robot is made up of 3 packages (folders) labeled robot, commands, and subsystems
  • There are other types of robots but we will use Command Based

Subsystems#

  • A subsystem is a special template class made by FRC.
  • In robotics, subsystems are sections of the whole robot.
    • For example every FRC robot has a Drivetrain subsystem which is what controls the robot’s driving both physically and programmatically.
      • To avoid confusion between software and mechanical teams, subsystems should be called the same thing. If we have a ball intake system, we will both call it Intake or Collector.
    • Subsystems of a robot can contain parts to control or read data from.
      • The Drivetrain subsystem could contain motor controllers and encoders both physically and programmatically.
  • Using a dog as an example: the legs, tail, and head are subsystems.
    • The head subsystem has the parts: eyes, ears, and nose.
  • When programming subsystems we use variables and methods to tell our subsystem what it has and what it is capable of or should do.
    • These variables will be the parts in the subsystem
    • These methods will define what those parts are capable of.
  • Using a dog head subsystem as an example:
    • Some variables (parts) would be: leftEye, rightEye, nose, leftEar, rightEar.
    • Some example methods would be closeEyes or openEyes since these are things the dog are capable of.
      • These methods would use both the leftEye and rightEye and close them.
    • Example
      //This method closes the dog eyes
      +public void closeEyes(){
      +    leftEye.close();
      +    rightEye.close();
  • A robot example of a Drivetrain subsystem would have leftMotor, and rightMotor as variables and setSpeed as a method telling it how to set the speed of those motor controllers.
  • Having the setSpeed method tells our program that our Drivetrain subsystem can set its speed.
    • Example
      //This method sets the speed of the drivetrain
      +public void setSpeed(double speed){
      +    leftMotor.set(speed);
      +    rightMotor.set(speed);
      +}

Commands#

  • A command is a special template class (file) made by FRC.
  • In robotics, commands are actions you want a robot to do (just like a real life command).
    • A command is an action a subsystem(s) performs.
    • For example you may want your robot to drive full speed forward so you make a command class called DriveForward.
      • Since a robot uses a Drivetrain subsystem to control its motors, this command would call our previously created setSpeed method from that subsystem.
  • Tip

    Subsystems define what the robot is made of and what it can do while commands actually tell the robot to do those things
  • Using a dog as an example we can tell the dog to blink by creating a BlinkEyes command
    • The command would call the method, closeEyes() then the method openEyes()
  • BlinkEyes Command
    //This command will continuously run the two methods in execute
    +protected void execute() {
    +    dog.head.closeEyes();
    +    dog.head.openEyes();
    +}
  • A robot example of a DriveForward command would call (use) the setSpeed methods that we created in the Drivetrain subsystem
  • DriveForward, when executed, will tell our robot to drive forward using the Drivetrain subsystem
  • DriveForward Command
    //This command tells the robot to drive forward full speed
    +protected void initialize(){
    +    robot.drivetrain.setSpeed(1.0);
    +}

Default Command Structure#

  • The template for FRC commands actually come with some pre-defined methods that have special properties for FRC robots, they are:
    • void initialize() - Methods in here are called just before this Command runs the first time.
    • void execute() - Methods in here are called repeatedly when this Command is scheduled to run
    • boolean isFinished() - When this returns true, the Command stops running execute()
    • void end() - Methods in here are called once after isFinished returns true
    • void interrupted() - Methods in here are called when another command which requires one or more of the same subsystems is scheduled to run
  • Tip

    It is good practice to call end() in interrupted()

Overview of execution#

  • In FRC programming our main class is Robot.java and all other classes (command files and subsystem files) must be loaded from Robot.java either directly or indirectly
    • Example

      Robot.java loads RobotContainer.java, RobotContainer.java loads DriveForward.java.
  • All subsystem files must be added to RobotContainer.java.
    • This loads our subsystems into the code and allow its public methods to be useable by other files such as commands later by typing RobotContainer.nameOfSubsystem.desiredMethod();

New Project Files#

See Default Project Contents


Summary#

  • Command based robots are broken down into subsystems and commands
  • Subsystems define what the robot is made of and what it can do while commands actually tell the robot to do those things
  • All classes must directly or indirectly connect to Robot.java.
    • All Subsystems must be added to RobotContainer.java
  • RobotMap.java holds port numbers and IDs accessible throughout the program by typing: RobotMap.NameOfMotor()
  • RobotContainer.java contains our publicly accessible instances of our subsystems. It also connects our commands to physical controllers.
« Previous Next »
\ No newline at end of file diff --git a/contributing.html b/contributing.html new file mode 100644 index 0000000..6d663bb --- /dev/null +++ b/contributing.html @@ -0,0 +1,52 @@ + Contributing - FRC Java Programming

Contributing#

Helping out with the project!

Example Project Code#

If you make Example Project Code changes please contribute changes that reflect this in the Documentation. This will make it easier for us and more likely that your contribution will be approved.

Documentation#

If you make documentation changes please contribute changes that reflect this in the Example Project Code. This will make it easier for us and more likely that your contribution will be approved.

There are a couple of ways to contribute to this project:


Via the web#

Editing Pages#

On each page there is an option to edit the page. Any changes you make through this option will be submitted and become live once they are approved.

The edit icon looks like this:

Alternatively you could create a pull request and clone the repository

New Pages#

You can help the project by making new pages. Any pages you make will become live once they are approved.

Click here to create a new page

Please use the New Page Template

Click here to see tips on creating markdown documents

Warning

Make sure all documentation files end in .md

Tip

You can add to a certain tab by appending /tab_name/ to the file name

Tip

Visit Admonitions (call-out) references for a list off call-outs like this one.


Via local source#

Prerequisites#

  1. Install GitHub Desktop (Beginner) or Install Git (Expert)
  2. Install Python
  3. Install pip requirements
    1. Run one of the following commands. Try each one in order until successful.
      • pip install -r requirements.txt
      • python -m pip install -r requirements.txt
      • py -m pip install -r requirements.txt

Creating local edits#

  1. Visit https://github.com/FRCTeam3255/FRC-Java-Tutorial/tree/main/ and fork the repository.
  2. Clone your the newly created fork to your machine and open it
  3. Run the command mkdocs serve to open up a live local version of the project in your browser
  4. If mkdocs serve does not work on its own, try each one in order until successful:
    • python -m mkdocs serve
    • py -m mkdocs serve
  5. Make your changes or additions in the docs directory.
  6. Please maintain the organizational folder structure.
  7. If added a new page, add the relative url to the mkdocs.yml file in the # Navigation (nav:) section.
  8. For new pages please use the New Page Template
  9. Click here to see tips on creating markdown documents

Pushing your local edits to the web#

  1. Commit your changes
  2. Push your changes to GitHub
  3. Back on the webpage for your fork of the project select Pull Request
  4. Create a new pull request
  5. Wait for the pull request to be approved.

New Page Template#

Please copy this code as a template to create your new page

# Page title
+<!-- This page was contributed by:  -->
+
+Subtitle
+
+<!-- Add a page image to make it pretty! -->
+![Image Title](imageURL)
+
+## Overview
+
+This section will help you learn to BLANK.
+
+**See table of contents for a breakdown of this section.**
+
+***
+
+## Section One
+
+- Some info
+- Some other into
+    - Some sub info
+
+### Section One Subsection
+
+***
+
+## Section Two
+
+- Info
+- Info 2
+
+!!! Tip
+    This is a tip.
« Previous
\ No newline at end of file diff --git a/css/fonts/Roboto-Slab-Bold.woff b/css/fonts/Roboto-Slab-Bold.woff new file mode 100644 index 0000000..6cb6000 Binary files /dev/null and b/css/fonts/Roboto-Slab-Bold.woff differ diff --git a/css/fonts/Roboto-Slab-Bold.woff2 b/css/fonts/Roboto-Slab-Bold.woff2 new file mode 100644 index 0000000..7059e23 Binary files /dev/null and b/css/fonts/Roboto-Slab-Bold.woff2 differ diff --git a/css/fonts/Roboto-Slab-Regular.woff b/css/fonts/Roboto-Slab-Regular.woff new file mode 100644 index 0000000..f815f63 Binary files /dev/null and b/css/fonts/Roboto-Slab-Regular.woff differ diff --git a/css/fonts/Roboto-Slab-Regular.woff2 b/css/fonts/Roboto-Slab-Regular.woff2 new file mode 100644 index 0000000..f2c76e5 Binary files /dev/null and b/css/fonts/Roboto-Slab-Regular.woff2 differ diff --git a/css/fonts/fontawesome-webfont.eot b/css/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..e9f60ca Binary files /dev/null and b/css/fonts/fontawesome-webfont.eot differ diff --git a/css/fonts/fontawesome-webfont.svg b/css/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..855c845 --- /dev/null +++ b/css/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/css/fonts/fontawesome-webfont.ttf b/css/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..35acda2 Binary files /dev/null and b/css/fonts/fontawesome-webfont.ttf differ diff --git a/css/fonts/fontawesome-webfont.woff b/css/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..400014a Binary files /dev/null and b/css/fonts/fontawesome-webfont.woff differ diff --git a/css/fonts/fontawesome-webfont.woff2 b/css/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000..4d13fc6 Binary files /dev/null and b/css/fonts/fontawesome-webfont.woff2 differ diff --git a/css/fonts/lato-bold-italic.woff b/css/fonts/lato-bold-italic.woff new file mode 100644 index 0000000..88ad05b Binary files /dev/null and b/css/fonts/lato-bold-italic.woff differ diff --git a/css/fonts/lato-bold-italic.woff2 b/css/fonts/lato-bold-italic.woff2 new file mode 100644 index 0000000..c4e3d80 Binary files /dev/null and b/css/fonts/lato-bold-italic.woff2 differ diff --git a/css/fonts/lato-bold.woff b/css/fonts/lato-bold.woff new file mode 100644 index 0000000..c6dff51 Binary files /dev/null and b/css/fonts/lato-bold.woff differ diff --git a/css/fonts/lato-bold.woff2 b/css/fonts/lato-bold.woff2 new file mode 100644 index 0000000..bb19504 Binary files /dev/null and b/css/fonts/lato-bold.woff2 differ diff --git a/css/fonts/lato-normal-italic.woff b/css/fonts/lato-normal-italic.woff new file mode 100644 index 0000000..76114bc Binary files /dev/null and b/css/fonts/lato-normal-italic.woff differ diff --git a/css/fonts/lato-normal-italic.woff2 b/css/fonts/lato-normal-italic.woff2 new file mode 100644 index 0000000..3404f37 Binary files /dev/null and b/css/fonts/lato-normal-italic.woff2 differ diff --git a/css/fonts/lato-normal.woff b/css/fonts/lato-normal.woff new file mode 100644 index 0000000..ae1307f Binary files /dev/null and b/css/fonts/lato-normal.woff differ diff --git a/css/fonts/lato-normal.woff2 b/css/fonts/lato-normal.woff2 new file mode 100644 index 0000000..3bf9843 Binary files /dev/null and b/css/fonts/lato-normal.woff2 differ diff --git a/css/theme.css b/css/theme.css new file mode 100644 index 0000000..ad77300 --- /dev/null +++ b/css/theme.css @@ -0,0 +1,13 @@ +/* + * This file is copied from the upstream ReadTheDocs Sphinx + * theme. To aid upgradability this file should *not* be edited. + * modifications we need should be included in theme_extra.css. + * + * https://github.com/readthedocs/sphinx_rtd_theme + */ + + /* sphinx_rtd_theme version 1.2.0 | MIT license */ +html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}[hidden],audio:not([controls]){display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:0}dfn{font-style:italic}ins{background:#ff9;text-decoration:none}ins,mark{color:#000}mark{background:#ff0;font-style:italic;font-weight:700}.rst-content code,.rst-content tt,code,kbd,pre,samp{font-family:monospace,serif;_font-family:courier new,monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:after,q:before{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}dl,ol,ul{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure,form{margin:0}label{cursor:pointer}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type=button],input[type=reset],input[type=submit]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}textarea{resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{body,html,section{background:none!important}*{box-shadow:none!important;text-shadow:none!important;filter:none!important;-ms-filter:none!important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}.rst-content .toctree-wrapper>p.caption,h2,h3,p{orphans:3;widows:3}.rst-content .toctree-wrapper>p.caption,h2,h3{page-break-after:avoid}}.btn,.fa:before,.icon:before,.rst-content .admonition,.rst-content .admonition-title:before,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .code-block-caption .headerlink:before,.rst-content .danger,.rst-content .eqno .headerlink:before,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-alert,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before,input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:FontAwesome;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713);src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix&v=4.7.0) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#fontawesomeregular) format("svg");font-weight:400;font-style:normal}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:.08em solid #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa-pull-left.icon,.fa.fa-pull-left,.rst-content .code-block-caption .fa-pull-left.headerlink,.rst-content .eqno .fa-pull-left.headerlink,.rst-content .fa-pull-left.admonition-title,.rst-content code.download span.fa-pull-left:first-child,.rst-content dl dt .fa-pull-left.headerlink,.rst-content h1 .fa-pull-left.headerlink,.rst-content h2 .fa-pull-left.headerlink,.rst-content h3 .fa-pull-left.headerlink,.rst-content h4 .fa-pull-left.headerlink,.rst-content h5 .fa-pull-left.headerlink,.rst-content h6 .fa-pull-left.headerlink,.rst-content p .fa-pull-left.headerlink,.rst-content table>caption .fa-pull-left.headerlink,.rst-content tt.download span.fa-pull-left:first-child,.wy-menu-vertical li.current>a button.fa-pull-left.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-left.toctree-expand,.wy-menu-vertical li button.fa-pull-left.toctree-expand{margin-right:.3em}.fa-pull-right.icon,.fa.fa-pull-right,.rst-content .code-block-caption .fa-pull-right.headerlink,.rst-content .eqno .fa-pull-right.headerlink,.rst-content .fa-pull-right.admonition-title,.rst-content code.download span.fa-pull-right:first-child,.rst-content dl dt .fa-pull-right.headerlink,.rst-content h1 .fa-pull-right.headerlink,.rst-content h2 .fa-pull-right.headerlink,.rst-content h3 .fa-pull-right.headerlink,.rst-content h4 .fa-pull-right.headerlink,.rst-content h5 .fa-pull-right.headerlink,.rst-content h6 .fa-pull-right.headerlink,.rst-content p .fa-pull-right.headerlink,.rst-content table>caption .fa-pull-right.headerlink,.rst-content tt.download span.fa-pull-right:first-child,.wy-menu-vertical li.current>a button.fa-pull-right.toctree-expand,.wy-menu-vertical li.on a button.fa-pull-right.toctree-expand,.wy-menu-vertical li button.fa-pull-right.toctree-expand{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left,.pull-left.icon,.rst-content .code-block-caption .pull-left.headerlink,.rst-content .eqno .pull-left.headerlink,.rst-content .pull-left.admonition-title,.rst-content code.download span.pull-left:first-child,.rst-content dl dt .pull-left.headerlink,.rst-content h1 .pull-left.headerlink,.rst-content h2 .pull-left.headerlink,.rst-content h3 .pull-left.headerlink,.rst-content h4 .pull-left.headerlink,.rst-content h5 .pull-left.headerlink,.rst-content h6 .pull-left.headerlink,.rst-content p .pull-left.headerlink,.rst-content table>caption .pull-left.headerlink,.rst-content tt.download span.pull-left:first-child,.wy-menu-vertical li.current>a button.pull-left.toctree-expand,.wy-menu-vertical li.on a button.pull-left.toctree-expand,.wy-menu-vertical li button.pull-left.toctree-expand{margin-right:.3em}.fa.pull-right,.pull-right.icon,.rst-content .code-block-caption .pull-right.headerlink,.rst-content .eqno .pull-right.headerlink,.rst-content .pull-right.admonition-title,.rst-content code.download span.pull-right:first-child,.rst-content dl dt .pull-right.headerlink,.rst-content h1 .pull-right.headerlink,.rst-content h2 .pull-right.headerlink,.rst-content h3 .pull-right.headerlink,.rst-content h4 .pull-right.headerlink,.rst-content h5 .pull-right.headerlink,.rst-content h6 .pull-right.headerlink,.rst-content p .pull-right.headerlink,.rst-content table>caption .pull-right.headerlink,.rst-content tt.download span.pull-right:first-child,.wy-menu-vertical li.current>a button.pull-right.toctree-expand,.wy-menu-vertical li.on a button.pull-right.toctree-expand,.wy-menu-vertical li button.pull-right.toctree-expand{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s linear infinite;animation:fa-spin 2s linear infinite}.fa-pulse{-webkit-animation:fa-spin 1s steps(8) infinite;animation:fa-spin 1s steps(8) infinite}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);-ms-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scaleY(-1);-ms-transform:scaleY(-1);transform:scaleY(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before,.icon-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-close:before,.fa-remove:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-cog:before,.fa-gear:before{content:""}.fa-trash-o:before{content:""}.fa-home:before,.icon-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before,.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-repeat:before,.fa-rotate-right:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before,.icon-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:""}.fa-check-circle:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before,.rst-content .admonition-title:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before,.icon-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-exclamation-triangle:before,.fa-warning:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-cogs:before,.fa-gears:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before,.icon-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before,.icon-circle-arrow-left:before{content:""}.fa-arrow-circle-right:before,.icon-circle-arrow-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before,.icon-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-floppy-o:before,.fa-save:before{content:""}.fa-square:before{content:""}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before,.icon-caret-down:before,.wy-dropdown .caret:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-sort:before,.fa-unsorted:before{content:""}.fa-sort-desc:before,.fa-sort-down:before{content:""}.fa-sort-asc:before,.fa-sort-up:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-gavel:before,.fa-legal:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-bolt:before,.fa-flash:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-clipboard:before,.fa-paste:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-chain-broken:before,.fa-unlink:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:""}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:""}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:""}.fa-eur:before,.fa-euro:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-inr:before,.fa-rupee:before{content:""}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:""}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:""}.fa-krw:before,.fa-won:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before,.icon-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-try:before,.fa-turkish-lira:before{content:""}.fa-plus-square-o:before,.wy-menu-vertical li button.toctree-expand:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-bank:before,.fa-institution:before,.fa-university:before{content:""}.fa-graduation-cap:before,.fa-mortar-board:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:""}.fa-file-archive-o:before,.fa-file-zip-o:before{content:""}.fa-file-audio-o:before,.fa-file-sound-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:""}.fa-empire:before,.fa-ge:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-paper-plane:before,.fa-send:before{content:""}.fa-paper-plane-o:before,.fa-send-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-bed:before,.fa-hotel:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-y-combinator:before,.fa-yc:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-television:before,.fa-tv:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before,.icon-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:""}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-sign-language:before,.fa-signing:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-address-card:before,.fa-vcard:before{content:""}.fa-address-card-o:before,.fa-vcard-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.fa,.icon,.rst-content .admonition-title,.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content code.download span:first-child,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink,.rst-content tt.download span:first-child,.wy-dropdown .caret,.wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li button.toctree-expand{font-family:inherit}.fa:before,.icon:before,.rst-content .admonition-title:before,.rst-content .code-block-caption .headerlink:before,.rst-content .eqno .headerlink:before,.rst-content code.download span:first-child:before,.rst-content dl dt .headerlink:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content p.caption .headerlink:before,.rst-content p .headerlink:before,.rst-content table>caption .headerlink:before,.rst-content tt.download span:first-child:before,.wy-dropdown .caret:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-menu-vertical li.current>a button.toctree-expand:before,.wy-menu-vertical li.on a button.toctree-expand:before,.wy-menu-vertical li button.toctree-expand:before{font-family:FontAwesome;display:inline-block;font-style:normal;font-weight:400;line-height:1;text-decoration:inherit}.rst-content .code-block-caption a .headerlink,.rst-content .eqno a .headerlink,.rst-content a .admonition-title,.rst-content code.download a span:first-child,.rst-content dl dt a .headerlink,.rst-content h1 a .headerlink,.rst-content h2 a .headerlink,.rst-content h3 a .headerlink,.rst-content h4 a .headerlink,.rst-content h5 a .headerlink,.rst-content h6 a .headerlink,.rst-content p.caption a .headerlink,.rst-content p a .headerlink,.rst-content table>caption a .headerlink,.rst-content tt.download a span:first-child,.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand,.wy-menu-vertical li a button.toctree-expand,a .fa,a .icon,a .rst-content .admonition-title,a .rst-content .code-block-caption .headerlink,a .rst-content .eqno .headerlink,a .rst-content code.download span:first-child,a .rst-content dl dt .headerlink,a .rst-content h1 .headerlink,a .rst-content h2 .headerlink,a .rst-content h3 .headerlink,a .rst-content h4 .headerlink,a .rst-content h5 .headerlink,a .rst-content h6 .headerlink,a .rst-content p.caption .headerlink,a .rst-content p .headerlink,a .rst-content table>caption .headerlink,a .rst-content tt.download span:first-child,a .wy-menu-vertical li button.toctree-expand{display:inline-block;text-decoration:inherit}.btn .fa,.btn .icon,.btn .rst-content .admonition-title,.btn .rst-content .code-block-caption .headerlink,.btn .rst-content .eqno .headerlink,.btn .rst-content code.download span:first-child,.btn .rst-content dl dt .headerlink,.btn .rst-content h1 .headerlink,.btn .rst-content h2 .headerlink,.btn .rst-content h3 .headerlink,.btn .rst-content h4 .headerlink,.btn .rst-content h5 .headerlink,.btn .rst-content h6 .headerlink,.btn .rst-content p .headerlink,.btn .rst-content table>caption .headerlink,.btn .rst-content tt.download span:first-child,.btn .wy-menu-vertical li.current>a button.toctree-expand,.btn .wy-menu-vertical li.on a button.toctree-expand,.btn .wy-menu-vertical li button.toctree-expand,.nav .fa,.nav .icon,.nav .rst-content .admonition-title,.nav .rst-content .code-block-caption .headerlink,.nav .rst-content .eqno .headerlink,.nav .rst-content code.download span:first-child,.nav .rst-content dl dt .headerlink,.nav .rst-content h1 .headerlink,.nav .rst-content h2 .headerlink,.nav .rst-content h3 .headerlink,.nav .rst-content h4 .headerlink,.nav .rst-content h5 .headerlink,.nav .rst-content h6 .headerlink,.nav .rst-content p .headerlink,.nav .rst-content table>caption .headerlink,.nav .rst-content tt.download span:first-child,.nav .wy-menu-vertical li.current>a button.toctree-expand,.nav .wy-menu-vertical li.on a button.toctree-expand,.nav .wy-menu-vertical li button.toctree-expand,.rst-content .btn .admonition-title,.rst-content .code-block-caption .btn .headerlink,.rst-content .code-block-caption .nav .headerlink,.rst-content .eqno .btn .headerlink,.rst-content .eqno .nav .headerlink,.rst-content .nav .admonition-title,.rst-content code.download .btn span:first-child,.rst-content code.download .nav span:first-child,.rst-content dl dt .btn .headerlink,.rst-content dl dt .nav .headerlink,.rst-content h1 .btn .headerlink,.rst-content h1 .nav .headerlink,.rst-content h2 .btn .headerlink,.rst-content h2 .nav .headerlink,.rst-content h3 .btn .headerlink,.rst-content h3 .nav .headerlink,.rst-content h4 .btn .headerlink,.rst-content h4 .nav .headerlink,.rst-content h5 .btn .headerlink,.rst-content h5 .nav .headerlink,.rst-content h6 .btn .headerlink,.rst-content h6 .nav .headerlink,.rst-content p .btn .headerlink,.rst-content p .nav .headerlink,.rst-content table>caption .btn .headerlink,.rst-content table>caption .nav .headerlink,.rst-content tt.download .btn span:first-child,.rst-content tt.download .nav span:first-child,.wy-menu-vertical li .btn button.toctree-expand,.wy-menu-vertical li.current>a .btn button.toctree-expand,.wy-menu-vertical li.current>a .nav button.toctree-expand,.wy-menu-vertical li .nav button.toctree-expand,.wy-menu-vertical li.on a .btn button.toctree-expand,.wy-menu-vertical li.on a .nav button.toctree-expand{display:inline}.btn .fa-large.icon,.btn .fa.fa-large,.btn .rst-content .code-block-caption .fa-large.headerlink,.btn .rst-content .eqno .fa-large.headerlink,.btn .rst-content .fa-large.admonition-title,.btn .rst-content code.download span.fa-large:first-child,.btn .rst-content dl dt .fa-large.headerlink,.btn .rst-content h1 .fa-large.headerlink,.btn .rst-content h2 .fa-large.headerlink,.btn .rst-content h3 .fa-large.headerlink,.btn .rst-content h4 .fa-large.headerlink,.btn .rst-content h5 .fa-large.headerlink,.btn .rst-content h6 .fa-large.headerlink,.btn .rst-content p .fa-large.headerlink,.btn .rst-content table>caption .fa-large.headerlink,.btn .rst-content tt.download span.fa-large:first-child,.btn .wy-menu-vertical li button.fa-large.toctree-expand,.nav .fa-large.icon,.nav .fa.fa-large,.nav .rst-content .code-block-caption .fa-large.headerlink,.nav .rst-content .eqno .fa-large.headerlink,.nav .rst-content .fa-large.admonition-title,.nav .rst-content code.download span.fa-large:first-child,.nav .rst-content dl dt .fa-large.headerlink,.nav .rst-content h1 .fa-large.headerlink,.nav .rst-content h2 .fa-large.headerlink,.nav .rst-content h3 .fa-large.headerlink,.nav .rst-content h4 .fa-large.headerlink,.nav .rst-content h5 .fa-large.headerlink,.nav .rst-content h6 .fa-large.headerlink,.nav .rst-content p .fa-large.headerlink,.nav .rst-content table>caption .fa-large.headerlink,.nav .rst-content tt.download span.fa-large:first-child,.nav .wy-menu-vertical li button.fa-large.toctree-expand,.rst-content .btn .fa-large.admonition-title,.rst-content .code-block-caption .btn .fa-large.headerlink,.rst-content .code-block-caption .nav .fa-large.headerlink,.rst-content .eqno .btn .fa-large.headerlink,.rst-content .eqno .nav .fa-large.headerlink,.rst-content .nav .fa-large.admonition-title,.rst-content code.download .btn span.fa-large:first-child,.rst-content code.download .nav span.fa-large:first-child,.rst-content dl dt .btn .fa-large.headerlink,.rst-content dl dt .nav .fa-large.headerlink,.rst-content h1 .btn .fa-large.headerlink,.rst-content h1 .nav .fa-large.headerlink,.rst-content h2 .btn .fa-large.headerlink,.rst-content h2 .nav .fa-large.headerlink,.rst-content h3 .btn .fa-large.headerlink,.rst-content h3 .nav .fa-large.headerlink,.rst-content h4 .btn .fa-large.headerlink,.rst-content h4 .nav .fa-large.headerlink,.rst-content h5 .btn .fa-large.headerlink,.rst-content h5 .nav .fa-large.headerlink,.rst-content h6 .btn .fa-large.headerlink,.rst-content h6 .nav .fa-large.headerlink,.rst-content p .btn .fa-large.headerlink,.rst-content p .nav .fa-large.headerlink,.rst-content table>caption .btn .fa-large.headerlink,.rst-content table>caption .nav .fa-large.headerlink,.rst-content tt.download .btn span.fa-large:first-child,.rst-content tt.download .nav span.fa-large:first-child,.wy-menu-vertical li .btn button.fa-large.toctree-expand,.wy-menu-vertical li .nav button.fa-large.toctree-expand{line-height:.9em}.btn .fa-spin.icon,.btn .fa.fa-spin,.btn .rst-content .code-block-caption .fa-spin.headerlink,.btn .rst-content .eqno .fa-spin.headerlink,.btn .rst-content .fa-spin.admonition-title,.btn .rst-content code.download span.fa-spin:first-child,.btn .rst-content dl dt .fa-spin.headerlink,.btn .rst-content h1 .fa-spin.headerlink,.btn .rst-content h2 .fa-spin.headerlink,.btn .rst-content h3 .fa-spin.headerlink,.btn .rst-content h4 .fa-spin.headerlink,.btn .rst-content h5 .fa-spin.headerlink,.btn .rst-content h6 .fa-spin.headerlink,.btn .rst-content p .fa-spin.headerlink,.btn .rst-content table>caption .fa-spin.headerlink,.btn .rst-content tt.download span.fa-spin:first-child,.btn .wy-menu-vertical li button.fa-spin.toctree-expand,.nav .fa-spin.icon,.nav .fa.fa-spin,.nav .rst-content .code-block-caption .fa-spin.headerlink,.nav .rst-content .eqno .fa-spin.headerlink,.nav .rst-content .fa-spin.admonition-title,.nav .rst-content code.download span.fa-spin:first-child,.nav .rst-content dl dt .fa-spin.headerlink,.nav .rst-content h1 .fa-spin.headerlink,.nav .rst-content h2 .fa-spin.headerlink,.nav .rst-content h3 .fa-spin.headerlink,.nav .rst-content h4 .fa-spin.headerlink,.nav .rst-content h5 .fa-spin.headerlink,.nav .rst-content h6 .fa-spin.headerlink,.nav .rst-content p .fa-spin.headerlink,.nav .rst-content table>caption .fa-spin.headerlink,.nav .rst-content tt.download span.fa-spin:first-child,.nav .wy-menu-vertical li button.fa-spin.toctree-expand,.rst-content .btn .fa-spin.admonition-title,.rst-content .code-block-caption .btn .fa-spin.headerlink,.rst-content .code-block-caption .nav .fa-spin.headerlink,.rst-content .eqno .btn .fa-spin.headerlink,.rst-content .eqno .nav .fa-spin.headerlink,.rst-content .nav .fa-spin.admonition-title,.rst-content code.download .btn span.fa-spin:first-child,.rst-content code.download .nav span.fa-spin:first-child,.rst-content dl dt .btn .fa-spin.headerlink,.rst-content dl dt .nav .fa-spin.headerlink,.rst-content h1 .btn .fa-spin.headerlink,.rst-content h1 .nav .fa-spin.headerlink,.rst-content h2 .btn .fa-spin.headerlink,.rst-content h2 .nav .fa-spin.headerlink,.rst-content h3 .btn .fa-spin.headerlink,.rst-content h3 .nav .fa-spin.headerlink,.rst-content h4 .btn .fa-spin.headerlink,.rst-content h4 .nav .fa-spin.headerlink,.rst-content h5 .btn .fa-spin.headerlink,.rst-content h5 .nav .fa-spin.headerlink,.rst-content h6 .btn .fa-spin.headerlink,.rst-content h6 .nav .fa-spin.headerlink,.rst-content p .btn .fa-spin.headerlink,.rst-content p .nav .fa-spin.headerlink,.rst-content table>caption .btn .fa-spin.headerlink,.rst-content table>caption .nav .fa-spin.headerlink,.rst-content tt.download .btn span.fa-spin:first-child,.rst-content tt.download .nav span.fa-spin:first-child,.wy-menu-vertical li .btn button.fa-spin.toctree-expand,.wy-menu-vertical li .nav button.fa-spin.toctree-expand{display:inline-block}.btn.fa:before,.btn.icon:before,.rst-content .btn.admonition-title:before,.rst-content .code-block-caption .btn.headerlink:before,.rst-content .eqno .btn.headerlink:before,.rst-content code.download span.btn:first-child:before,.rst-content dl dt .btn.headerlink:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content p .btn.headerlink:before,.rst-content table>caption .btn.headerlink:before,.rst-content tt.download span.btn:first-child:before,.wy-menu-vertical li button.btn.toctree-expand:before{opacity:.5;-webkit-transition:opacity .05s ease-in;-moz-transition:opacity .05s ease-in;transition:opacity .05s ease-in}.btn.fa:hover:before,.btn.icon:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content .code-block-caption .btn.headerlink:hover:before,.rst-content .eqno .btn.headerlink:hover:before,.rst-content code.download span.btn:first-child:hover:before,.rst-content dl dt .btn.headerlink:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content p .btn.headerlink:hover:before,.rst-content table>caption .btn.headerlink:hover:before,.rst-content tt.download span.btn:first-child:hover:before,.wy-menu-vertical li button.btn.toctree-expand:hover:before{opacity:1}.btn-mini .fa:before,.btn-mini .icon:before,.btn-mini .rst-content .admonition-title:before,.btn-mini .rst-content .code-block-caption .headerlink:before,.btn-mini .rst-content .eqno .headerlink:before,.btn-mini .rst-content code.download span:first-child:before,.btn-mini .rst-content dl dt .headerlink:before,.btn-mini .rst-content h1 .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.btn-mini .rst-content p .headerlink:before,.btn-mini .rst-content table>caption .headerlink:before,.btn-mini .rst-content tt.download span:first-child:before,.btn-mini .wy-menu-vertical li button.toctree-expand:before,.rst-content .btn-mini .admonition-title:before,.rst-content .code-block-caption .btn-mini .headerlink:before,.rst-content .eqno .btn-mini .headerlink:before,.rst-content code.download .btn-mini span:first-child:before,.rst-content dl dt .btn-mini .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.rst-content p .btn-mini .headerlink:before,.rst-content table>caption .btn-mini .headerlink:before,.rst-content tt.download .btn-mini span:first-child:before,.wy-menu-vertical li .btn-mini button.toctree-expand:before{font-size:14px;vertical-align:-15%}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning,.wy-alert{padding:12px;line-height:24px;margin-bottom:24px;background:#e7f2fa}.rst-content .admonition-title,.wy-alert-title{font-weight:700;display:block;color:#fff;background:#6ab0de;padding:6px 12px;margin:-12px -12px 12px}.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.admonition,.rst-content .wy-alert-danger.admonition-todo,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.seealso,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning,.wy-alert.wy-alert-danger{background:#fdf3f2}.rst-content .danger .admonition-title,.rst-content .danger .wy-alert-title,.rst-content .error .admonition-title,.rst-content .error .wy-alert-title,.rst-content .wy-alert-danger.admonition-todo .admonition-title,.rst-content .wy-alert-danger.admonition-todo .wy-alert-title,.rst-content .wy-alert-danger.admonition .admonition-title,.rst-content .wy-alert-danger.admonition .wy-alert-title,.rst-content .wy-alert-danger.attention .admonition-title,.rst-content .wy-alert-danger.attention .wy-alert-title,.rst-content .wy-alert-danger.caution .admonition-title,.rst-content .wy-alert-danger.caution .wy-alert-title,.rst-content .wy-alert-danger.hint .admonition-title,.rst-content .wy-alert-danger.hint .wy-alert-title,.rst-content .wy-alert-danger.important .admonition-title,.rst-content .wy-alert-danger.important .wy-alert-title,.rst-content .wy-alert-danger.note .admonition-title,.rst-content .wy-alert-danger.note .wy-alert-title,.rst-content .wy-alert-danger.seealso .admonition-title,.rst-content .wy-alert-danger.seealso .wy-alert-title,.rst-content .wy-alert-danger.tip .admonition-title,.rst-content .wy-alert-danger.tip .wy-alert-title,.rst-content .wy-alert-danger.warning .admonition-title,.rst-content .wy-alert-danger.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-danger .admonition-title,.wy-alert.wy-alert-danger .rst-content .admonition-title,.wy-alert.wy-alert-danger .wy-alert-title{background:#f29f97}.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .warning,.rst-content .wy-alert-warning.admonition,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.note,.rst-content .wy-alert-warning.seealso,.rst-content .wy-alert-warning.tip,.wy-alert.wy-alert-warning{background:#ffedcc}.rst-content .admonition-todo .admonition-title,.rst-content .admonition-todo .wy-alert-title,.rst-content .attention .admonition-title,.rst-content .attention .wy-alert-title,.rst-content .caution .admonition-title,.rst-content .caution .wy-alert-title,.rst-content .warning .admonition-title,.rst-content .warning .wy-alert-title,.rst-content .wy-alert-warning.admonition .admonition-title,.rst-content .wy-alert-warning.admonition .wy-alert-title,.rst-content .wy-alert-warning.danger .admonition-title,.rst-content .wy-alert-warning.danger .wy-alert-title,.rst-content .wy-alert-warning.error .admonition-title,.rst-content .wy-alert-warning.error .wy-alert-title,.rst-content .wy-alert-warning.hint .admonition-title,.rst-content .wy-alert-warning.hint .wy-alert-title,.rst-content .wy-alert-warning.important .admonition-title,.rst-content .wy-alert-warning.important .wy-alert-title,.rst-content .wy-alert-warning.note .admonition-title,.rst-content .wy-alert-warning.note .wy-alert-title,.rst-content .wy-alert-warning.seealso .admonition-title,.rst-content .wy-alert-warning.seealso .wy-alert-title,.rst-content .wy-alert-warning.tip .admonition-title,.rst-content .wy-alert-warning.tip .wy-alert-title,.rst-content .wy-alert.wy-alert-warning .admonition-title,.wy-alert.wy-alert-warning .rst-content .admonition-title,.wy-alert.wy-alert-warning .wy-alert-title{background:#f0b37e}.rst-content .note,.rst-content .seealso,.rst-content .wy-alert-info.admonition,.rst-content .wy-alert-info.admonition-todo,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .wy-alert-info.hint,.rst-content .wy-alert-info.important,.rst-content .wy-alert-info.tip,.rst-content .wy-alert-info.warning,.wy-alert.wy-alert-info{background:#e7f2fa}.rst-content .note .admonition-title,.rst-content .note .wy-alert-title,.rst-content .seealso .admonition-title,.rst-content .seealso .wy-alert-title,.rst-content .wy-alert-info.admonition-todo .admonition-title,.rst-content .wy-alert-info.admonition-todo .wy-alert-title,.rst-content .wy-alert-info.admonition .admonition-title,.rst-content .wy-alert-info.admonition .wy-alert-title,.rst-content .wy-alert-info.attention .admonition-title,.rst-content .wy-alert-info.attention .wy-alert-title,.rst-content .wy-alert-info.caution .admonition-title,.rst-content .wy-alert-info.caution .wy-alert-title,.rst-content .wy-alert-info.danger .admonition-title,.rst-content .wy-alert-info.danger .wy-alert-title,.rst-content .wy-alert-info.error .admonition-title,.rst-content .wy-alert-info.error .wy-alert-title,.rst-content .wy-alert-info.hint .admonition-title,.rst-content .wy-alert-info.hint .wy-alert-title,.rst-content .wy-alert-info.important .admonition-title,.rst-content .wy-alert-info.important .wy-alert-title,.rst-content .wy-alert-info.tip .admonition-title,.rst-content .wy-alert-info.tip .wy-alert-title,.rst-content .wy-alert-info.warning .admonition-title,.rst-content .wy-alert-info.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-info .admonition-title,.wy-alert.wy-alert-info .rst-content .admonition-title,.wy-alert.wy-alert-info .wy-alert-title{background:#6ab0de}.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-success.admonition,.rst-content .wy-alert-success.admonition-todo,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.seealso,.rst-content .wy-alert-success.warning,.wy-alert.wy-alert-success{background:#dbfaf4}.rst-content .hint .admonition-title,.rst-content .hint .wy-alert-title,.rst-content .important .admonition-title,.rst-content .important .wy-alert-title,.rst-content .tip .admonition-title,.rst-content .tip .wy-alert-title,.rst-content .wy-alert-success.admonition-todo .admonition-title,.rst-content .wy-alert-success.admonition-todo .wy-alert-title,.rst-content .wy-alert-success.admonition .admonition-title,.rst-content .wy-alert-success.admonition .wy-alert-title,.rst-content .wy-alert-success.attention .admonition-title,.rst-content .wy-alert-success.attention .wy-alert-title,.rst-content .wy-alert-success.caution .admonition-title,.rst-content .wy-alert-success.caution .wy-alert-title,.rst-content .wy-alert-success.danger .admonition-title,.rst-content .wy-alert-success.danger .wy-alert-title,.rst-content .wy-alert-success.error .admonition-title,.rst-content .wy-alert-success.error .wy-alert-title,.rst-content .wy-alert-success.note .admonition-title,.rst-content .wy-alert-success.note .wy-alert-title,.rst-content .wy-alert-success.seealso .admonition-title,.rst-content .wy-alert-success.seealso .wy-alert-title,.rst-content .wy-alert-success.warning .admonition-title,.rst-content .wy-alert-success.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-success .admonition-title,.wy-alert.wy-alert-success .rst-content .admonition-title,.wy-alert.wy-alert-success .wy-alert-title{background:#1abc9c}.rst-content .wy-alert-neutral.admonition,.rst-content .wy-alert-neutral.admonition-todo,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.seealso,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning,.wy-alert.wy-alert-neutral{background:#f3f6f6}.rst-content .wy-alert-neutral.admonition-todo .admonition-title,.rst-content .wy-alert-neutral.admonition-todo .wy-alert-title,.rst-content .wy-alert-neutral.admonition .admonition-title,.rst-content .wy-alert-neutral.admonition .wy-alert-title,.rst-content .wy-alert-neutral.attention .admonition-title,.rst-content .wy-alert-neutral.attention .wy-alert-title,.rst-content .wy-alert-neutral.caution .admonition-title,.rst-content .wy-alert-neutral.caution .wy-alert-title,.rst-content .wy-alert-neutral.danger .admonition-title,.rst-content .wy-alert-neutral.danger .wy-alert-title,.rst-content .wy-alert-neutral.error .admonition-title,.rst-content .wy-alert-neutral.error .wy-alert-title,.rst-content .wy-alert-neutral.hint .admonition-title,.rst-content .wy-alert-neutral.hint .wy-alert-title,.rst-content .wy-alert-neutral.important .admonition-title,.rst-content .wy-alert-neutral.important .wy-alert-title,.rst-content .wy-alert-neutral.note .admonition-title,.rst-content .wy-alert-neutral.note .wy-alert-title,.rst-content .wy-alert-neutral.seealso .admonition-title,.rst-content .wy-alert-neutral.seealso .wy-alert-title,.rst-content .wy-alert-neutral.tip .admonition-title,.rst-content .wy-alert-neutral.tip .wy-alert-title,.rst-content .wy-alert-neutral.warning .admonition-title,.rst-content .wy-alert-neutral.warning .wy-alert-title,.rst-content .wy-alert.wy-alert-neutral .admonition-title,.wy-alert.wy-alert-neutral .rst-content .admonition-title,.wy-alert.wy-alert-neutral .wy-alert-title{color:#404040;background:#e1e4e5}.rst-content .wy-alert-neutral.admonition-todo a,.rst-content .wy-alert-neutral.admonition a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.seealso a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a,.wy-alert.wy-alert-neutral a{color:#2980b9}.rst-content .admonition-todo p:last-child,.rst-content .admonition p:last-child,.rst-content .attention p:last-child,.rst-content .caution p:last-child,.rst-content .danger p:last-child,.rst-content .error p:last-child,.rst-content .hint p:last-child,.rst-content .important p:last-child,.rst-content .note p:last-child,.rst-content .seealso p:last-child,.rst-content .tip p:last-child,.rst-content .warning p:last-child,.wy-alert p:last-child{margin-bottom:0}.wy-tray-container{position:fixed;bottom:0;left:0;z-index:600}.wy-tray-container li{display:block;width:300px;background:transparent;color:#fff;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,.1);padding:0 24px;min-width:20%;opacity:0;height:0;line-height:56px;overflow:hidden;-webkit-transition:all .3s ease-in;-moz-transition:all .3s ease-in;transition:all .3s ease-in}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.wy-tray-container li.on{opacity:1;height:56px}@media screen and (max-width:768px){.wy-tray-container{bottom:auto;top:0;width:100%}.wy-tray-container li{width:100%}}button{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;cursor:pointer;line-height:normal;-webkit-appearance:button;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}.btn{display:inline-block;border-radius:2px;line-height:normal;white-space:nowrap;text-align:center;cursor:pointer;font-size:100%;padding:6px 12px 8px;color:#fff;border:1px solid rgba(0,0,0,.1);background-color:#27ae60;text-decoration:none;font-weight:400;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 2px -1px hsla(0,0%,100%,.5),inset 0 -2px 0 0 rgba(0,0,0,.1);outline-none:false;vertical-align:middle;*display:inline;zoom:1;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:all .1s linear;-moz-transition:all .1s linear;transition:all .1s linear}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;outline:0}.btn:active{box-shadow:inset 0 -1px 0 0 rgba(0,0,0,.05),inset 0 2px 0 0 rgba(0,0,0,.1);padding:8px 12px 6px}.btn:visited{color:#fff}.btn-disabled,.btn-disabled:active,.btn-disabled:focus,.btn-disabled:hover,.btn:disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9!important}.btn-info:hover{background-color:#2e8ece!important}.btn-neutral{background-color:#f3f6f6!important;color:#404040!important}.btn-neutral:hover{background-color:#e5ebeb!important;color:#404040}.btn-neutral:visited{color:#404040!important}.btn-success{background-color:#27ae60!important}.btn-success:hover{background-color:#295!important}.btn-danger{background-color:#e74c3c!important}.btn-danger:hover{background-color:#ea6153!important}.btn-warning{background-color:#e67e22!important}.btn-warning:hover{background-color:#e98b39!important}.btn-invert{background-color:#222}.btn-invert:hover{background-color:#2f2f2f!important}.btn-link{background-color:transparent!important;color:#2980b9;box-shadow:none;border-color:transparent!important}.btn-link:active,.btn-link:hover{background-color:transparent!important;color:#409ad5!important;box-shadow:none}.btn-link:visited{color:#9b59b6}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:after,.wy-btn-group:before{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown-active .wy-dropdown-menu{display:block}.wy-dropdown-menu{position:absolute;left:0;display:none;float:left;top:100%;min-width:100%;background:#fcfcfc;z-index:100;border:1px solid #cfd7dd;box-shadow:0 2px 2px 0 rgba(0,0,0,.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px;cursor:pointer}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:1px solid #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type=search]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-up .wy-dropdown-menu{bottom:100%;top:auto;left:auto;right:0}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;left:auto;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned .wy-help-inline,.wy-form-aligned input,.wy-form-aligned label,.wy-form-aligned select,.wy-form-aligned textarea{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:6px 12px 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:6px}fieldset{margin:0}fieldset,legend{border:0;padding:0}legend{width:100%;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label,legend{display:block}label{margin:0 0 .3125em;color:#333;font-size:90%}input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}.wy-control-group{margin-bottom:24px;max-width:1200px;margin-left:auto;margin-right:auto;*zoom:1}.wy-control-group:after,.wy-control-group:before{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-required>label:after{content:" *";color:#e74c3c}.wy-control-group .wy-form-full,.wy-control-group .wy-form-halves,.wy-control-group .wy-form-thirds{padding-bottom:12px}.wy-control-group .wy-form-full input[type=color],.wy-control-group .wy-form-full input[type=date],.wy-control-group .wy-form-full input[type=datetime-local],.wy-control-group .wy-form-full input[type=datetime],.wy-control-group .wy-form-full input[type=email],.wy-control-group .wy-form-full input[type=month],.wy-control-group .wy-form-full input[type=number],.wy-control-group .wy-form-full input[type=password],.wy-control-group .wy-form-full input[type=search],.wy-control-group .wy-form-full input[type=tel],.wy-control-group .wy-form-full input[type=text],.wy-control-group .wy-form-full input[type=time],.wy-control-group .wy-form-full input[type=url],.wy-control-group .wy-form-full input[type=week],.wy-control-group .wy-form-full select,.wy-control-group .wy-form-halves input[type=color],.wy-control-group .wy-form-halves input[type=date],.wy-control-group .wy-form-halves input[type=datetime-local],.wy-control-group .wy-form-halves input[type=datetime],.wy-control-group .wy-form-halves input[type=email],.wy-control-group .wy-form-halves input[type=month],.wy-control-group .wy-form-halves input[type=number],.wy-control-group .wy-form-halves input[type=password],.wy-control-group .wy-form-halves input[type=search],.wy-control-group .wy-form-halves input[type=tel],.wy-control-group .wy-form-halves input[type=text],.wy-control-group .wy-form-halves input[type=time],.wy-control-group .wy-form-halves input[type=url],.wy-control-group .wy-form-halves input[type=week],.wy-control-group .wy-form-halves select,.wy-control-group .wy-form-thirds input[type=color],.wy-control-group .wy-form-thirds input[type=date],.wy-control-group .wy-form-thirds input[type=datetime-local],.wy-control-group .wy-form-thirds input[type=datetime],.wy-control-group .wy-form-thirds input[type=email],.wy-control-group .wy-form-thirds input[type=month],.wy-control-group .wy-form-thirds input[type=number],.wy-control-group .wy-form-thirds input[type=password],.wy-control-group .wy-form-thirds input[type=search],.wy-control-group .wy-form-thirds input[type=tel],.wy-control-group .wy-form-thirds input[type=text],.wy-control-group .wy-form-thirds input[type=time],.wy-control-group .wy-form-thirds input[type=url],.wy-control-group .wy-form-thirds input[type=week],.wy-control-group .wy-form-thirds select{width:100%}.wy-control-group .wy-form-full{float:left;display:block;width:100%;margin-right:0}.wy-control-group .wy-form-full:last-child{margin-right:0}.wy-control-group .wy-form-halves{float:left;display:block;margin-right:2.35765%;width:48.82117%}.wy-control-group .wy-form-halves:last-child,.wy-control-group .wy-form-halves:nth-of-type(2n){margin-right:0}.wy-control-group .wy-form-halves:nth-of-type(odd){clear:left}.wy-control-group .wy-form-thirds{float:left;display:block;margin-right:2.35765%;width:31.76157%}.wy-control-group .wy-form-thirds:last-child,.wy-control-group .wy-form-thirds:nth-of-type(3n){margin-right:0}.wy-control-group .wy-form-thirds:nth-of-type(3n+1){clear:left}.wy-control-group.wy-control-group-no-input .wy-control,.wy-control-no-input{margin:6px 0 0;font-size:90%}.wy-control-no-input{display:inline-block}.wy-control-group.fluid-input input[type=color],.wy-control-group.fluid-input input[type=date],.wy-control-group.fluid-input input[type=datetime-local],.wy-control-group.fluid-input input[type=datetime],.wy-control-group.fluid-input input[type=email],.wy-control-group.fluid-input input[type=month],.wy-control-group.fluid-input input[type=number],.wy-control-group.fluid-input input[type=password],.wy-control-group.fluid-input input[type=search],.wy-control-group.fluid-input input[type=tel],.wy-control-group.fluid-input input[type=text],.wy-control-group.fluid-input input[type=time],.wy-control-group.fluid-input input[type=url],.wy-control-group.fluid-input input[type=week]{width:100%}.wy-form-message-inline{padding-left:.3em;color:#666;font-size:90%}.wy-form-message{display:block;color:#999;font-size:70%;margin-top:.3125em;font-style:italic}.wy-form-message p{font-size:inherit;font-style:italic;margin-bottom:6px}.wy-form-message p:last-child{margin-bottom:0}input{line-height:normal}input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;*overflow:visible}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}input[type=datetime-local]{padding:.34375em .625em}input[disabled]{cursor:default}input[type=checkbox],input[type=radio]{padding:0;margin-right:.3125em;*height:13px;*width:13px}input[type=checkbox],input[type=radio],input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}input[type=color]:focus,input[type=date]:focus,input[type=datetime-local]:focus,input[type=datetime]:focus,input[type=email]:focus,input[type=month]:focus,input[type=number]:focus,input[type=password]:focus,input[type=search]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=time]:focus,input[type=url]:focus,input[type=week]:focus{outline:0;outline:thin dotted\9;border-color:#333}input.no-focus:focus{border-color:#ccc!important}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type=color][disabled],input[type=date][disabled],input[type=datetime-local][disabled],input[type=datetime][disabled],input[type=email][disabled],input[type=month][disabled],input[type=number][disabled],input[type=password][disabled],input[type=search][disabled],input[type=tel][disabled],input[type=text][disabled],input[type=time][disabled],input[type=url][disabled],input[type=week][disabled]{cursor:not-allowed;background-color:#fafafa}input:focus:invalid,select:focus:invalid,textarea:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,select:focus:invalid:focus,textarea:focus:invalid:focus{border-color:#e74c3c}input[type=checkbox]:focus:invalid:focus,input[type=file]:focus:invalid:focus,input[type=radio]:focus:invalid:focus{outline-color:#e74c3c}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif}select,textarea{padding:.5em .625em;display:inline-block;border:1px solid #ccc;font-size:80%;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border .3s linear;-moz-transition:border .3s linear;transition:border .3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}input[readonly],select[disabled],select[readonly],textarea[disabled],textarea[readonly]{cursor:not-allowed;background-color:#fafafa}input[type=checkbox][disabled],input[type=radio][disabled]{cursor:not-allowed}.wy-checkbox,.wy-radio{margin:6px 0;color:#404040;display:block}.wy-checkbox input,.wy-radio input{vertical-align:baseline}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap;padding:6px}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{line-height:27px;padding:0 8px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:1px solid #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-switch{position:relative;display:block;height:24px;margin-top:12px;cursor:pointer}.wy-switch:before{left:0;top:0;width:36px;height:12px;background:#ccc}.wy-switch:after,.wy-switch:before{position:absolute;content:"";display:block;border-radius:4px;-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.wy-switch:after{width:18px;height:18px;background:#999;left:-3px;top:-3px}.wy-switch span{position:absolute;left:48px;display:block;font-size:12px;color:#ccc;line-height:1}.wy-switch.active:before{background:#1e8449}.wy-switch.active:after{left:24px;background:#27ae60}.wy-switch.disabled{cursor:not-allowed;opacity:.8}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error>label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type=color],.wy-control-group.wy-control-group-error input[type=date],.wy-control-group.wy-control-group-error input[type=datetime-local],.wy-control-group.wy-control-group-error input[type=datetime],.wy-control-group.wy-control-group-error input[type=email],.wy-control-group.wy-control-group-error input[type=month],.wy-control-group.wy-control-group-error input[type=number],.wy-control-group.wy-control-group-error input[type=password],.wy-control-group.wy-control-group-error input[type=search],.wy-control-group.wy-control-group-error input[type=tel],.wy-control-group.wy-control-group-error input[type=text],.wy-control-group.wy-control-group-error input[type=time],.wy-control-group.wy-control-group-error input[type=url],.wy-control-group.wy-control-group-error input[type=week],.wy-control-group.wy-control-group-error textarea{border:1px solid #e74c3c}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:.5em .625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}@media only screen and (max-width:480px){.wy-form button[type=submit]{margin:.7em 0 0}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=text],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week],.wy-form label{margin-bottom:.3em;display:block}.wy-form input[type=color],.wy-form input[type=date],.wy-form input[type=datetime-local],.wy-form input[type=datetime],.wy-form input[type=email],.wy-form input[type=month],.wy-form input[type=number],.wy-form input[type=password],.wy-form input[type=search],.wy-form input[type=tel],.wy-form input[type=time],.wy-form input[type=url],.wy-form input[type=week]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-control{margin:1.5em 0 0}.wy-form-message,.wy-form-message-inline,.wy-form .wy-help-inline{display:block;font-size:80%;padding:6px 0}}@media screen and (max-width:768px){.tablet-hide{display:none}}@media screen and (max-width:480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.rst-content table.docutils,.rst-content table.field-list,.wy-table{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.rst-content table.docutils caption,.rst-content table.field-list caption,.wy-table caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.rst-content table.docutils td,.rst-content table.docutils th,.rst-content table.field-list td,.rst-content table.field-list th,.wy-table td,.wy-table th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.rst-content table.docutils td:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list td:first-child,.rst-content table.field-list th:first-child,.wy-table td:first-child,.wy-table th:first-child{border-left-width:0}.rst-content table.docutils thead,.rst-content table.field-list thead,.wy-table thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.rst-content table.docutils thead th,.rst-content table.field-list thead th,.wy-table thead th{font-weight:700;border-bottom:2px solid #e1e4e5}.rst-content table.docutils td,.rst-content table.field-list td,.wy-table td{background-color:transparent;vertical-align:middle}.rst-content table.docutils td p,.rst-content table.field-list td p,.wy-table td p{line-height:18px}.rst-content table.docutils td p:last-child,.rst-content table.field-list td p:last-child,.wy-table td p:last-child{margin-bottom:0}.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min,.wy-table .wy-table-cell-min{width:1%;padding-right:0}.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:grey;font-size:90%}.wy-table-tertiary{color:grey;font-size:80%}.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td,.wy-table-backed,.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td{background-color:#f3f6f6}.rst-content table.docutils,.wy-table-bordered-all{border:1px solid #e1e4e5}.rst-content table.docutils td,.wy-table-bordered-all td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.rst-content table.docutils tbody>tr:last-child td,.wy-table-bordered-all tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0!important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}a{color:#2980b9;text-decoration:none;cursor:pointer}a:hover{color:#3091d1}a:visited{color:#9b59b6}html{height:100%}body,html{overflow-x:hidden}body{font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;font-weight:400;color:#404040;min-height:100%;background:#edf0f2}.wy-text-left{text-align:left}.wy-text-center{text-align:center}.wy-text-right{text-align:right}.wy-text-large{font-size:120%}.wy-text-normal{font-size:100%}.wy-text-small,small{font-size:80%}.wy-text-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22!important}a.wy-text-warning:hover{color:#eb9950!important}.wy-text-info{color:#2980b9!important}a.wy-text-info:hover{color:#409ad5!important}.wy-text-success{color:#27ae60!important}a.wy-text-success:hover{color:#36d278!important}.wy-text-danger{color:#e74c3c!important}a.wy-text-danger:hover{color:#ed7669!important}.wy-text-neutral{color:#404040!important}a.wy-text-neutral:hover{color:#595959!important}.rst-content .toctree-wrapper>p.caption,h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif}p{line-height:24px;font-size:16px;margin:0 0 24px}h1{font-size:175%}.rst-content .toctree-wrapper>p.caption,h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}hr{display:block;height:1px;border:0;border-top:1px solid #e1e4e5;margin:24px 0;padding:0}.rst-content code,.rst-content tt,code{white-space:nowrap;max-width:100%;background:#fff;border:1px solid #e1e4e5;font-size:75%;padding:0 5px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#e74c3c;overflow-x:auto}.rst-content tt.code-large,code.code-large{font-size:90%}.rst-content .section ul,.rst-content .toctree-wrapper ul,.rst-content section ul,.wy-plain-list-disc,article ul{list-style:disc;line-height:24px;margin-bottom:24px}.rst-content .section ul li,.rst-content .toctree-wrapper ul li,.rst-content section ul li,.wy-plain-list-disc li,article ul li{list-style:disc;margin-left:24px}.rst-content .section ul li p:last-child,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li p:last-child,.rst-content .toctree-wrapper ul li ul,.rst-content section ul li p:last-child,.rst-content section ul li ul,.wy-plain-list-disc li p:last-child,.wy-plain-list-disc li ul,article ul li p:last-child,article ul li ul{margin-bottom:0}.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li,.rst-content section ul li li,.wy-plain-list-disc li li,article ul li li{list-style:circle}.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li,.rst-content section ul li li li,.wy-plain-list-disc li li li,article ul li li li{list-style:square}.rst-content .section ul li ol li,.rst-content .toctree-wrapper ul li ol li,.rst-content section ul li ol li,.wy-plain-list-disc li ol li,article ul li ol li{list-style:decimal}.rst-content .section ol,.rst-content .section ol.arabic,.rst-content .toctree-wrapper ol,.rst-content .toctree-wrapper ol.arabic,.rst-content section ol,.rst-content section ol.arabic,.wy-plain-list-decimal,article ol{list-style:decimal;line-height:24px;margin-bottom:24px}.rst-content .section ol.arabic li,.rst-content .section ol li,.rst-content .toctree-wrapper ol.arabic li,.rst-content .toctree-wrapper ol li,.rst-content section ol.arabic li,.rst-content section ol li,.wy-plain-list-decimal li,article ol li{list-style:decimal;margin-left:24px}.rst-content .section ol.arabic li ul,.rst-content .section ol li p:last-child,.rst-content .section ol li ul,.rst-content .toctree-wrapper ol.arabic li ul,.rst-content .toctree-wrapper ol li p:last-child,.rst-content .toctree-wrapper ol li ul,.rst-content section ol.arabic li ul,.rst-content section ol li p:last-child,.rst-content section ol li ul,.wy-plain-list-decimal li p:last-child,.wy-plain-list-decimal li ul,article ol li p:last-child,article ol li ul{margin-bottom:0}.rst-content .section ol.arabic li ul li,.rst-content .section ol li ul li,.rst-content .toctree-wrapper ol.arabic li ul li,.rst-content .toctree-wrapper ol li ul li,.rst-content section ol.arabic li ul li,.rst-content section ol li ul li,.wy-plain-list-decimal li ul li,article ol li ul li{list-style:disc}.wy-breadcrumbs{*zoom:1}.wy-breadcrumbs:after,.wy-breadcrumbs:before{display:table;content:""}.wy-breadcrumbs:after{clear:both}.wy-breadcrumbs>li{display:inline-block;padding-top:5px}.wy-breadcrumbs>li.wy-breadcrumbs-aside{float:right}.rst-content .wy-breadcrumbs>li code,.rst-content .wy-breadcrumbs>li tt,.wy-breadcrumbs>li .rst-content tt,.wy-breadcrumbs>li code{all:inherit;color:inherit}.breadcrumb-item:before{content:"/";color:#bbb;font-size:13px;padding:0 6px 0 3px}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width:480px){.wy-breadcrumbs-extra,.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}html{font-size:16px}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:after,.wy-menu-horiz:before{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz li,.wy-menu-horiz ul{display:inline-block}.wy-menu-horiz li:hover{background:hsla(0,0%,100%,.1)}.wy-menu-horiz li.divide-left{border-left:1px solid #404040}.wy-menu-horiz li.divide-right{border-right:1px solid #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical{width:300px}.wy-menu-vertical header,.wy-menu-vertical p.caption{color:#55a5d9;height:32px;line-height:32px;padding:0 1.618em;margin:12px 0 0;display:block;font-weight:700;text-transform:uppercase;font-size:85%;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:1px solid #404040}.wy-menu-vertical li.divide-bottom{border-bottom:1px solid #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:grey;border-right:1px solid #c9c9c9;padding:.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.rst-content .wy-menu-vertical li tt,.wy-menu-vertical li .rst-content tt,.wy-menu-vertical li code{border:none;background:inherit;color:inherit;padding-left:0;padding-right:0}.wy-menu-vertical li button.toctree-expand{display:block;float:left;margin-left:-1.2em;line-height:18px;color:#4d4d4d;border:none;background:none;padding:0}.wy-menu-vertical li.current>a,.wy-menu-vertical li.on a{color:#404040;font-weight:700;position:relative;background:#fcfcfc;border:none;padding:.4045em 1.618em}.wy-menu-vertical li.current>a:hover,.wy-menu-vertical li.on a:hover{background:#fcfcfc}.wy-menu-vertical li.current>a:hover button.toctree-expand,.wy-menu-vertical li.on a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.current>a button.toctree-expand,.wy-menu-vertical li.on a button.toctree-expand{display:block;line-height:18px;color:#333}.wy-menu-vertical li.toctree-l1.current>a{border-bottom:1px solid #c9c9c9;border-top:1px solid #c9c9c9}.wy-menu-vertical .toctree-l1.current .toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .toctree-l11>ul{display:none}.wy-menu-vertical .toctree-l1.current .current.toctree-l2>ul,.wy-menu-vertical .toctree-l2.current .current.toctree-l3>ul,.wy-menu-vertical .toctree-l3.current .current.toctree-l4>ul,.wy-menu-vertical .toctree-l4.current .current.toctree-l5>ul,.wy-menu-vertical .toctree-l5.current .current.toctree-l6>ul,.wy-menu-vertical .toctree-l6.current .current.toctree-l7>ul,.wy-menu-vertical .toctree-l7.current .current.toctree-l8>ul,.wy-menu-vertical .toctree-l8.current .current.toctree-l9>ul,.wy-menu-vertical .toctree-l9.current .current.toctree-l10>ul,.wy-menu-vertical .toctree-l10.current .current.toctree-l11>ul{display:block}.wy-menu-vertical li.toctree-l3,.wy-menu-vertical li.toctree-l4{font-size:.9em}.wy-menu-vertical li.toctree-l2 a,.wy-menu-vertical li.toctree-l3 a,.wy-menu-vertical li.toctree-l4 a,.wy-menu-vertical li.toctree-l5 a,.wy-menu-vertical li.toctree-l6 a,.wy-menu-vertical li.toctree-l7 a,.wy-menu-vertical li.toctree-l8 a,.wy-menu-vertical li.toctree-l9 a,.wy-menu-vertical li.toctree-l10 a{color:#404040}.wy-menu-vertical li.toctree-l2 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l3 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l4 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l5 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l6 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l7 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l8 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l9 a:hover button.toctree-expand,.wy-menu-vertical li.toctree-l10 a:hover button.toctree-expand{color:grey}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a,.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a,.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a,.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a,.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a,.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a,.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a,.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{display:block}.wy-menu-vertical li.toctree-l2.current>a{padding:.4045em 2.427em}.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{padding:.4045em 1.618em .4045em 4.045em}.wy-menu-vertical li.toctree-l3.current>a{padding:.4045em 4.045em}.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{padding:.4045em 1.618em .4045em 5.663em}.wy-menu-vertical li.toctree-l4.current>a{padding:.4045em 5.663em}.wy-menu-vertical li.toctree-l4.current li.toctree-l5>a{padding:.4045em 1.618em .4045em 7.281em}.wy-menu-vertical li.toctree-l5.current>a{padding:.4045em 7.281em}.wy-menu-vertical li.toctree-l5.current li.toctree-l6>a{padding:.4045em 1.618em .4045em 8.899em}.wy-menu-vertical li.toctree-l6.current>a{padding:.4045em 8.899em}.wy-menu-vertical li.toctree-l6.current li.toctree-l7>a{padding:.4045em 1.618em .4045em 10.517em}.wy-menu-vertical li.toctree-l7.current>a{padding:.4045em 10.517em}.wy-menu-vertical li.toctree-l7.current li.toctree-l8>a{padding:.4045em 1.618em .4045em 12.135em}.wy-menu-vertical li.toctree-l8.current>a{padding:.4045em 12.135em}.wy-menu-vertical li.toctree-l8.current li.toctree-l9>a{padding:.4045em 1.618em .4045em 13.753em}.wy-menu-vertical li.toctree-l9.current>a{padding:.4045em 13.753em}.wy-menu-vertical li.toctree-l9.current li.toctree-l10>a{padding:.4045em 1.618em .4045em 15.371em}.wy-menu-vertical li.toctree-l10.current>a{padding:.4045em 15.371em}.wy-menu-vertical li.toctree-l10.current li.toctree-l11>a{padding:.4045em 1.618em .4045em 16.989em}.wy-menu-vertical li.toctree-l2.current>a,.wy-menu-vertical li.toctree-l2.current li.toctree-l3>a{background:#c9c9c9}.wy-menu-vertical li.toctree-l2 button.toctree-expand{color:#a3a3a3}.wy-menu-vertical li.toctree-l3.current>a,.wy-menu-vertical li.toctree-l3.current li.toctree-l4>a{background:#bdbdbd}.wy-menu-vertical li.toctree-l3 button.toctree-expand{color:#969696}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#d9d9d9;font-weight:400}.wy-menu-vertical a{line-height:18px;padding:.4045em 1.618em;display:block;position:relative;font-size:90%;color:#d9d9d9}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:hover button.toctree-expand{color:#d9d9d9}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-menu-vertical a:active button.toctree-expand{color:#fff}.wy-side-nav-search{display:block;width:300px;padding:.809em;margin-bottom:.809em;z-index:200;background-color:#2980b9;text-align:center;color:#fcfcfc}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto .809em;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search .wy-dropdown>a,.wy-side-nav-search>a{color:#fcfcfc;font-size:100%;font-weight:700;display:inline-block;padding:4px 6px;margin-bottom:.809em;max-width:100%}.wy-side-nav-search .wy-dropdown>a:hover,.wy-side-nav-search>a:hover{background:hsla(0,0%,100%,.1)}.wy-side-nav-search .wy-dropdown>a img.logo,.wy-side-nav-search>a img.logo{display:block;margin:0 auto;height:auto;width:auto;border-radius:0;max-width:100%;background:transparent}.wy-side-nav-search .wy-dropdown>a.icon img.logo,.wy-side-nav-search>a.icon img.logo{margin-top:.85em}.wy-side-nav-search>div.version{margin-top:-.4045em;margin-bottom:.809em;font-weight:400;color:hsla(0,0%,100%,.3)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all .2s ease-in;-moz-transition:all .2s ease-in;transition:all .2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:#fcfcfc}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:fixed;top:0;bottom:0;left:0;padding-bottom:2em;width:300px;overflow-x:hidden;overflow-y:hidden;min-height:100%;color:#9b9b9b;background:#343131;z-index:200}.wy-side-scroll{width:320px;position:relative;overflow-x:hidden;overflow-y:scroll;height:100%}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:.4045em .809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:after,.wy-nav-top:before{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:700}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer;padding-top:inherit}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:grey}footer p{margin-bottom:12px}.rst-content footer span.commit tt,footer span.commit .rst-content tt,footer span.commit code{padding:0;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:1em;background:none;border:none;color:grey}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:after,.rst-footer-buttons:before{width:100%;display:table;content:""}.rst-footer-buttons:after{clear:both}.rst-breadcrumbs-buttons{margin-top:12px;*zoom:1}.rst-breadcrumbs-buttons:after,.rst-breadcrumbs-buttons:before{display:table;content:""}.rst-breadcrumbs-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:1px solid #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:1px solid #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:grey;font-size:90%}.genindextable li>ul{margin-left:24px}@media screen and (max-width:768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-menu.wy-menu-vertical,.wy-side-nav-search,.wy-side-scroll{width:auto}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width:1100px){.wy-nav-content-wrap{background:rgba(0,0,0,.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.rst-versions,.wy-nav-side,footer{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:after,.rst-versions .rst-current-version:before{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-content .code-block-caption .rst-versions .rst-current-version .headerlink,.rst-content .eqno .rst-versions .rst-current-version .headerlink,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-content code.download .rst-versions .rst-current-version span:first-child,.rst-content dl dt .rst-versions .rst-current-version .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-content p .rst-versions .rst-current-version .headerlink,.rst-content table>caption .rst-versions .rst-current-version .headerlink,.rst-content tt.download .rst-versions .rst-current-version span:first-child,.rst-versions .rst-current-version .fa,.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-versions .rst-current-version .rst-content .code-block-caption .headerlink,.rst-versions .rst-current-version .rst-content .eqno .headerlink,.rst-versions .rst-current-version .rst-content code.download span:first-child,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-versions .rst-current-version .rst-content p .headerlink,.rst-versions .rst-current-version .rst-content table>caption .headerlink,.rst-versions .rst-current-version .rst-content tt.download span:first-child,.rst-versions .rst-current-version .wy-menu-vertical li button.toctree-expand,.wy-menu-vertical li .rst-versions .rst-current-version button.toctree-expand{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}.rst-content .toctree-wrapper>p.caption,.rst-content h1,.rst-content h2,.rst-content h3,.rst-content h4,.rst-content h5,.rst-content h6{margin-bottom:24px}.rst-content img{max-width:100%;height:auto}.rst-content div.figure,.rst-content figure{margin-bottom:24px}.rst-content div.figure .caption-text,.rst-content figure .caption-text{font-style:italic}.rst-content div.figure p:last-child.caption,.rst-content figure p:last-child.caption{margin-bottom:0}.rst-content div.figure.align-center,.rst-content figure.align-center{text-align:center}.rst-content .section>a>img,.rst-content .section>img,.rst-content section>a>img,.rst-content section>img{margin-bottom:24px}.rst-content abbr[title]{text-decoration:none}.rst-content.style-external-links a.reference.external:after{font-family:FontAwesome;content:"\f08e";color:#b3b3b3;vertical-align:super;font-size:60%;margin:0 .2em}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content pre.literal-block{white-space:pre;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;display:block;overflow:auto}.rst-content div[class^=highlight],.rst-content pre.literal-block{border:1px solid #e1e4e5;overflow-x:auto;margin:1px 0 24px}.rst-content div[class^=highlight] div[class^=highlight],.rst-content pre.literal-block div[class^=highlight]{padding:0;border:none;margin:0}.rst-content div[class^=highlight] td.code{width:100%}.rst-content .linenodiv pre{border-right:1px solid #e6e9ea;margin:0;padding:12px;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;user-select:none;pointer-events:none}.rst-content div[class^=highlight] pre{white-space:pre;margin:0;padding:12px;display:block;overflow:auto}.rst-content div[class^=highlight] pre .hll{display:block;margin:0 -12px;padding:0 12px}.rst-content .linenodiv pre,.rst-content div[class^=highlight] pre,.rst-content pre.literal-block{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;font-size:12px;line-height:1.4}.rst-content div.highlight .gp,.rst-content div.highlight span.linenos{user-select:none;pointer-events:none}.rst-content div.highlight span.linenos{display:inline-block;padding-left:0;padding-right:12px;margin-right:12px;border-right:1px solid #e6e9ea}.rst-content .code-block-caption{font-style:italic;font-size:85%;line-height:1;padding:1em 0;text-align:center}@media print{.rst-content .codeblock,.rst-content div[class^=highlight],.rst-content div[class^=highlight] pre{white-space:pre-wrap}}.rst-content .admonition,.rst-content .admonition-todo,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .note,.rst-content .seealso,.rst-content .tip,.rst-content .warning{clear:both}.rst-content .admonition-todo .last,.rst-content .admonition-todo>:last-child,.rst-content .admonition .last,.rst-content .admonition>:last-child,.rst-content .attention .last,.rst-content .attention>:last-child,.rst-content .caution .last,.rst-content .caution>:last-child,.rst-content .danger .last,.rst-content .danger>:last-child,.rst-content .error .last,.rst-content .error>:last-child,.rst-content .hint .last,.rst-content .hint>:last-child,.rst-content .important .last,.rst-content .important>:last-child,.rst-content .note .last,.rst-content .note>:last-child,.rst-content .seealso .last,.rst-content .seealso>:last-child,.rst-content .tip .last,.rst-content .tip>:last-child,.rst-content .warning .last,.rst-content .warning>:last-child{margin-bottom:0}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent!important;border-color:rgba(0,0,0,.1)!important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha>li,.rst-content .toctree-wrapper ol.loweralpha,.rst-content .toctree-wrapper ol.loweralpha>li,.rst-content section ol.loweralpha,.rst-content section ol.loweralpha>li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha>li,.rst-content .toctree-wrapper ol.upperalpha,.rst-content .toctree-wrapper ol.upperalpha>li,.rst-content section ol.upperalpha,.rst-content section ol.upperalpha>li{list-style:upper-alpha}.rst-content .section ol li>*,.rst-content .section ul li>*,.rst-content .toctree-wrapper ol li>*,.rst-content .toctree-wrapper ul li>*,.rst-content section ol li>*,.rst-content section ul li>*{margin-top:12px;margin-bottom:12px}.rst-content .section ol li>:first-child,.rst-content .section ul li>:first-child,.rst-content .toctree-wrapper ol li>:first-child,.rst-content .toctree-wrapper ul li>:first-child,.rst-content section ol li>:first-child,.rst-content section ul li>:first-child{margin-top:0}.rst-content .section ol li>p,.rst-content .section ol li>p:last-child,.rst-content .section ul li>p,.rst-content .section ul li>p:last-child,.rst-content .toctree-wrapper ol li>p,.rst-content .toctree-wrapper ol li>p:last-child,.rst-content .toctree-wrapper ul li>p,.rst-content .toctree-wrapper ul li>p:last-child,.rst-content section ol li>p,.rst-content section ol li>p:last-child,.rst-content section ul li>p,.rst-content section ul li>p:last-child{margin-bottom:12px}.rst-content .section ol li>p:only-child,.rst-content .section ol li>p:only-child:last-child,.rst-content .section ul li>p:only-child,.rst-content .section ul li>p:only-child:last-child,.rst-content .toctree-wrapper ol li>p:only-child,.rst-content .toctree-wrapper ol li>p:only-child:last-child,.rst-content .toctree-wrapper ul li>p:only-child,.rst-content .toctree-wrapper ul li>p:only-child:last-child,.rst-content section ol li>p:only-child,.rst-content section ol li>p:only-child:last-child,.rst-content section ul li>p:only-child,.rst-content section ul li>p:only-child:last-child{margin-bottom:0}.rst-content .section ol li>ol,.rst-content .section ol li>ul,.rst-content .section ul li>ol,.rst-content .section ul li>ul,.rst-content .toctree-wrapper ol li>ol,.rst-content .toctree-wrapper ol li>ul,.rst-content .toctree-wrapper ul li>ol,.rst-content .toctree-wrapper ul li>ul,.rst-content section ol li>ol,.rst-content section ol li>ul,.rst-content section ul li>ol,.rst-content section ul li>ul{margin-bottom:12px}.rst-content .section ol.simple li>*,.rst-content .section ol.simple li ol,.rst-content .section ol.simple li ul,.rst-content .section ul.simple li>*,.rst-content .section ul.simple li ol,.rst-content .section ul.simple li ul,.rst-content .toctree-wrapper ol.simple li>*,.rst-content .toctree-wrapper ol.simple li ol,.rst-content .toctree-wrapper ol.simple li ul,.rst-content .toctree-wrapper ul.simple li>*,.rst-content .toctree-wrapper ul.simple li ol,.rst-content .toctree-wrapper ul.simple li ul,.rst-content section ol.simple li>*,.rst-content section ol.simple li ol,.rst-content section ol.simple li ul,.rst-content section ul.simple li>*,.rst-content section ul.simple li ol,.rst-content section ul.simple li ul{margin-top:0;margin-bottom:0}.rst-content .line-block{margin-left:0;margin-bottom:24px;line-height:24px}.rst-content .line-block .line-block{margin-left:24px;margin-bottom:0}.rst-content .topic-title{font-weight:700;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0 0 24px 24px}.rst-content .align-left{float:left;margin:0 24px 24px 0}.rst-content .align-center{margin:auto}.rst-content .align-center:not(table){display:block}.rst-content .code-block-caption .headerlink,.rst-content .eqno .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink,.rst-content dl dt .headerlink,.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content p.caption .headerlink,.rst-content p .headerlink,.rst-content table>caption .headerlink{opacity:0;font-size:14px;font-family:FontAwesome;margin-left:.5em}.rst-content .code-block-caption .headerlink:focus,.rst-content .code-block-caption:hover .headerlink,.rst-content .eqno .headerlink:focus,.rst-content .eqno:hover .headerlink,.rst-content .toctree-wrapper>p.caption .headerlink:focus,.rst-content .toctree-wrapper>p.caption:hover .headerlink,.rst-content dl dt .headerlink:focus,.rst-content dl dt:hover .headerlink,.rst-content h1 .headerlink:focus,.rst-content h1:hover .headerlink,.rst-content h2 .headerlink:focus,.rst-content h2:hover .headerlink,.rst-content h3 .headerlink:focus,.rst-content h3:hover .headerlink,.rst-content h4 .headerlink:focus,.rst-content h4:hover .headerlink,.rst-content h5 .headerlink:focus,.rst-content h5:hover .headerlink,.rst-content h6 .headerlink:focus,.rst-content h6:hover .headerlink,.rst-content p.caption .headerlink:focus,.rst-content p.caption:hover .headerlink,.rst-content p .headerlink:focus,.rst-content p:hover .headerlink,.rst-content table>caption .headerlink:focus,.rst-content table>caption:hover .headerlink{opacity:1}.rst-content p a{overflow-wrap:anywhere}.rst-content .wy-table td p,.rst-content .wy-table td ul,.rst-content .wy-table th p,.rst-content .wy-table th ul,.rst-content table.docutils td p,.rst-content table.docutils td ul,.rst-content table.docutils th p,.rst-content table.docutils th ul,.rst-content table.field-list td p,.rst-content table.field-list td ul,.rst-content table.field-list th p,.rst-content table.field-list th ul{font-size:inherit}.rst-content .btn:focus{outline:2px solid}.rst-content table>caption .headerlink:after{font-size:12px}.rst-content .centered{text-align:center}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:1px solid #e1e4e5}.rst-content .sidebar dl,.rst-content .sidebar p,.rst-content .sidebar ul{font-size:90%}.rst-content .sidebar .last,.rst-content .sidebar>:last-child{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:Roboto Slab,ff-tisa-web-pro,Georgia,Arial,sans-serif;font-weight:700;background:#e1e4e5;padding:6px 12px;margin:-24px -24px 24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;box-shadow:0 0 0 2px #f1c40f;display:inline;font-weight:700}.rst-content .citation-reference,.rst-content .footnote-reference{vertical-align:baseline;position:relative;top:-.4em;line-height:0;font-size:90%}.rst-content .citation-reference>span.fn-bracket,.rst-content .footnote-reference>span.fn-bracket{display:none}.rst-content .hlist{width:100%}.rst-content dl dt span.classifier:before{content:" : "}.rst-content dl dt span.classifier-delimiter{display:none!important}html.writer-html4 .rst-content table.docutils.citation,html.writer-html4 .rst-content table.docutils.footnote{background:none;border:none}html.writer-html4 .rst-content table.docutils.citation td,html.writer-html4 .rst-content table.docutils.citation tr,html.writer-html4 .rst-content table.docutils.footnote td,html.writer-html4 .rst-content table.docutils.footnote tr{border:none;background-color:transparent!important;white-space:normal}html.writer-html4 .rst-content table.docutils.citation td.label,html.writer-html4 .rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{display:grid;grid-template-columns:auto minmax(80%,95%)}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{display:inline-grid;grid-template-columns:max-content auto}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{display:grid;grid-template-columns:auto auto minmax(.65rem,auto) minmax(40%,95%)}html.writer-html5 .rst-content aside.citation>span.label,html.writer-html5 .rst-content aside.footnote>span.label,html.writer-html5 .rst-content div.citation>span.label{grid-column-start:1;grid-column-end:2}html.writer-html5 .rst-content aside.citation>span.backrefs,html.writer-html5 .rst-content aside.footnote>span.backrefs,html.writer-html5 .rst-content div.citation>span.backrefs{grid-column-start:2;grid-column-end:3;grid-row-start:1;grid-row-end:3}html.writer-html5 .rst-content aside.citation>p,html.writer-html5 .rst-content aside.footnote>p,html.writer-html5 .rst-content div.citation>p{grid-column-start:4;grid-column-end:5}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.field-list,html.writer-html5 .rst-content dl.footnote{margin-bottom:24px}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dt{padding-left:1rem}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.field-list>dd,html.writer-html5 .rst-content dl.field-list>dt,html.writer-html5 .rst-content dl.footnote>dd,html.writer-html5 .rst-content dl.footnote>dt{margin-bottom:0}html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{font-size:.9rem}html.writer-html5 .rst-content dl.citation>dt,html.writer-html5 .rst-content dl.footnote>dt{margin:0 .5rem .5rem 0;line-height:1.2rem;word-break:break-all;font-weight:400}html.writer-html5 .rst-content dl.citation>dt>span.brackets:before,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:before{content:"["}html.writer-html5 .rst-content dl.citation>dt>span.brackets:after,html.writer-html5 .rst-content dl.footnote>dt>span.brackets:after{content:"]"}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a{word-break:keep-all}html.writer-html5 .rst-content dl.citation>dt>span.fn-backref>a:not(:first-child):before,html.writer-html5 .rst-content dl.footnote>dt>span.fn-backref>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content dl.citation>dd,html.writer-html5 .rst-content dl.footnote>dd{margin:0 0 .5rem;line-height:1.2rem}html.writer-html5 .rst-content dl.citation>dd p,html.writer-html5 .rst-content dl.footnote>dd p{font-size:.9rem}html.writer-html5 .rst-content aside.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content div.citation{padding-left:1rem;padding-right:1rem;font-size:.9rem;line-height:1.2rem}html.writer-html5 .rst-content aside.citation p,html.writer-html5 .rst-content aside.footnote p,html.writer-html5 .rst-content div.citation p{font-size:.9rem;line-height:1.2rem;margin-bottom:12px}html.writer-html5 .rst-content aside.citation span.backrefs,html.writer-html5 .rst-content aside.footnote span.backrefs,html.writer-html5 .rst-content div.citation span.backrefs{text-align:left;font-style:italic;margin-left:.65rem;word-break:break-word;word-spacing:-.1rem;max-width:5rem}html.writer-html5 .rst-content aside.citation span.backrefs>a,html.writer-html5 .rst-content aside.footnote span.backrefs>a,html.writer-html5 .rst-content div.citation span.backrefs>a{word-break:keep-all}html.writer-html5 .rst-content aside.citation span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content aside.footnote span.backrefs>a:not(:first-child):before,html.writer-html5 .rst-content div.citation span.backrefs>a:not(:first-child):before{content:" "}html.writer-html5 .rst-content aside.citation span.label,html.writer-html5 .rst-content aside.footnote span.label,html.writer-html5 .rst-content div.citation span.label{line-height:1.2rem}html.writer-html5 .rst-content aside.citation-list,html.writer-html5 .rst-content aside.footnote-list,html.writer-html5 .rst-content div.citation-list{margin-bottom:24px}html.writer-html5 .rst-content dl.option-list kbd{font-size:.9rem}.rst-content table.docutils.footnote,html.writer-html4 .rst-content table.docutils.citation,html.writer-html5 .rst-content aside.footnote,html.writer-html5 .rst-content aside.footnote-list aside.footnote,html.writer-html5 .rst-content div.citation-list>div.citation,html.writer-html5 .rst-content dl.citation,html.writer-html5 .rst-content dl.footnote{color:grey}.rst-content table.docutils.footnote code,.rst-content table.docutils.footnote tt,html.writer-html4 .rst-content table.docutils.citation code,html.writer-html4 .rst-content table.docutils.citation tt,html.writer-html5 .rst-content aside.footnote-list aside.footnote code,html.writer-html5 .rst-content aside.footnote-list aside.footnote tt,html.writer-html5 .rst-content aside.footnote code,html.writer-html5 .rst-content aside.footnote tt,html.writer-html5 .rst-content div.citation-list>div.citation code,html.writer-html5 .rst-content div.citation-list>div.citation tt,html.writer-html5 .rst-content dl.citation code,html.writer-html5 .rst-content dl.citation tt,html.writer-html5 .rst-content dl.footnote code,html.writer-html5 .rst-content dl.footnote tt{color:#555}.rst-content .wy-table-responsive.citation,.rst-content .wy-table-responsive.footnote{margin-bottom:0}.rst-content .wy-table-responsive.citation+:not(.citation),.rst-content .wy-table-responsive.footnote+:not(.footnote){margin-top:24px}.rst-content .wy-table-responsive.citation:last-child,.rst-content .wy-table-responsive.footnote:last-child{margin-bottom:24px}.rst-content table.docutils th{border-color:#e1e4e5}html.writer-html5 .rst-content table.docutils th{border:1px solid #e1e4e5}html.writer-html5 .rst-content table.docutils td>p,html.writer-html5 .rst-content table.docutils th>p{line-height:1rem;margin-bottom:0;font-size:.9rem}.rst-content table.docutils td .last,.rst-content table.docutils td .last>:last-child{margin-bottom:0}.rst-content table.field-list,.rst-content table.field-list td{border:none}.rst-content table.field-list td p{line-height:inherit}.rst-content table.field-list td>strong{display:inline-block}.rst-content table.field-list .field-name{padding-right:10px;text-align:left;white-space:nowrap}.rst-content table.field-list .field-body{text-align:left}.rst-content code,.rst-content tt{color:#000;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;padding:2px 5px}.rst-content code big,.rst-content code em,.rst-content tt big,.rst-content tt em{font-size:100%!important;line-height:normal}.rst-content code.literal,.rst-content tt.literal{color:#e74c3c;white-space:normal}.rst-content code.xref,.rst-content tt.xref,a .rst-content code,a .rst-content tt{font-weight:700;color:#404040;overflow-wrap:normal}.rst-content kbd,.rst-content pre,.rst-content samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace}.rst-content a code,.rst-content a tt{color:#2980b9}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:700;margin-bottom:12px}.rst-content dl ol,.rst-content dl p,.rst-content dl table,.rst-content dl ul{margin-bottom:12px}.rst-content dl dd{margin:0 0 12px 24px;line-height:24px}.rst-content dl dd>ol:last-child,.rst-content dl dd>p:last-child,.rst-content dl dd>table:last-child,.rst-content dl dd>ul:last-child{margin-bottom:0}html.writer-html4 .rst-content dl:not(.docutils),html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple){margin-bottom:24px}html.writer-html4 .rst-content dl:not(.docutils)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{display:table;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:3px solid #6ab0de;padding:6px;position:relative}html.writer-html4 .rst-content dl:not(.docutils)>dt:before,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:before{color:#6ab0de}html.writer-html4 .rst-content dl:not(.docutils)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt{margin-bottom:6px;border:none;border-left:3px solid #ccc;background:#f0f0f0;color:#555}html.writer-html4 .rst-content dl:not(.docutils) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) dl:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt .headerlink{color:#404040;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils)>dt:first-child,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple)>dt:first-child{margin-top:0}html.writer-html4 .rst-content dl:not(.docutils) code.descclassname,html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descclassname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{background-color:transparent;border:none;padding:0;font-size:100%!important}html.writer-html4 .rst-content dl:not(.docutils) code.descname,html.writer-html4 .rst-content dl:not(.docutils) tt.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) code.descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) tt.descname{font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .optional,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:700}html.writer-html4 .rst-content dl:not(.docutils) .property,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .property{display:inline-block;padding-right:8px;max-width:100%}html.writer-html4 .rst-content dl:not(.docutils) .k,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .k{font-style:italic}html.writer-html4 .rst-content dl:not(.docutils) .descclassname,html.writer-html4 .rst-content dl:not(.docutils) .descname,html.writer-html4 .rst-content dl:not(.docutils) .sig-name,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descclassname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .descname,html.writer-html5 .rst-content dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.citation):not(.glossary):not(.simple) .sig-name{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,Courier,monospace;color:#000}.rst-content .viewcode-back,.rst-content .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content .viewcode-back{display:block;float:right}.rst-content p.rubric{margin-bottom:12px;font-weight:700}.rst-content code.download,.rst-content tt.download{background:inherit;padding:inherit;font-weight:400;font-family:inherit;font-size:inherit;color:inherit;border:inherit;white-space:inherit}.rst-content code.download span:first-child,.rst-content tt.download span:first-child{-webkit-font-smoothing:subpixel-antialiased}.rst-content code.download span:first-child:before,.rst-content tt.download span:first-child:before{margin-right:4px}.rst-content .guilabel{border:1px solid #7fbbe3;background:#e7f2fa;font-size:80%;font-weight:700;border-radius:4px;padding:2.4px 6px;margin:auto 2px}.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>.kbd,.rst-content :not(dl.option-list)>:not(dt):not(kbd):not(.kbd)>kbd{color:inherit;font-size:80%;background-color:#fff;border:1px solid #a6a6a6;border-radius:4px;box-shadow:0 2px grey;padding:2.4px 6px;margin:auto 0}.rst-content .versionmodified{font-style:italic}@media screen and (max-width:480px){.rst-content .sidebar{width:100%}}span[id*=MathJax-Span]{color:#404040}.math{text-align:center}@font-face{font-family:Lato;src:url(fonts/lato-normal.woff2?bd03a2cc277bbbc338d464e679fe9942) format("woff2"),url(fonts/lato-normal.woff?27bd77b9162d388cb8d4c4217c7c5e2a) format("woff");font-weight:400;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold.woff2?cccb897485813c7c256901dbca54ecf2) format("woff2"),url(fonts/lato-bold.woff?d878b6c29b10beca227e9eef4246111b) format("woff");font-weight:700;font-style:normal;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-bold-italic.woff2?0b6bb6725576b072c5d0b02ecdd1900d) format("woff2"),url(fonts/lato-bold-italic.woff?9c7e4e9eb485b4a121c760e61bc3707c) format("woff");font-weight:700;font-style:italic;font-display:block}@font-face{font-family:Lato;src:url(fonts/lato-normal-italic.woff2?4eb103b4d12be57cb1d040ed5e162e9d) format("woff2"),url(fonts/lato-normal-italic.woff?f28f2d6482446544ef1ea1ccc6dd5892) format("woff");font-weight:400;font-style:italic;font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:400;src:url(fonts/Roboto-Slab-Regular.woff2?7abf5b8d04d26a2cafea937019bca958) format("woff2"),url(fonts/Roboto-Slab-Regular.woff?c1be9284088d487c5e3ff0a10a92e58c) format("woff");font-display:block}@font-face{font-family:Roboto Slab;font-style:normal;font-weight:700;src:url(fonts/Roboto-Slab-Bold.woff2?9984f4a9bda09be08e83f2506954adbe) format("woff2"),url(fonts/Roboto-Slab-Bold.woff?bed5564a116b05148e3b3bea6fb1162a) format("woff");font-display:block} diff --git a/css/theme_extra.css b/css/theme_extra.css new file mode 100644 index 0000000..9f4b063 --- /dev/null +++ b/css/theme_extra.css @@ -0,0 +1,191 @@ +/* + * Wrap inline code samples otherwise they shoot of the side and + * can't be read at all. + * + * https://github.com/mkdocs/mkdocs/issues/313 + * https://github.com/mkdocs/mkdocs/issues/233 + * https://github.com/mkdocs/mkdocs/issues/834 + */ +.rst-content code { + white-space: pre-wrap; + word-wrap: break-word; + padding: 2px 5px; +} + +/** + * Make code blocks display as blocks and give them the appropriate + * font size and padding. + * + * https://github.com/mkdocs/mkdocs/issues/855 + * https://github.com/mkdocs/mkdocs/issues/834 + * https://github.com/mkdocs/mkdocs/issues/233 + */ +.rst-content pre code { + white-space: pre; + word-wrap: normal; + display: block; + padding: 12px; + font-size: 12px; +} + +/** + * Fix code colors + * + * https://github.com/mkdocs/mkdocs/issues/2027 + */ +.rst-content code { + color: #E74C3C; +} + +.rst-content pre code { + color: #000; + background: #f8f8f8; +} + +/* + * Fix link colors when the link text is inline code. + * + * https://github.com/mkdocs/mkdocs/issues/718 + */ +a code { + color: #2980B9; +} +a:hover code { + color: #3091d1; +} +a:visited code { + color: #9B59B6; +} + +/* + * The CSS classes from highlight.js seem to clash with the + * ReadTheDocs theme causing some code to be incorrectly made + * bold and italic. + * + * https://github.com/mkdocs/mkdocs/issues/411 + */ +pre .cs, pre .c { + font-weight: inherit; + font-style: inherit; +} + +/* + * Fix some issues with the theme and non-highlighted code + * samples. Without and highlighting styles attached the + * formatting is broken. + * + * https://github.com/mkdocs/mkdocs/issues/319 + */ +.rst-content .no-highlight { + display: block; + padding: 0.5em; + color: #333; +} + + +/* + * Additions specific to the search functionality provided by MkDocs + */ + +.search-results { + margin-top: 23px; +} + +.search-results article { + border-top: 1px solid #E1E4E5; + padding-top: 24px; +} + +.search-results article:first-child { + border-top: none; +} + +form .search-query { + width: 100%; + border-radius: 50px; + padding: 6px 12px; /* csslint allow: box-model */ + border-color: #D1D4D5; +} + +/* + * Improve inline code blocks within admonitions. + * + * https://github.com/mkdocs/mkdocs/issues/656 + */ + .rst-content .admonition code { + color: #404040; + border: 1px solid #c7c9cb; + border: 1px solid rgba(0, 0, 0, 0.2); + background: #f8fbfd; + background: rgba(255, 255, 255, 0.7); +} + +/* + * Account for wide tables which go off the side. + * Override borders to avoid weirdness on narrow tables. + * + * https://github.com/mkdocs/mkdocs/issues/834 + * https://github.com/mkdocs/mkdocs/pull/1034 + */ +.rst-content .section .docutils { + width: 100%; + overflow: auto; + display: block; + border: none; +} + +td, th { + border: 1px solid #e1e4e5 !important; /* csslint allow: important */ + border-collapse: collapse; +} + +/* + * Without the following amendments, the navigation in the theme will be + * slightly cut off. This is due to the fact that the .wy-nav-side has a + * padding-bottom of 2em, which must not necessarily align with the font-size of + * 90 % on the .rst-current-version container, combined with the padding of 12px + * above and below. These amendments fix this in two steps: First, make sure the + * .rst-current-version container has a fixed height of 40px, achieved using + * line-height, and then applying a padding-bottom of 40px to this container. In + * a second step, the items within that container are re-aligned using flexbox. + * + * https://github.com/mkdocs/mkdocs/issues/2012 + */ + .wy-nav-side { + padding-bottom: 40px; +} + +/* + * The second step of above amendment: Here we make sure the items are aligned + * correctly within the .rst-current-version container. Using flexbox, we + * achieve it in such a way that it will look like the following: + * + * [No repo_name] + * Next >> // On the first page + * << Previous Next >> // On all subsequent pages + * + * [With repo_name] + * Next >> // On the first page + * << Previous Next >> // On all subsequent pages + * + * https://github.com/mkdocs/mkdocs/issues/2012 + */ +.rst-versions .rst-current-version { + padding: 0 12px; + display: flex; + font-size: initial; + justify-content: space-between; + align-items: center; + line-height: 40px; +} + +/* + * Please note that this amendment also involves removing certain inline-styles + * from the file ./mkdocs/themes/readthedocs/versions.html. + * + * https://github.com/mkdocs/mkdocs/issues/2012 + */ +.rst-current-version span { + flex: 1; + text-align: center; +} diff --git a/examples/basic_elevator.html b/examples/basic_elevator.html new file mode 100644 index 0000000..5e4ac22 --- /dev/null +++ b/examples/basic_elevator.html @@ -0,0 +1,20 @@ + [WIP] Basic Elevator Subsystem - FRC Java Programming

[WIP] Basic Elevator Subsystem#

Subtitle

Image Title

Overview#

This section will help you learn to create a basic elevator or lift subsystem.

This subsystem will contain:

  • Two motors in a single gear box
  • Use single encoder to lift to specific distances
  • 3 distances 2, 5, 10 inches
  • 3 buttons to get to those distances
  • Hard stop safeties using limit switches
  • Top and bottom
  • Run motors at 25% speed so we can watch easier

See table of contents for a breakdown of this section.


Section One#

  • Some info
  • Some other into
    • Some sub info

Section One Subsection#


Section Two#

  • Info
  • Info 2

Tip

This is a tip.

\ No newline at end of file diff --git a/examples/basic_shooter.html b/examples/basic_shooter.html new file mode 100644 index 0000000..33739fe --- /dev/null +++ b/examples/basic_shooter.html @@ -0,0 +1,20 @@ + [WIP] Basic Shooting Subsystem - FRC Java Programming

[WIP] Basic Shooting Subsystem#

Subtitle

Image Title

Overview#

This section will help you learn to BLANK.

See table of contents for a breakdown of this section.


Section One#

  • Some info
  • Some other into
    • Some sub info

Section One Subsection#


Section Two#

  • Info
  • Info 2

Tip

This is a tip.

\ No newline at end of file diff --git a/examples/pid_elevator.html b/examples/pid_elevator.html new file mode 100644 index 0000000..8b378ef --- /dev/null +++ b/examples/pid_elevator.html @@ -0,0 +1,20 @@ + [WIP] PID Driven Elevator - FRC Java Programming

[WIP] PID Driven Elevator#

Subtitle

Image Title

Overview#

This section will help you learn to BLANK.

See table of contents for a breakdown of this section.


Section One#

  • Some info
  • Some other into
    • Some sub info

Section One Subsection#


Section Two#

  • Info
  • Info 2

Tip

This is a tip.

\ No newline at end of file diff --git a/examples/pid_shooter.html b/examples/pid_shooter.html new file mode 100644 index 0000000..ebb0d04 --- /dev/null +++ b/examples/pid_shooter.html @@ -0,0 +1,20 @@ + [WIP] PID Driven Shooter - FRC Java Programming

[WIP] PID Driven Shooter#

Subtitle

Image Title

Overview#

This section will help you learn to BLANK.

See table of contents for a breakdown of this section.


Section One#

  • Some info
  • Some other into
    • Some sub info

Section One Subsection#


Section Two#

  • Info
  • Info 2

Tip

This is a tip.

\ No newline at end of file diff --git a/img/favicon.ico b/img/favicon.ico new file mode 100644 index 0000000..e85006a Binary files /dev/null and b/img/favicon.ico differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..748660b --- /dev/null +++ b/index.html @@ -0,0 +1,20 @@ + Introductions - FRC Java Programming

Introductions#

FIRST

The unofficial FIRST Robotics Competition Java Programming Tutorial.

Info

Updated for the 2021 Season
Last updated: 9/30/21

Disclaimer: Some screenshots may have different colors, icons, more/less folders/files than you due to themes or personal settings. This is normal and should not impact the tutorial. If you still have any questions please contact us.

Powered by#

sn_banner

Contributors#

Name Team Team Role
Tayler Uva 3255 Coach
Isaac Sayasane 3255 Alumni
Sharon Riggs 6995 Mentor
\ No newline at end of file diff --git a/js/html5shiv.min.js b/js/html5shiv.min.js new file mode 100644 index 0000000..1a01c94 --- /dev/null +++ b/js/html5shiv.min.js @@ -0,0 +1,4 @@ +/** +* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document); diff --git a/js/jquery-3.6.0.min.js b/js/jquery-3.6.0.min.js new file mode 100644 index 0000000..c4c6022 --- /dev/null +++ b/js/jquery-3.6.0.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (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(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=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))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(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&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(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&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(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 ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);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&&E){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=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(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(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===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]||se.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]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(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(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(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=se.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 j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.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 S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(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 je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.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)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.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),m(t)&&(t=t.call(e,n,S.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)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.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"===S.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"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(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)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(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?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.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.mouseenter(e).mouseleave(t||e)}}),S.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){S.fn[n]=function(e,t){return 0"),n("table.docutils.footnote").wrap("
"),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(''),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t [WIP] Creating an Autonomous Command - FRC Java Programming

[WIP] Creating an Autonomous Command#

Overview#

In this section we will be going over

  1. Creating an autonomous command group
  2. Using RobotPreferences to quickly change our autonomous values
  3. Using an encoder to autonomously drive
  4. Creating a delay timer to pace our commands in autonomous

What Is an Autonomous Command#

  • An autonomous command is a command that is ran during "autonomous mode" under the autonomousInit method in Robot.java
  • It could be a single command or a command group
  • It's especially helpful to have if you don't have any cameras to drive the robot during a "sandstorm" period (2019 game mechanic where the drivers couldn't see during the pre tele-op phase)
  • For this tutorial we will create an autonomous command group that makes the robot drive forward 5 feet, wait 5 seconds, and then pitch the shooter up during autonomous

Creating Commands For Autonomous#

  • Since we can't control our robot during an autonomous command we will want to create commands that allow the robot to move independently of a driver

Creating the DriveDistance Command#

1) Create a new command called DriveDistance

2) Before the constructor create a double called distance

  • We will use this to tell the command to finish when the robot drives the inputted distance

3) In the DriveDistance constructor add a double parameter called inches

4) Inside type:

distance = inches;

5) In initialize add our resetDriveEncoder method

  • We want to reset the encoder before we drive so that it counts the distance from zero

6) In execute add our arcadeDrive method and change the moveSpeed parameter to a RobotPreference named driveDistanceSpeed and rotateSpeed to 0.0

  • We only want to drive the robot forward; a RobotPreference will help us tune the drive speed

7) In isFinished type:

return Robot.m_drivetrain.getDriveEncoderDistance() == distance;

8) In end stop the Drivetrain and call end in interrupted

Example

Your full DriveDistance.java should look like this

package frc.robot.commands;
+
+import edu.wpi.first.wpilibj.command.Command;
+import frc.robot.Robot;
+import frc.robot.RobotPreferences;
+
+public class DriveDistance extends Command {
+
+    private double distance;
+
+    public DriveDistance(double inches) {
+    // Use requires() here to declare subsystem dependencies
+    // eg. requires(chassis);
+    requires(Robot.m_drivetrain);
+    distance = inches;
+    }
+
+    // Called just before this Command runs the first time
+    @Override
+    protected void initialize() {
+    Robot.m_drivetrain.resetDriveEncoder();
+    }
+
+    // Called repeatedly when this Command is scheduled to run
+    @Override
+    protected void execute() {
+    Robot.m_drivetrain.arcadeDrive(RobotPreferences.driveDistanceSpeed(), 0.0);
+    }
+
+    // Make this return true when this Command no longer needs to run execute()
+    @Override
+    protected boolean isFinished() {
+    return Robot.m_drivetrain.getDriveEncoderDistance() == distance;
+    }
+
+    // Called once after isFinished returns true
+    @Override
+    protected void end() {
+    Robot.m_drivetrain.arcadeDrive(0.0, 0.0);
+    }
+
+    // Called when another command which requires one or more of the same
+    // subsystems is scheduled to run
+    @Override
+    protected void interrupted() {
+    end();
+    }
+}

The code you typed in RobotPreferences.java should be this

public static final double driveDistanceSpeed() {
+    return Preferences.getInstance().getDouble("driveDistanceSpeed", 0.5);
+}

Creating The Autonomous Command#

  • We will create an Autonomous command group with the DriveDistance command and the ShooterPitchUp command

1) Create a new Command Group named Autonomous

2) In the constructor type

addSequential(new DriveDistance(RobotPreferences.autoDriveDistance()));
+addSequential(new ShooterUp());
  • To add a command to run in a command group use addSequential to execute commands in order

Creating the DoDelay Command#

  • In order to add timing in between our commands in our command groups we will need to create a DoDelay command
  • Unlike regular delays the DoDelay command will not stall our robot, but wait a certain amount of time before running a command

1) Create a new command called DoDelay

2) Before the constructor add two private doubles called expireTime and timeout

3) In the constructor add a double called seconds in the parameter

4) Inside the constructor set timeout equal to seconds

5) Create a protected void method called startTimer

6) Inside set expireTime equal to timeSinceInitialized + timeout

  • This will let the robot know how much time will have passed since the command was initialized when it finishes

7) In initialized add our startTimer method

8) In isFinished return timeSinceInitialized is greater or equal to expireTime

Example

Your full DoDelay.java should look like this

package frc.robot.commands;
+
+import edu.wpi.first.wpilibj.command.Command;
+
+public class DoDelay extends Command {
+
+private double expireTime;
+private double timeout;
+
+    public DoDelay(double seconds) {
+    // Use requires() here to declare subsystem dependencies
+    // eg. requires(chassis);
+    timeout = seconds;
+    }
+
+    protected void startTimer() {
+    expireTime = timeSinceInitialized() + timeout;
+    }
+
+    // Called just before this Command runs the first time
+    @Override
+    protected void initialize() {
+    startTimer();
+    }
+
+    // Called repeatedly when this Command is scheduled to run
+    @Override
+    protected void execute() {
+    }
+
+    // Make this return true when this Command no longer needs to run execute()
+    @Override
+    protected boolean isFinished() {
+    return (timeSinceInitialized() >= expireTime);
+    }
+
+    // Called once after isFinished returns true
+    @Override
+    protected void end() {
+    }
+
+    // Called when another command which requires one or more of the same
+    // subsystems is scheduled to run
+    @Override
+    protected void interrupted() {
+    }
+}

Adding the DoDelay Command to Autonomous.java#

  • Add our DoDelay command in between DriveDistance and ShooterPitchUp with a RobotPreference called autoDelay
Example

Your full Autonomous.java should look like this

package frc.robot.commands;
+
+import edu.wpi.first.wpilibj.command.CommandGroup;
+import frc.robot.RobotPreferences;
+
+public class Autonomous extends CommandGroup {
+    /**
+    * Add your docs here.
+    */
+    public Autonomous() {
+    addSequential(new DriveDistance(RobotPreferences.autoDriveDistance()));
+    addSequential(new DoDelay(RobotPreferences.autoDelay()));
+    addSequential(new ShooterUp());
+    }
+}

The code you typed in RobotPreferences.java should look like this

public static double autoDelay() {
+    return Preferences.getInstance().getDouble("autoDelay", 5.0);
+}
+
+public static double autoDriveDistance() {
+    return Preferences.getInstance().getDouble("autoDriveDistance", 12.0);
+}

Adding Our Autonomous Command to Robot.java#

  • In order to run our Autonomous command in autonomous we will have to put it in Robot.java so that it will run as soon as the robot enters the autonomous mode

  • In Robot.java under autonomousInit find m_autonomousCommand = m_chooser.getSelected(); and change it to

    public void autonomousInit() {
    +m_autonomousCommand = new Autonomous();
    +...

Testing Our Autonomous Command#

  • Now that we have finished coding our Autonomous command deploy code and add our new RobotPreferences to the widget on the ShuffleBoard
  • We have three preferences that change our autonomous behavior driveDistanceSpeed, autoDriveDistance and autoDelay
  • driveDistanceSpeed will determine the direction and how fast the robot drives
  • autoDriveDistance will determine how many inches the robot drives forward or backward
  • autoDelay will determine how long the robot waits before executing ShooterPitchUp
  • Change these values before enabling your robot in autonomous to make you get the desired results

Tips For Debugging Our Autonomous Command#

  • If the robot doesn't seem to stop moving or drive in the right direction check for inversions in your Drive commands or in the Drivetrain subsystem
  • You may also need to check if your encoder is working, if there are inversions, or if you are using the getEncoderCount method instead of the getEncoderDistanceMethod
  • If your robot doesn't move make sure you typed in the RobotPreference names exactly or check your talon IDs/Connection
  • If nothing happens after your robot is finished driving check your autoDelay preference and whether your Shooter piston is already actuated or if your solenoids are working
\ No newline at end of file diff --git a/programming/deploying.html b/programming/deploying.html new file mode 100644 index 0000000..8193ba2 --- /dev/null +++ b/programming/deploying.html @@ -0,0 +1,24 @@ + Deploying Robot Code - FRC Java Programming

Deploying Robot Code#

Bring your creation to life!

roboRIO

Overview#

This section will help you learn to deploy code to your robot.

See table of contents for a breakdown of this section.


How to deploy#

Hardware#

To deploy code, first make sure your computer is connected to the robot in ONE of the following ways:

  • USB
  • Ethernet
  • Robot's Wireless Network

Software#

Note

Make sure your team number in **wpilib_preferences.json** in the **.wpilib** folder is set to the same team number your roboRIO was programmed for (it should be the number you set when creating the project and you will NOT need to check this every time as it should not change by itself).

1) Select the W icon from the tab bar or use the shortcut by holding down Ctrl+Shift+P at the same time. (Replace ctrl with cmd on macOS)

2) Type and hit enter or select: WPILib: Deploy Robot Code

Tip

Alternatively you can do one of the following:
+
+- Use **Shift+F5** at any time to deploy. (you may also need to hold fn depending on your computer configuration)
+- Right-click on the build.gradle file in the project hierarchy and select "Build Robot Code”
+- Open the shortcut menu indicated by the ellipses in the top right corner of the VS Code window and select "Build Robot Code"

Testing#

  1. Open up the DriverStation software on any computer that has it installed.
  2. Enable the robot
  3. Try moving the joysticks on your controller when enabled.
    1. If it doesn’t, check your port numbers for your controller, axes, and motor controllers
\ No newline at end of file diff --git a/programming/driving_robot.html b/programming/driving_robot.html new file mode 100644 index 0000000..3db8023 --- /dev/null +++ b/programming/driving_robot.html @@ -0,0 +1,342 @@ + Creating a Basic Driving Robot - FRC Java Programming

Creating a Basic Driving Robot#

Lets get moving!

Drive base

Picture source: Team 2984

Overview#

This section is designed to help you program a basic driving robot, start to finish.

See table of contents for a breakdown of this section.


Creating the Drivetrain Subsystem#

Before we begin we must create the class file for the drivetrain subsystem. See Creating a New Subsystem for info on how to do this.

What will be added to the Drivetrain#

In the Drivetrain class we will tell the subsystem what type of components it will be using.

  • A Drivetrain needs motor controllers. In our case we will use 4 Talon SRs (a brand of controller for motors).
    • You could use other motor controllers such as Victor SPs or Talon SRXs but we will be using Talon SRs
    • If you are using other motor controllers, replace Talon with TalonSRX, Victor, or VictorSP in the code you write depending on the type you use.
    • You can use 2 motors (left and right), but for this tutorial we will use 4.

Tip

Be sure to read [Visual Studio Code Tips](../basics/vscode_tips.md){target=_blank} before getting started! It will make your life a lot easier.

Creating the Talon Variables#

1) Create 4 global variables of data type Talon and name them: leftFrontTalon, rightFrontTalon, leftBackTalon, rightBackTalon

  • To get started type the word Talon followed by the name i.e. Talon leftFrontTalon;
  • These will eventually hold the object values for Talons and their port numbers.

2) Next assign their values to null (more info on null).

  • We do this to make sure it is empty at this point.
  • When we assign these variables a value, we will be getting the motor controller's port numbers out of Constants
    • This means we cannot assign them at the global level
Example

The code you typed should be this:

Talon leftFrontTalon = null;
+Talon leftBackTalon = null;
+Talon rightFrontTalon = null;
+Talon rightBackTalon = null;

Your full Drivetrain.java should look like this:

package frc.robot.subsystems;
+
+import edu.wpi.first.wpilibj.Talon;
+import edu.wpi.first.wpilibj.command.Subsystem;
+
+/**
+ * Add your docs here.
+ */
+public class Drivetrain extends Subsystem {
+  // Put methods for controlling this subsystem
+  // here. Call these from Commands.
+
+  Talon leftFrontTalon = null;
+  Talon leftBackTalon = null;
+  Talon rightFrontTalon = null;
+  Talon rightBackTalon = null;
+
+  @Override
+  public void periodic() {
+    // This method will be called once per scheduler run
+  }
+}
If an error occurs (red squiggles)
  1. Click the word Talon
  2. 💡 Click the light bulb
  3. Select "Import 'Talon' (edu.wpi.first.wpilibj)"
  4. Your error should be gone!

Creating and filling the constructor#

1) Create the constructor for Drivetrain.java (more info on constructors)

  • The constructor is where we will assign values to our talon variables.

Now that we have created the Talons we must initialize them and tell them what port on the roboRIO they are on.

2) Initialize (set value of) leftFrontTalon to new Talon(0).

  • This initializes a new talon, leftFrontTalon, in a new piece of memory and states it is on port 0 of the roboRIO.
  • This should be done within the constructor Drivetrain()
    • The constructor Talon(int) takes a variable of type int. In this case the int (integer) refers to the port number on the roboRIO.

    This calls the constructor Talon(int) in the Talon class.

    roboRIO port diagram

Example

The code you typed should be this:

public Drivetrain() {
+  // Talons
+  leftFrontTalon = new Talon(0);
+}

Your full Drivetrain.java should look like this:

package frc.robot.subsystems;
+
+import edu.wpi.first.wpilibj.Talon;
+import edu.wpi.first.wpilibj.command.Subsystem;
+
+/**
+ * Add your docs here.
+ */
+public class Drivetrain extends Subsystem {
+  // Put methods for controlling this subsystem
+  // here. Call these from Commands.
+
+  Talon leftFrontTalon = null;
+  Talon leftBackTalon = null;
+  Talon rightFrontTalon = null;
+  Talon rightBackTalon = null;
+
+  public Drivetrain() {
+    // Talons
+    leftFrontTalon = new Talon(0);
+  }
+
+  @Override
+  public void periodic() {
+    // This method will be called once per scheduler run
+  }

Using Constants#

Since each subsystem has its own components with their own ports, it is easy to lose track of which ports are being used and for what. To counter this you can use a class called Constants to hold all these values in a single location.

1) To use Constants, instead of putting 0 for the port on the Talon type:

Constants.DRIVETRAIN_LEFT_FRONT_TALON

  • Names should follow the pattern SUBSYSTEM_NAME_OF_COMPONENT
  • The name is all caps since it is a constant (more info on constants).

2) Click on the underlined text

3) Click on the 💡light bulb and select “create constant…”

4) Click on Constants.java tab that just popped up

5) Change the 0 to the correct port for that motor controller on your robot/roboRIO

Danger

If you set this to the wrong value, you could damage your robot when it tries to move!

6) Repeat these steps for the remaining Talons.

Tip

Remember to save both Drivetrain.java and Constants.java

Example

The code you type should be this:

leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON);

Your full Drivetrain.java should look like this:

package frc.robot.subsystems;
+
+import edu.wpi.first.wpilibj.Talon;
+import edu.wpi.first.wpilibj.command.Subsystem;
+import frc.robot.Constants;
+
+/**
+ * Add your docs here.
+ */
+public class Drivetrain extends Subsystem {
+  // Put methods for controlling this subsystem
+  // here. Call these from Commands.
+
+  Talon leftFrontTalon = null;
+  Talon leftBackTalon = null;
+  Talon rightFrontTalon = null;
+  Talon rightBackTalon = null;
+
+  public Drivetrain() {
+    // Talons
+    leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON);
+    leftBackTalon = new Talon(Constants.DRIVETRAIN_LEFT_BACK_TALON);
+    rightFrontTalon = new Talon(Constants.DRIVETRAIN_RIGHT_FRONT_TALON);
+    rightBackTalon = new Talon(Constants.DRIVETRAIN_RIGHT_BACK_TALON);
+  }
+
+  @Override
+  public void periodic() {
+    // This method will be called once per scheduler run
+  }
+}

Your full Constants.java should look similar to this:

package frc.robot;
+
+public class Constants {
+  // Talons
+  public static final int DRIVETRAIN_LEFT_FRONT_TALON = 0;
+  public static final int DRIVETRAIN_LEFT_BACK_TALON = 1;
+  public static final int DRIVETRAIN_RIGHT_FRONT_TALON = 2;
+  public static final int DRIVETRAIN_RIGHT_BACK_TALON = 3;
+}

Warning

Remember to use the values for YOUR specific robot or you could risk damaging it!

Creating the arcade drive#

What is the Drive Class#

  • The FIRST Drive class has many pre-configured methods available to us including DifferentialDrive, and many alterations of MecanumDrive.
  • DifferentialDrive contains subsections such as TankDrive and ArcadeDrive.
  • For our tutorial we will be creating an ArcadeDrive
  • Arcade drives run by taking a moveSpeed and rotateSpeed. moveSpeed defines the forward and reverse speed and rotateSpeed defines the turning left and right speed.
  • To create an arcade drive we will be using our already existing Drivetrain class and adding to it.

Programing a RobotDrive#

1) In the same place we created our talons (outside of the constructor) we will create a DifferentialDrive and SpeedControllerGroups for our left and right motor controllers.

Outside of the constructor type:

SpeedControllerGroup leftMotors = null;
+SpeedControllerGroup rightMotors = null;
+
+DifferentialDrive differentialDrive = null;
  • Since DifferentialDrive only takes 2 parameters we need to create speed controller groups to combine like motor controllers together.
    • In this case we will combine the left motors together and the right motors together.

Warning

You should only group motors that are spinning the same direction physically when positive power is being applied otherwise you could damage your robot.

2) Now we must initialize the SpeedControllerGroups and DifferentialDrive like we did our talons. ...

In the constructor type:

leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon);
+rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon);
+
+differentialDrive = new DifferentialDrive(leftMotors, rightMotors);
Example

The code you type outside the constructor should be this:

SpeedControllerGroup leftMotors = null;
+SpeedControllerGroup rightMotors = null;
+
+DifferentialDrive differentialDrive = null;

The code you type inside the constructor should be this:

leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon);
+rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon);
+
+differentialDrive = new DifferentialDrive(leftMotors, rightMotors);

Your full Drivetrain.java should look like this:

package frc.robot.subsystems;
+
+import edu.wpi.first.wpilibj.SpeedControllerGroup;
+import edu.wpi.first.wpilibj.Talon;
+import edu.wpi.first.wpilibj.command.Subsystem;
+import edu.wpi.first.wpilibj.drive.DifferentialDrive;
+import frc.robot.Constants;
+
+/**
+ * Add your docs here.
+ */
+public class Drivetrain extends Subsystem {
+  // Put methods for controlling this subsystem
+  // here. Call these from Commands.
+
+  Talon leftFrontTalon = null;
+  Talon leftBackTalon = null;
+  Talon rightFrontTalon = null;
+  Talon rightBackTalon = null;
+
+  SpeedControllerGroup leftMotors = null;
+  SpeedControllerGroup rightMotors = null;
+
+  DifferentialDrive differentialDrive = null;
+
+  public Drivetrain() {
+    // Talons
+    leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON);
+    leftBackTalon = new Talon(Constants.DRIVETRAIN_LEFT_BACK_TALON);
+    rightFrontTalon = new Talon(Constants.DRIVETRAIN_RIGHT_FRONT_TALON);
+    rightBackTalon = new Talon(Constants.DRIVETRAIN_RIGHT_BACK_TALON);
+
+    leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon);
+    rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon);
+
+    differentialDrive = new DifferentialDrive(leftMotors, rightMotors);
+  }
+
+  @Override
+  public void periodic() {
+    // This method will be called once per scheduler run
+  }
+}

Creating the arcadeDrive method#

Now it’s time to make an arcadeDrive from our differentialDrive!

1) Let’s create a public void method called “arcadeDrive” with type “double” parameters moveSpeed and rotateSpeed.

Below the constructor type:

public void arcadeDrive(double moveSpeed, double rotateSpeed) {
+
+}

Tip

By putting something in the parentheses it makes the method require a parameter when it is used. When the method gets used and parameters are passed, they will be store in moveSpeed and rotateSpeed (in that order). See parameters for more info.

2) Now lets make our method call the differentialDrive's arcadeDrive method.

Inside our method type:

differentialDrive.arcadeDrive(moveSpeed, rotateSpeed);

DifferentialDrive's arcadeDrive method takes parameters moveValue and rotateValue.

Note

At this point you could instead create a tank drive, however implementation differs slightly. To do so type differentialDrive.tankDrive(moveSpeed, rotateSpeed); instead of differentialDrive.arcadeDrive(moveSpeed, rotateSpeed); and change the method name reflect this.

Tip

If you want to limit the max speed you can multiple the speeds by a decimal (i.e. 0.5*moveSpeed will make the motors only move half of their maximum speed)

You may want to do this for initial testing to make sure everything is going the right direction.

Example

The code you type should be this:

public void arcadeDrive(double moveSpeed, double rotateSpeed) {
+  differentialDrive.arcadeDrive(moveSpeed, rotateSpeed);
+}

Your full Drivetrain.java should look like this:

package frc.robot.subsystems;
+
+import edu.wpi.first.wpilibj.SpeedControllerGroup;
+import edu.wpi.first.wpilibj.Talon;
+import edu.wpi.first.wpilibj.command.Subsystem;
+import edu.wpi.first.wpilibj.drive.DifferentialDrive;
+import frc.robot.Constants;
+
+/**
+ * Add your docs here.
+ */
+public class Drivetrain extends Subsystem {
+  // Put methods for controlling this subsystem
+  // here. Call these from Commands.
+
+  Talon leftFrontTalon = null;
+  Talon leftBackTalon = null;
+  Talon rightFrontTalon = null;
+  Talon rightBackTalon = null;
+
+  SpeedControllerGroup leftMotors = null;
+  SpeedControllerGroup rightMotors = null;
+
+  DifferentialDrive differentialDrive = null;
+
+  public Drivetrain() {
+    // Talons
+    leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON);
+    leftBackTalon = new Talon(Constants.DRIVETRAIN_LEFT_BACK_TALON);
+    rightFrontTalon = new Talon(Constants.DRIVETRAIN_RIGHT_FRONT_TALON);
+    rightBackTalon = new Talon(Constants.DRIVETRAIN_RIGHT_BACK_TALON);
+
+    leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon);
+    rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon);
+
+    differentialDrive = new DifferentialDrive(leftMotors, rightMotors);
+  }
+
+  public void arcadeDrive(double moveSpeed, double rotateSpeed) {
+    differentialDrive.arcadeDrive(moveSpeed, rotateSpeed);
+  }
+
+  @Override
+  public void periodic() {
+    // This method will be called once per scheduler run
+  }
+}

Making our robot controllable#

Creating the Joystick#

In order to drive our robot, it needs to know what will be controlling it. To do so, we will create a new joystick in RobotContainer.java

1) Open RobotContainer.java

2) Type: 

public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER);

  • Import any classes if necessary such as: import edu.wpi.first.wpilibj.Joystick;
  • A variable driverController of type Joystick pointing to a joystick on port DRIVER_CONTROLLER from Constants

3) Click the 💡 light bulb to create a new CONSTANT and set the value to the port number the joystick uses on the laptop (this can be found in the Driverstation software).

Example

The code you type should be this:

public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER);

Your full RobotContainer.java should look like this:

package frc.robot;
+
+import edu.wpi.first.wpilibj.Joystick;
+
+/**
+ * This class is where the bulk of the robot should be declared. Since
+ * Command-based is a "declarative" paradigm, very little robot logic should
+ * actually be handled in the {@link Robot} periodic methods (other than the
+ * scheduler calls). Instead, the structure of the robot (including subsystems,
+ * commands, and button mappings) should be declared here.
+ */
+public class RobotContainer {
+  // The robot's subsystems and commands are defined here...
+  public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER);
+}

Your full Constants.java should look similar to this:

package frc.robot;
+
+public class Constants {
+  // Talons
+  public static final int DRIVETRAIN_LEFT_FRONT_TALON = 0;
+  public static final int DRIVETRAIN_LEFT_BACK_TALON = 1;
+  public static final int DRIVETRAIN_RIGHT_FRONT_TALON = 2;
+  public static final int DRIVETRAIN_RIGHT_BACK_TALON = 3;
+
+  // Joysticks
+  public static final int DRIVER_CONTROLLER = 0;
+}

Creating the DriveArcade Command#

  • Remember that methods tell the robot what it can do but in order to make it do these things we must give it a command. See Command Based Robot
  • Now that we have created the method, we need to create a command to call and use that method.
  • Let’s create a new command called DriveArcade that calls arcadeDrive method we just created!

Before we begin we must create the class file for the DriveArcade command. See Creating a New Command for info on how to do this and info on what each pre-created method does.

In the constructor#

1) In the constructor DriveArcade() type:

addRequirements(RobotContainer.m_drivetrain);
  • This means, this command will end all other commands currently using drivetrain and will run instead when executed.
  • It also means, other commands that require drivetrain will stop this command and run instead when executed.

Warning

If you use the light bulb to import ‘Robot', be sure to import the one with “frc.robot”

In the execute method#

1) In the execute method we will create 2 variables of type double called moveSpeed and rotateSpeed.

  • We want these variables to be the value of the axis of the controller we are using to drive the robot. So we will set them equal to that by using the joystick getRawAxis method.
  • Controllers return an axis value between 1 and -1 to indicate how far the joystick is pushed up or down. Our personal controller returns up as -1 so we want to invert it.
    • In Java you can put a negative “ - “in front of a numeric value to invert it (value * -1)
    • The joystick’s getRawAxis method will get the position value of the axis as you move it. The method takes parameter “axis number.” (This can be found in the Driverstation software and we will store it in Constants).

In the execute() method type:

double moveSpeed = -RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_MOVE_AXIS);
+double rotateSpeed = RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_ROTATE_AXIS);

Tip

Remember to use the light bulb for importing and creating constants if needed!

2) Also in the execute method we will we want to call the arcadeDrive method we created in Drivetrain and give it the variables moveSpeed and rotateSpeed we created as parameters.

In the execute() method below rotateSpeed type:

RobotContainer.m_drivetrain.arcadeDrive(moveSpeed, rotateSpeed);

In the isFinished method#

Since we will be using this command to control the robot we want it to run indefinitely.

1) To do this we are going to continue having isFinished return false, meaning the command will never finish.

(We don't need to change anything as this is the default)

Tip

  • If we did want a command to finish, we make this return true.
  • This can be done by replacing false with true to make it finish instantly
  • Alternatively we can make a condition which can return true
    • For example (timePassed > 10) will return true after 10 seconds but return false anytime before 10 seconds have passed.

In the end method#

1) We will call the arcadeDrive method and give it 0 and 0 as the parameters.

In the end() method type:

RobotContainer.m_drivetrain.arcadeDrive(0, 0);
  • This make the motors stop running when the command ends by setting the movement speed to zero and rotation speed to zero.

Completed Example#

Example

Your full Constants.java should look similar to this:

package frc.robot;
+
+public class Constants {
+  // Talons
+  public static final int DRIVETRAIN_LEFT_FRONT_TALON = 0;
+  public static final int DRIVETRAIN_LEFT_BACK_TALON = 1;
+  public static final int DRIVETRAIN_RIGHT_FRONT_TALON = 2;
+  public static final int DRIVETRAIN_RIGHT_BACK_TALON = 3;
+
+  // Joysticks
+  public static final int DRIVER_CONTROLLER = 0;
+  public static final int DRIVER_CONTROLLER_MOVE_AXIS = 1; // Change for your controller
+  public static final int DRIVER_CONTROLLER_ROTATE_AXIS = 2; // Change for your controller
+}

Your full DriveArcade.java should look like this:

package frc.robot.commands;
+
+import edu.wpi.first.wpilibj.command.Command;
+import frc.robot.RobotContainer;
+import frc.robot.Constants;
+
+public class DriveArcade extends Command {
+  public DriveArcade() {
+    // Use addRequirements() here to declare subsystem dependencies.
+    addRequirements(RobotContainer.m_drivetrain);
+  }
+
+  // Called just before this Command runs the first time
+  @Override
+  protected void initialize() {
+  }
+
+  // Called repeatedly when this Command is scheduled to run
+  @Override
+  protected void execute() {
+    double moveSpeed = -RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_MOVE_AXIS);
+    double rotateSpeed = RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_ROTATE_AXIS);
+
+    RobotContainer.m_drivetrain.arcadeDrive(moveSpeed, rotateSpeed);
+  }
+
+  // Called once the command ends or is interrupted.
+  @Override
+  protected void end(boolean interrupted) {
+    Robot.m_drivetrain.arcadeDrive(0, 0);
+  }
+
+  // Make this return true when this Command no longer needs to run execute()
+  @Override
+  protected boolean isFinished() {
+    return false;
+  }
+}

Using setDefaultCommand#

  • Commands passed to this method will run when the robot is enabled.
  • They also run if no other commands using the subsystem are running.
    • This is why we write addRequirements(Robot.m_subsystemName) in the commands we create, it ends currently running commands using that subsystem to allow a new command is run.

1) Back in RobotContainer.java in the constructor we will call the setDefaultCommand of m_drivetrain and pass it the DriveArcade command

In the RobotContainer.java constructor type:

m_drivetrain.setDefaultCommand(new DriveArcade());

Tip

Remember to use the light bulb for importing if needed!

Example

Your full RobotContainer.java should look like this:

package frc.robot;
+
+import edu.wpi.first.wpilibj.Joystick;
+import frc.robot.commands.*;
+import frc.robot.subsystems.*;
+import edu.wpi.first.wpilibj2.command.Command;
+
+/**
+ * This class is where the bulk of the robot should be declared.  Since Command-based is a
+ * "declarative" paradigm, very little robot logic should actually be handled in the {@link Robot}
+ * periodic methods (other than the scheduler calls).  Instead, the structure of the robot
+ * (including subsystems, commands, and button mappings) should be declared here.
+ */
+public class RobotContainer {
+  // The robot's subsystems and commands are defined here...
+  public static final Drivetrain m_drivetrain = new Drivetrain();
+  private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem();
+
+  private final ExampleCommand m_autoCommand = new ExampleCommand(m_exampleSubsystem);
+
+  public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER);
+
+  /**
+   * The container for the robot.  Contains subsystems, OI devices, and commands.
+   */
+  public RobotContainer() {
+    // Configure the button bindings
+    configureButtonBindings();
+
+    // Set default commands on subsystems
+    m_drivetrain.setDefaultCommand(new DriveArcade());
+  }
+
+  /**
+   * Use this method to define your button->command mappings.  Buttons can be created by
+   * instantiating a {@link GenericHID} or one of its subclasses ({@link
+   * edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then passing it to a
+   * {@link edu.wpi.first.wpilibj2.command.button.JoystickButton}.
+   */
+  private void configureButtonBindings() {
+  }
+
+
+  /**
+   * Use this to pass the autonomous command to the main {@link Robot} class.
+   *
+   * @return the command to run in autonomous
+   */
+  public Command getAutonomousCommand() {
+    // An ExampleCommand will run in autonomous
+    return m_autoCommand;
+  }
+}
\ No newline at end of file diff --git a/programming/new_project.html b/programming/new_project.html new file mode 100644 index 0000000..d334b97 --- /dev/null +++ b/programming/new_project.html @@ -0,0 +1,68 @@ + Creating Project Files - FRC Java Programming

Creating Project Files#

Lets get started

VSCode

Overview#

Before we can start programing a robot, we must create a new project in Visual Studio Code (VSCode).

See table of contents for a breakdown of this section.


Creating a New Project#

1) Select the W icon from the tab bar or use the shortcut by holding down Ctrl+Shift+P at the same time. (Replace ctrl with command on macOS)

2) Type and hit enter or select WPILib: Create a new project

3) Click Select a Project Type and choose Template
4) Click Select a Language and choose Java
5) Click Select a project base and choose Command Robot

6) Click Select a new project folder and choose where on your computer you would like to store the program

7) Enter a project name in the text field labeled as such
8) Enter your team number in the text field labeled as such
9) Select Generate Project

10) When prompted “Would you like to open the folder?”, select Yes (Current Window)

Default Project Contents#

Newly created projects have many files within them. We only care about the contents within the src/main/java/frc/robot/ folder. Everything else can be ignored at this point in the tutorial.

Files in the robot folder:

  • ExampleCommand.java
    • An example Command
  • ExampleSubsystem.java
    • An example SubSystem
  • Constants.java (new in 2020, replaces RobotMap.java)
    • Used to map physical ports (digital if using the CAN bus) of sensors or devices connected to the robot and assign them a variable name to be used in other parts of the code.
      • This provides flexibility for changing wiring, makes checking the wiring easier, and significantly reduces the number of magic numbers floating around.
    • Can also be used to store generic constant values as variables in the code
  • Main.java
    • Used for advanced programming
    • Will be ignored/left as is for this tutorial
  • RobotContainer.java (new in 2020, replaces OI.java)
    • Used to declare our subsystem
    • Used to create a connection between commands and Operator Interfaces (OI) such as Joysticks or buttons
  • Robot.java
    • The main class of the robot which is run when a robot boots up.
    • Used to run special methods in the init and period phases of the auto, teleop, and disabled states
Example


Creating a New Subsystem#

1) Click on the src folder to expand it.
2) Do the same for java then subsystems

3) Right click on subsystems and select Create a new class/ command.

4) Select Subsystem (New) and type your DesiredSubsystemName (i.e. Drivetrain) for the name and hit enter on your keyboard.


5) Click on the newly created DesiredSubsystemName.java (or Drivetrain.java if you named it that)

Adding the Subsystem to RobotContainer.java#

Do not forget this step!

When a robot program runs on the roboRIO it only runs the main file Robot.java and anything Robot.java links to such as RobotContainer.java.
We have created a new subsystem but we have not yet linked it to Robot.java through RobotContainer.java.

We must do this for EVERY subsystem we create

1) In RobotContainer.java we will create a new public global constant variable of type DesiredSubsystemName (i.e. Drivetrain):
public static final m_desiredSubsystemName = new DesiredSubsystemName();
(i.e. public static final m_drivetrain = new Drivetrain();)

Now when we use this subsystem in commands, we must call RobotContainer.m_desiredSubsystemName. to get access to it and its methods. (i.e. RobotContainer.m_drivetrain.someMethod())

Default Subsystem Contents#

Newly created subsystems are empty with the exception of the periodic.

  • Currently there is no constructor, we will create a constructor ourselves later.
  • periodic - a method that will be called periodically (once per robot scheduler run)
    • Useful for adding/updating data to Driverstation dashboard
    • Useful updating variables that need to always up to date
Example
package frc.robot.subsystems;
+
+import edu.wpi.first.wpilibj2.command.SubsystemBase;
+
+public class Drivetrain extends SubsystemBase {
+  /**
+   * Creates a new Drivetrain.
+   */
+  public Drivetrain() {
+
+  }
+
+  @Override
+  public void periodic() {
+    // This method will be called once per scheduler run
+  }
+}

Creating a New Command#

1) Click on the src folder to expand it (if it isn't already).
2) Do the same for commands

3) Right click on commands and select Create a new class/ command.

4) Select Command (New) and type DesiredCommandName (i.e. DriveArcade) for the name and hit enter on your keyboard.


5) Click on the newly created DesiredCommandName.java (or DriveArcade.java if you named it that)

Default Command Contents#

Newly created commands have some predefined methods in them specific for a command based robot.

  • Constructor - Called when the robot program is FIRST loaded.
    • Subsystem dependencies are declared here.
  • initialize() - Called ONCE just before this Command runs the first time.
  • execute() - Called REPEATEDLY when this Command is scheduled to run
  • end() - Called ONCE after isFinished returns true or when another command which requires one or more of the same subsystems is scheduled to run
  • isFinished() - Make this return TRUE when this Command no longer needs to run execute() (initialize always runs once regardless).
Example
package frc.robot.commands;
+
+import edu.wpi.first.wpilibj2.command.CommandBase;
+
+public class DriveArcade extends CommandBase {
+  /**
+   * Creates a new DriveArcade.
+   */
+  public DriveArcade() {
+    // Use addRequirements() here to declare subsystem dependencies.
+  }
+
+  // Called when the command is initially scheduled.
+  @Override
+  public void initialize() {
+  }
+
+  // Called every time the scheduler runs while the command is scheduled.
+  @Override
+  public void execute() {
+  }
+
+  // Called once the command ends or is interrupted.
+  @Override
+  public void end(boolean interrupted) {
+  }
+
+  // Returns true when the command should end.
+  @Override
+  public boolean isFinished() {
+    return false;
+  }
+}
\ No newline at end of file diff --git a/programming/pid.html b/programming/pid.html new file mode 100644 index 0000000..d115572 --- /dev/null +++ b/programming/pid.html @@ -0,0 +1,33 @@ + [WIP] Getting started with PID - FRC Java Programming \ No newline at end of file diff --git a/programming/pneumatics.html b/programming/pneumatics.html new file mode 100644 index 0000000..4398ba0 --- /dev/null +++ b/programming/pneumatics.html @@ -0,0 +1,124 @@ + [WIP] Using Pneumatics - FRC Java Programming

[WIP] Using Pneumatics#

Check the air pressure

Piston

Overview#

This section will help you learn to program pneumatic for your robot. For this section we are going to create a new subsystem called shooter and add one pneumatic piston (cylinder) which will be used for changing the pitch of the shooter.

See table of contents for a breakdown of this section.


Background info#

What Are Pneumatics#

  • You have probably heard of hydraulics before (which is based on water pressure). Pneumatics are essentially the same but with air pressure.
  • Unlike motors and gears which are commonly infinitely positional, pneumatic cylinders are typically dual-positional or sometimes tri-positional.
  • Pneumatic cylinders are actuated through devices called solenoids.
  • Solenoids are used to control pneumatic pistons (air cylinders) similar to how Talons control motors.

What Are Solenoids#

  • Cylinders are actuated with either single solenoids or double solenoids.
  • A single solenoid actuates with one air line, using air to switch to and hold the extended state and releasing air (sometimes paired with a spring) to allow the cylinder to return to the retracted state.
    • A single solenoid valve has one solenoid, and shifts when voltage is CONSTANTLY supplied to that solenoid. When voltage is removed, it shifts back to a "home" position.
  • A double solenoid actuates with two air lines, using air to switch and hold states between retracted and extended.
    • A double solenoid has two solenoids, and when voltage is supplied to one (and not the other) the valve shifts.
  • Solenoids are connected to the Pneumatics Control Module (PCM)
    • The PCM is connected to the roboRIO via the CAN bus.

Programming Solenoids#

For this section we are going to create a new subsystem called shooter and add one pneumatic piston (cylinder) which will be used for changing the pitch of the shooter.

See Creating a New Subsystem.

What will be added to the Shooter subsystem#

1)

  • Create a new Shooter subsystem.
  • It will be controlled through a double solenoid.
  • We are going to create a DoubleSolenoid named pitchSolenoid.
  • DoubleSolenoids have 2 controllable positions (deployed(forward) and retracted(reverse)).
  • The DoubleSolenoid constructor takes 2 parameters - (new DoubleSolenoid(port1, port2) )
  • Port 1 and Port 2 refer to Forward control and Reverse control ports on the PCM.
  • Like all ports we use, we will store this in the RobotMap.

2) Create your DoubleSolenoid named pitchSolenoid now using the same technique used to create a talon but replacing Talon with DoubleSolenoid. (For single solenoids just use Solenoid).

Example

Your full Shooter.java should look like this

package frc.robot.subsystems;
+import edu.wpi.first.wpilibj.DoubleSolenoid;
+import edu.wpi.first.wpilibj.command.Subsystem;
+import frc.robot.RobotMap;
+
+/**
+* Add your docs here.
+*/
+public class Shooter extends Subsystem {
+    // Put methods for controlling this subsystem
+    // here. Call these from Commands.
+    DoubleSolenoid pitchSolenoid = null;
+
+    public Shooter() {
+       pitchSolenoid = new DoubleSolenoid(RobotMap.SHOOTER_PITCH_SOLENOID_DEPLOY, RobotMap.SHOOTER_PITCH_SOLENOID_RETRACT);
+    }
+
+    @Override
+    public void initDefaultCommand() {
+    // Set the default command for a subsystem here.
+    // setDefaultCommand(new MySpecialCommand());
+    }
+}

The code you typed in Robot.java should be this

Outside robotInit

public static Shooter m_shooter = null;
Inside robotInit
m_shooter = new Shooter();

The code you typed in RobotMap.java should be this

// Solenoids
+public static final int SHOOTER_PITCH_SOLENOID_DEPLOY = 0;
+public static final int SHOOTER_PITCH_SOLENOID_RETRACT = 1;

Creating Pitch Up/Down Methods#

1) Create a public void method called pitchUp.

2) Inside type:

pitchSolenoid.set(Value.kForward);
  • This sets the value of the solenoid to forward (deployed) !!! Note if you wanted multiple solenoids to deploy at the same time also have them do .set(Value.kForward);

3) Do the same for the pitchDown method but change kForward to kReverse.

Example

The code you typed should be this

public void pitchUp(){
+   pitchSolenoid.set(Value.kForward);
+}
+
+public void pitchDown(){
+   pitchSolenoid.set(Value.kForward);
+}

Creating The Commands to Use Pneumatics#

Creating Deploy/Retract Instant Commands#

  • Now that we have created the methods we must create commands to use them.
  • Since changing the state of a solenoid only requires us to send a signal once (not continuously) we will create an InstantCommand instead of a Command
  • InstantCommands work the same as regular commands but hide everything except for initialize(). (InstantCommand extends Command)
  • Internally, they set isFinished to return always true so execute never runs.

1) Create a new InstantCommand called ShooterUp

  • Alternatively: Create a regular Command and set isFinished to true

2) In the constructor adds requires(Robot.m_shooter)

3) In initialize() add our newly created method pitchUp method

4) Repeat steps for ShooterDown command but change pitchUp* to **pitchDown

Example

Your full ShooterUp.java should look like this

package frc.robot.commands;
+
+import edu.wpi.first.wpilibj.command.InstantCommand;
+import frc.robot.Robot;
+
+/**
+* Add your docs here.
+*/
+public class ShooterUp extends InstantCommand {
+    /**
+    * Add your docs here.
+    */
+    public ShooterUp() {
+       super();
+       // Use requires() here to declare subsystem dependencies
+       // eg. requires(chassis);
+       requires(Robot.m_shooter);
+    }
+
+    // Called once when the command executes
+    @Override
+    protected void initialize() {
+       Robot.m_shooter.pitchUp();
+    }   
+}

Your full ShooterDown.java should look like this

package frc.robot.commands;
+
+import edu.wpi.first.wpilibj.command.InstantCommand;
+import frc.robot.Robot;
+
+/**
+* Add your docs here.
+*/
+public class ShooterDown extends InstantCommand {
+    /**
+    * Add your docs here.
+    */
+    public ShooterDown() {
+       super();
+       // Use requires() here to declare subsystem dependencies
+       // eg. requires(chassis);
+       requires(Robot.m_shooter);
+    }
+
+    // Called once when the command executes
+    @Override
+    protected void initialize() {
+       Robot.m_shooter.pitchDown();
+    }   
+}

Mapping Commands to Buttons#

Creating Joystick Buttons#

  • Now that we have created our ShooterUp and ShooterDown commands we need a way to run them.
  • Lets map them to buttons on our controller!

1) Open OI.java

2) Under our created joystick we will create Button variables and assign them to a button on our joystick

3) Type:

Button D1 = new JoystickButton(driverController, 1);
  • This creates a new Button named D1 (D representing driverController and 1 representing the button number) and sets it as a JoystickButton on the controller ‘driverController’ and button value 1 (this can be found in the Driverstation software).

4) Do this for the rest of the buttons on your controller.

Example

Your full OI.Java should look like this

package frc.robot;
+
+import edu.wpi.first.wpilibj.Joystick;
+import edu.wpi.first.wpilibj.buttons.Button;
+import edu.wpi.first.wpilibj.buttons.JoystickButton;
+
+/**
+* This class is the glue that binds the controls on the physical operator
+* interface to the commands and command groups that allow control of the robot.
+*/
+public class OI {
+   public Joystick driverController = new Joystick(RobotMap.OI_DRIVER_CONTROLLER);
+
+   Button D1 = new JoystickButton(driverController, 1);
+   Button D2 = new JoystickButton(driverController, 2);
+   Button D3 = new JoystickButton(driverController, 3);
+   Button D4 = new JoystickButton(driverController, 4);
+   Button D5 = new JoystickButton(driverController, 5);
+   Button D6 = new JoystickButton(driverController, 6);
+   Button D7 = new JoystickButton(driverController, 7);
+   Button D8 = new JoystickButton(driverController, 8);
+   Button D9 = new JoystickButton(driverController, 9);
+   Button D10 = new JoystickButton(driverController, 10);
+}

Mapping Joystick Buttons#

  • Now that we have created the buttons in the code we can map certain commands to them.

1) Create a constructor for OI

2) In the constructor type:

D1.whenPressed(new ShooterUp());
  • This means when the button D1 is pressed it runs the ShooterUp command and deploys our pneumatic piston.
  • There are other types of activations for buttons besides whenPressed like: whenRelease, whileHeld, etc.

3) Create a whenPressed button for ShooterDown as well

Example

The code you typed should be this

public OI(){
+   D1.whenPressed(new ShooterUp());
+   D2.whenPressed(new ShooterDown());
+}

Tip

You can change your import at the top of the file from:
import frc.robot.commands.ShooterUp; to
import frc.robot.commands.*;

The asterisk (wildcard) makes it so all files in the .command package (folder) are imported. This way you only have to import once.

\ No newline at end of file diff --git a/programming/robotpreferences.html b/programming/robotpreferences.html new file mode 100644 index 0000000..ac35aa9 --- /dev/null +++ b/programming/robotpreferences.html @@ -0,0 +1,41 @@ + [WIP] Using RobotPreferences - FRC Java Programming

[WIP] Using RobotPreferences#

Overview#

In this section we will be going over

  1. Creating and using RobotPreferences in shuffleboard
  2. How to convert encoder counts to inches

What Are RobotPreferences#

  • On SmartDashboard or ShuffleBoard there is a widget called Robot Preferences that can store variables that can be quickly changed
  • For example you might have a variable that changes PID values which can be changed from Robot Preferences on SmartDashboard/ShuffleBoard
  • For this section of our tutorial we will create a robot preference called driveEncoderCountsPerFoot

Creating RobotPreferences#

1) Create a new empty class called RobotPreferences

  • This is where we store all of our RobotPreferences to access anywhere
  • If we want to use a RobotPreference we call RobotPreferences.preferenceName()

2) Inside the constructor type:

public static double driveEncoderCountsPerFoot(){
+  return Preferences.getInstance().getDouble(“driveEncoderCountsPerFoot”, 1.0);
+}
  • The format for creating a RobotPreference is
public static variableType preferenceName(){
+  return Preferences.getInstance().getVariableType("preferenceName", value);
Example

Your full RobotPreferences.java should look like this

package frc.robot;
+
+import edu.wpi.first.wpilibj.Preferences;
+
+/**
+* Add your docs here.
+*/
+public class RobotPreferences {
+      // Drivetrain
+      /**
+      * Default value is 1.0
+      */
+      public static double driveEncoderCountsPerFoot() {
+        return Preferences.getInstance().getDouble("driveEncoderCountsPerFoot", 1.0);
+      }
+
+}

Creating getDriveEncoderDistance Method#

  • We will use this RobotPreference to help us create a method that can keep track of the distance our robot has driven in inches

1) Create a method called getDriveEncoderDistance inside of Drivetrain

2) Inside type:

return (getDriveEncoderCount() / RobotPreferences.driveEncoderCountsPerFoot()) * 12;
  • This will divide the current encoder count by however many counts there are in a foot then multiply that number by 12 to give us the encoder distance in inches

Note

You may need to invert this value if your encoder counts backward when the robot is driving forward

Example

The code you typed should be this

public double getDriveEncoderDistance() {
+ return (getDriveEncoderCount() / RobotPreferences.driveEncoderCountsPerFoot()) * 12;
+}

3) Add the method to the update method in Telemetry

Using RobotPreferences#

  • After deploying the code to your robot find the RobotPreferences widget and add it to your page
  • Click the add button and enter the string of the RobotPreference and its type (doubles and ints are numbers)
  • If you double click on the preference value you will notice that you can change its value
  • If you change a preference value it will update immediately

Tip

If you want to save your robot preference values that you've changed make sure you hardcode them in RobotPreferences.java later or take a picture if you want to use them again later

Measuring Distance Using Encoders#

  • Right now the encoders tell us distance in terms of encoder counts
  • We will use our driveEncoderCountsPerFoot preference to save how many counts there are when the robot drives 1 foot

1) Move the wheel on your robot with the Drivetrain encoder attached 1 foot or drive your robot 1 foot

2) Read how many counts your encoder has in the Drive Encoder Count window

  • If you want to measure again press the Reset Drive Encoder command button to reset the Drivetrain encoder count

3) Change the value of driveEncoderCountsPerFoot in the widget to this number

4) Reset the Drivetrain encoder and move the wheel 1 foot or drive the robot 1 foot again

5) Make sure your Drive Encoder Distance window reads approximately 12 (this is in inches)

  • If not repeat these steps again

6) Save your RobotPreferences widget with this value

7) Hardcode this value in RobotPreferences.java in the driveEncoderCountsPerFoot method incase you cannot recover your RobotPreferences save

\ No newline at end of file diff --git a/programming/shuffleboard.html b/programming/shuffleboard.html new file mode 100644 index 0000000..175f9b5 --- /dev/null +++ b/programming/shuffleboard.html @@ -0,0 +1,57 @@ + [WIP] Using Shuffleboard - FRC Java Programming

[WIP] Using Shuffleboard#

Overview#

In this section we will be going over

  1. Using and organizing the Shuffleboard
  2. Creating the Telemetry subsystem and adding buttons and data to be viewed in Shuffleboard

What is Shuffleboard#

  • Shuffleboard is one of the boards the driverstation displays robot data with
  • It can have widgets like graphs, camera streams, and meters
  • Unique to shuffleboard is the ability to have tabs for different boards

What is Telemetry#

  • Telemetry is where we add data to be viewed or command buttons on shuffleboard or smartdashboard
  • For this section of our tutorial we will be adding switch and encoder data to shuffleboard

Creating the Telemetry Subsystem#

1) Create a new Subsystem called Telemetry

2) Create a constructor for the Telemetry class

  • The constructor is where we will create buttons for shuffleboard

3) Inside type:

SmartDashboard.putData(“Reset Drive Encoder”, new DriveResetEncoder());

4) Create a public method called update

  • This method will run periodically in Robot.java to update sensor data on shuffleboard

5) Inside type:

SmartDashboard.putNumber(“Drivetrain Encoder Count”, Robot.m_drivetrain.getDriveEncoderCount());

6) Do the same for the getDriveEncoderDistance method

7) Try adding the Shooter Subsystem commands and sensor methods where they should be

Example

Your full Telemetry.java should look like this

package frc.robot.subsystems;
+
+import edu.wpi.first.wpilibj.command.Subsystem;
+import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
+import frc.robot.Robot;
+import frc.robot.commands.*;
+
+/**
+* Add your docs here.
+*/
+public class Telemetry extends Subsystem {
+  // Put methods for controlling this subsystem
+  // here. Call these from Commands.
+
+  public Telemetry() {
+        // Drivetrain
+        SmartDashboard.putData("Reset Drive Encoder", new DriveResetEncoder());
+
+        // Shooter
+    SmartDashboard.putData("Shooter Up", new ShooterUp());
+        SmartDashboard.putData("Shooter Down", new ShooterDown());
+        SmartDashboard.putData("Shooter Up Auto", new ShooterUpAuto());
+  }
+
+  public void update() {
+        // Drivetrain
+        SmartDashboard.putNumber("Drive Encoder Count", Robot.m_drivetrain.getDriveEncoderCount());
+
+        // Shooter
+        SmartDashboard.putBoolean("Shooter Switch", Robot.m_shooter.isShooterSwitchClosed());
+  }
+
+  @Override
+  public void initDefaultCommand() {
+        // Set the default command for a subsystem here.
+        // setDefaultCommand(new MySpecialCommand());
+  }
+}

Adding The Telemetry Subsystem to Robot.java#

1) When adding Telemetry to Robot.java, in robotInit we must add Telemetry after the other subsystems

  • This is because the Telemetry subsystem relies on methods that are created in other subsystems before it
  • It can be added before or after OI since they don’t use methods from each other

2) It is important that we add the update method to disabledPeriodic, autonomousPeriodic, and teleopPeriodic so that the Shuffleboard is always being updated with information on our sensors.

Example

The code you typed before robotInit should be this

public static Telemetry m_telemetry; 

The code you typed in robotInit should be this

m_telemetry = new Telemetry(); //This must be initialized after all other robot subsystems

The code you typed in disabledPeriodic, autonomousPeriodic, and teleopPeriodic should be this

Robot.m_telemetry.update();

Testing Shuffleboard#

  • After saving and deploying code, open the driver station
  • Click the gear on the left side and configure your team number and set the dashboard type to “ShuffleBoard”
  • If you are still connected to the robot you should see boxes for the buttons and data we added in Telemetry

Using Shuffleboard#

\ No newline at end of file diff --git a/programming/super_core.html b/programming/super_core.html new file mode 100644 index 0000000..852c2b4 --- /dev/null +++ b/programming/super_core.html @@ -0,0 +1,33 @@ + [WIP] Using SuperCORE - FRC Java Programming
\ No newline at end of file diff --git a/programming/using_sensors.html b/programming/using_sensors.html new file mode 100644 index 0000000..acaac38 --- /dev/null +++ b/programming/using_sensors.html @@ -0,0 +1,116 @@ + [WIP] Using Sensors and Switches - FRC Java Programming

[WIP] Using Sensors and Switches#

Overview#

In this section we will be going over

  1. Creating and using a switch in a shooter subsystem
    1. Create this subsystem now. (See Creating a New Subsystem)
  2. Creating an encoder in the drivetrain subsystem (See creating a driving robot)

What Are Sensors#

  • There are different types of sensors that give feedback on different things (i.e. Encoders measure distance, switches detect contact, gyros give orientation).
  • Most of these interface with the roboRIO through either the DIO, analog input, or custom electronics port.

Programming Switches (i.e. Limit Switches)#

1) For this tutorial we are going to add a switch to a shooter subsystem to automatically change the pitch of the shooter

  • Inside the shooter subsystem we are going to create a switch called shooterSwitch
  • It will be created as a DigitalInput
  • The DigitalInput constructor only takes 1 parameter - DigitalInput(port)
  • The port refers to the port numbers on the RoboRIO’s DIO
  • Store the port in Constants
Example

The code you typed should be this

DigitalInput shooterSwitch = null;

In the constructor

shooterSwitch = new DigitalInput(Constants.SHOOTER_SWITCH);

In Constants.Java

// Digital Inputs
+public static final int SHOOTER_SWITCH = 0;

Creating isShooterSwitchClosed Method#

1) Create a public boolean method called isShooterSwitchClosed

  • This method will tell us when the shooter switch is pressed

2) Inside type:

return shooterSwitch.get();
  • Switches have 2 states: open and closed.

  • Make sure you know which is true or false or you may have to invert the switch by rewiring or using the ! operator

Example

Your isShooterSwitchClosed() should look like this

public boolean isShooterSwitchClosed() {
+   return shooterSwitch.get();
+}
Full Shooter.java Example
package frc.robot.subsystems;
+
+import edu.wpi.first.wpilibj.DigitalInput;
+import edu.wpi.first.wpilibj2.command.SubsystemBase;
+import frc.robot.Constants;
+
+public class Shooter extends SubsystemBase {
+  /**
+   * Creates a new Shooter.
+   */
+  DigitalInput shooterSwitch = null;
+
+  public Shooter() {
+    shooterSwitch = new DigitalInput(Constants.SHOOTER_SWITCH);
+  }
+
+  public boolean isShooterSwitchClosed() {
+    return shooterSwitch.get();
+  }
+
+  @Override
+  public void periodic() {
+    // This method will be called once per scheduler run
+  }
+}

Creating ShooterUpAuto Command#

  • We will create a command that gives an example of how a Shooter switch may be used

1) For this tutorial we will use the switch to create a button that automatically pitches the shooter up after the switch is pressed

2) Create a new command called ShooterUpAuto

3) In the constructor add requires(Robot.m_Shooter)

4) In isFinished return our isShooterSwitchClosed method

  • we will not put anything in initialize or execute because we don't want anything to happen until the switch is closed

5) In end add our pitchUp method

  • we will not put end in interrupted either because we only want to change the pitch of the shooter if the switch is closed
Example

Your full ShooterUpAuto.java should look like this

package frc.robot.commands;
+
+import edu.wpi.first.wpilibj.command.Command;
+import frc.robot.Robot;
+
+public class ShooterUpAuto extends Command {
+  public ShooterUpAuto() {
+  // Use requires() here to declare subsystem dependencies
+  // eg. requires(chassis);
+  requires(Robot.m_shooter);
+  }
+
+  // Called just before this Command runs the first time
+  @Override
+  protected void initialize() {
+  }
+
+  // Called repeatedly when this Command is scheduled to run
+  @Override
+  protected void execute() {
+  }
+
+  // Make this return true when this Command no longer needs to run execute()
+  @Override
+  protected boolean isFinished() {
+    return Robot.m_shooter.isShooterSwitchClosed();
+  }
+
+  // Called once after isFinished returns true
+  @Override
+  protected void end() {
+    Robot.m_shooter.pitchUp();
+  }
+
+  // Called when another command which requires one or more of the same
+  // subsystems is scheduled to run
+  @Override
+  protected void interrupted() {
+  }
+}

Mapping ShooterAutoUpCommand#

  • To be able to test our command right now we can map it to a joystick button like we did our other Shooter commands
  • It would be best to make it a whenPressed or whileHeld button
  • whileHeld will run normally while the button is being held and be interrupted when released
Example

The code you typed should be this

D3.whenPressed(new ShooterUpAuto());

Or this

D3.whileHeld(new ShooterUpAuto());

Programming Encoders#

1) For this tutorial we are going to add a encoder to the Drivetrain subsystem to keep track of the distance the robot has driven

2) Inside the Drivetrain subsystem we are going to create an encoder called driveEncoder

  • It will be created as an Encoder
  • The Encoder constructor takes 2 parameters - (new Encoder(port1,port2))
  • These are DIO ports on the RoboRIO
  • Store ports in Constants as DRIVE_ENCODER_A and B
Example

The code you typed outside the constructor should be this

Encoder driveEncoder = null;

Inside the constructor

driveEncoder = new Encoder(Constants.DRIVETRAIN_ENCODER_A, Constants.DRIVETRAIN_ENCODER_B);

Creating Drive Encoder Methods#

1) Create a public double method called getDriveEncoderCount

2) Inside type:

return driveEncoder.get();
  • Encoders will return counts as an int
  • Depending which direction the encoder shaft rotates the value will increase or decrease

3) Create a public method called resetDriveEncoderCount

4) Inside type:

driveEncoder.reset();
  • This method will reset the drive encoder to zero which is useful for autonomous or when we use the robot as a ruler
Example

The code you typed should be this

public double getDriveEncoderCount() {
+      return driveEncoder.get();
+}
+
+public void resetDriveEncoder() {
+      driveEncoder.reset();
+}

Creating ResetDriveEncoder InstantCommand#

  • We need to create a command to use the resetDriveEncoder method since it’s a void method
  • We will create a InstantCommand since we will only use it to reset the drive encoder

1) Create a new InstantCommand called DriveResetEncoder

2) In the constructor add requires(Robot.m_drivetrain)

3) In initialize() add our resetDriveEncoder method

Example

Your full DriveResetEncoder command should look like this

package frc.robot.commands;
+
+import edu.wpi.first.wpilibj.command.InstantCommand;
+import frc.robot.Robot;
+
+/**
+* Add your docs here.
+*/
+public class DriveResetEncoder extends InstantCommand {
+/**
+* Add your docs here.
+*/
+ public DriveResetEncoder() {
+       super();
+       // Use requires() here to declare subsystem dependencies
+       // eg. requires(chassis);
+       requires(Robot.m_drivetrain);
+ }
+
+ // Called once when the command executes
+ @Override
+ protected void initialize() {
+       Robot.m_drivetrain.resetDriveEncoder();
+ }
+}
\ No newline at end of file diff --git a/search.html b/search.html new file mode 100644 index 0000000..fa178ff --- /dev/null +++ b/search.html @@ -0,0 +1,15 @@ + FRC Java Programming
\ No newline at end of file diff --git a/search/lunr.js b/search/lunr.js new file mode 100644 index 0000000..aca0a16 --- /dev/null +++ b/search/lunr.js @@ -0,0 +1,3475 @@ +/** + * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 + * Copyright (C) 2020 Oliver Nightingale + * @license MIT + */ + +;(function(){ + +/** + * A convenience function for configuring and constructing + * a new lunr Index. + * + * A lunr.Builder instance is created and the pipeline setup + * with a trimmer, stop word filter and stemmer. + * + * This builder object is yielded to the configuration function + * that is passed as a parameter, allowing the list of fields + * and other builder parameters to be customised. + * + * All documents _must_ be added within the passed config function. + * + * @example + * var idx = lunr(function () { + * this.field('title') + * this.field('body') + * this.ref('id') + * + * documents.forEach(function (doc) { + * this.add(doc) + * }, this) + * }) + * + * @see {@link lunr.Builder} + * @see {@link lunr.Pipeline} + * @see {@link lunr.trimmer} + * @see {@link lunr.stopWordFilter} + * @see {@link lunr.stemmer} + * @namespace {function} lunr + */ +var lunr = function (config) { + var builder = new lunr.Builder + + builder.pipeline.add( + lunr.trimmer, + lunr.stopWordFilter, + lunr.stemmer + ) + + builder.searchPipeline.add( + lunr.stemmer + ) + + config.call(builder, builder) + return builder.build() +} + +lunr.version = "2.3.9" +/*! + * lunr.utils + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * A namespace containing utils for the rest of the lunr library + * @namespace lunr.utils + */ +lunr.utils = {} + +/** + * Print a warning message to the console. + * + * @param {String} message The message to be printed. + * @memberOf lunr.utils + * @function + */ +lunr.utils.warn = (function (global) { + /* eslint-disable no-console */ + return function (message) { + if (global.console && console.warn) { + console.warn(message) + } + } + /* eslint-enable no-console */ +})(this) + +/** + * Convert an object to a string. + * + * In the case of `null` and `undefined` the function returns + * the empty string, in all other cases the result of calling + * `toString` on the passed object is returned. + * + * @param {Any} obj The object to convert to a string. + * @return {String} string representation of the passed object. + * @memberOf lunr.utils + */ +lunr.utils.asString = function (obj) { + if (obj === void 0 || obj === null) { + return "" + } else { + return obj.toString() + } +} + +/** + * Clones an object. + * + * Will create a copy of an existing object such that any mutations + * on the copy cannot affect the original. + * + * Only shallow objects are supported, passing a nested object to this + * function will cause a TypeError. + * + * Objects with primitives, and arrays of primitives are supported. + * + * @param {Object} obj The object to clone. + * @return {Object} a clone of the passed object. + * @throws {TypeError} when a nested object is passed. + * @memberOf Utils + */ +lunr.utils.clone = function (obj) { + if (obj === null || obj === undefined) { + return obj + } + + var clone = Object.create(null), + keys = Object.keys(obj) + + for (var i = 0; i < keys.length; i++) { + var key = keys[i], + val = obj[key] + + if (Array.isArray(val)) { + clone[key] = val.slice() + continue + } + + if (typeof val === 'string' || + typeof val === 'number' || + typeof val === 'boolean') { + clone[key] = val + continue + } + + throw new TypeError("clone is not deep and does not support nested objects") + } + + return clone +} +lunr.FieldRef = function (docRef, fieldName, stringValue) { + this.docRef = docRef + this.fieldName = fieldName + this._stringValue = stringValue +} + +lunr.FieldRef.joiner = "/" + +lunr.FieldRef.fromString = function (s) { + var n = s.indexOf(lunr.FieldRef.joiner) + + if (n === -1) { + throw "malformed field ref string" + } + + var fieldRef = s.slice(0, n), + docRef = s.slice(n + 1) + + return new lunr.FieldRef (docRef, fieldRef, s) +} + +lunr.FieldRef.prototype.toString = function () { + if (this._stringValue == undefined) { + this._stringValue = this.fieldName + lunr.FieldRef.joiner + this.docRef + } + + return this._stringValue +} +/*! + * lunr.Set + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * A lunr set. + * + * @constructor + */ +lunr.Set = function (elements) { + this.elements = Object.create(null) + + if (elements) { + this.length = elements.length + + for (var i = 0; i < this.length; i++) { + this.elements[elements[i]] = true + } + } else { + this.length = 0 + } +} + +/** + * A complete set that contains all elements. + * + * @static + * @readonly + * @type {lunr.Set} + */ +lunr.Set.complete = { + intersect: function (other) { + return other + }, + + union: function () { + return this + }, + + contains: function () { + return true + } +} + +/** + * An empty set that contains no elements. + * + * @static + * @readonly + * @type {lunr.Set} + */ +lunr.Set.empty = { + intersect: function () { + return this + }, + + union: function (other) { + return other + }, + + contains: function () { + return false + } +} + +/** + * Returns true if this set contains the specified object. + * + * @param {object} object - Object whose presence in this set is to be tested. + * @returns {boolean} - True if this set contains the specified object. + */ +lunr.Set.prototype.contains = function (object) { + return !!this.elements[object] +} + +/** + * Returns a new set containing only the elements that are present in both + * this set and the specified set. + * + * @param {lunr.Set} other - set to intersect with this set. + * @returns {lunr.Set} a new set that is the intersection of this and the specified set. + */ + +lunr.Set.prototype.intersect = function (other) { + var a, b, elements, intersection = [] + + if (other === lunr.Set.complete) { + return this + } + + if (other === lunr.Set.empty) { + return other + } + + if (this.length < other.length) { + a = this + b = other + } else { + a = other + b = this + } + + elements = Object.keys(a.elements) + + for (var i = 0; i < elements.length; i++) { + var element = elements[i] + if (element in b.elements) { + intersection.push(element) + } + } + + return new lunr.Set (intersection) +} + +/** + * Returns a new set combining the elements of this and the specified set. + * + * @param {lunr.Set} other - set to union with this set. + * @return {lunr.Set} a new set that is the union of this and the specified set. + */ + +lunr.Set.prototype.union = function (other) { + if (other === lunr.Set.complete) { + return lunr.Set.complete + } + + if (other === lunr.Set.empty) { + return this + } + + return new lunr.Set(Object.keys(this.elements).concat(Object.keys(other.elements))) +} +/** + * A function to calculate the inverse document frequency for + * a posting. This is shared between the builder and the index + * + * @private + * @param {object} posting - The posting for a given term + * @param {number} documentCount - The total number of documents. + */ +lunr.idf = function (posting, documentCount) { + var documentsWithTerm = 0 + + for (var fieldName in posting) { + if (fieldName == '_index') continue // Ignore the term index, its not a field + documentsWithTerm += Object.keys(posting[fieldName]).length + } + + var x = (documentCount - documentsWithTerm + 0.5) / (documentsWithTerm + 0.5) + + return Math.log(1 + Math.abs(x)) +} + +/** + * A token wraps a string representation of a token + * as it is passed through the text processing pipeline. + * + * @constructor + * @param {string} [str=''] - The string token being wrapped. + * @param {object} [metadata={}] - Metadata associated with this token. + */ +lunr.Token = function (str, metadata) { + this.str = str || "" + this.metadata = metadata || {} +} + +/** + * Returns the token string that is being wrapped by this object. + * + * @returns {string} + */ +lunr.Token.prototype.toString = function () { + return this.str +} + +/** + * A token update function is used when updating or optionally + * when cloning a token. + * + * @callback lunr.Token~updateFunction + * @param {string} str - The string representation of the token. + * @param {Object} metadata - All metadata associated with this token. + */ + +/** + * Applies the given function to the wrapped string token. + * + * @example + * token.update(function (str, metadata) { + * return str.toUpperCase() + * }) + * + * @param {lunr.Token~updateFunction} fn - A function to apply to the token string. + * @returns {lunr.Token} + */ +lunr.Token.prototype.update = function (fn) { + this.str = fn(this.str, this.metadata) + return this +} + +/** + * Creates a clone of this token. Optionally a function can be + * applied to the cloned token. + * + * @param {lunr.Token~updateFunction} [fn] - An optional function to apply to the cloned token. + * @returns {lunr.Token} + */ +lunr.Token.prototype.clone = function (fn) { + fn = fn || function (s) { return s } + return new lunr.Token (fn(this.str, this.metadata), this.metadata) +} +/*! + * lunr.tokenizer + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * A function for splitting a string into tokens ready to be inserted into + * the search index. Uses `lunr.tokenizer.separator` to split strings, change + * the value of this property to change how strings are split into tokens. + * + * This tokenizer will convert its parameter to a string by calling `toString` and + * then will split this string on the character in `lunr.tokenizer.separator`. + * Arrays will have their elements converted to strings and wrapped in a lunr.Token. + * + * Optional metadata can be passed to the tokenizer, this metadata will be cloned and + * added as metadata to every token that is created from the object to be tokenized. + * + * @static + * @param {?(string|object|object[])} obj - The object to convert into tokens + * @param {?object} metadata - Optional metadata to associate with every token + * @returns {lunr.Token[]} + * @see {@link lunr.Pipeline} + */ +lunr.tokenizer = function (obj, metadata) { + if (obj == null || obj == undefined) { + return [] + } + + if (Array.isArray(obj)) { + return obj.map(function (t) { + return new lunr.Token( + lunr.utils.asString(t).toLowerCase(), + lunr.utils.clone(metadata) + ) + }) + } + + var str = obj.toString().toLowerCase(), + len = str.length, + tokens = [] + + for (var sliceEnd = 0, sliceStart = 0; sliceEnd <= len; sliceEnd++) { + var char = str.charAt(sliceEnd), + sliceLength = sliceEnd - sliceStart + + if ((char.match(lunr.tokenizer.separator) || sliceEnd == len)) { + + if (sliceLength > 0) { + var tokenMetadata = lunr.utils.clone(metadata) || {} + tokenMetadata["position"] = [sliceStart, sliceLength] + tokenMetadata["index"] = tokens.length + + tokens.push( + new lunr.Token ( + str.slice(sliceStart, sliceEnd), + tokenMetadata + ) + ) + } + + sliceStart = sliceEnd + 1 + } + + } + + return tokens +} + +/** + * The separator used to split a string into tokens. Override this property to change the behaviour of + * `lunr.tokenizer` behaviour when tokenizing strings. By default this splits on whitespace and hyphens. + * + * @static + * @see lunr.tokenizer + */ +lunr.tokenizer.separator = /[\s\-]+/ +/*! + * lunr.Pipeline + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * lunr.Pipelines maintain an ordered list of functions to be applied to all + * tokens in documents entering the search index and queries being ran against + * the index. + * + * An instance of lunr.Index created with the lunr shortcut will contain a + * pipeline with a stop word filter and an English language stemmer. Extra + * functions can be added before or after either of these functions or these + * default functions can be removed. + * + * When run the pipeline will call each function in turn, passing a token, the + * index of that token in the original list of all tokens and finally a list of + * all the original tokens. + * + * The output of functions in the pipeline will be passed to the next function + * in the pipeline. To exclude a token from entering the index the function + * should return undefined, the rest of the pipeline will not be called with + * this token. + * + * For serialisation of pipelines to work, all functions used in an instance of + * a pipeline should be registered with lunr.Pipeline. Registered functions can + * then be loaded. If trying to load a serialised pipeline that uses functions + * that are not registered an error will be thrown. + * + * If not planning on serialising the pipeline then registering pipeline functions + * is not necessary. + * + * @constructor + */ +lunr.Pipeline = function () { + this._stack = [] +} + +lunr.Pipeline.registeredFunctions = Object.create(null) + +/** + * A pipeline function maps lunr.Token to lunr.Token. A lunr.Token contains the token + * string as well as all known metadata. A pipeline function can mutate the token string + * or mutate (or add) metadata for a given token. + * + * A pipeline function can indicate that the passed token should be discarded by returning + * null, undefined or an empty string. This token will not be passed to any downstream pipeline + * functions and will not be added to the index. + * + * Multiple tokens can be returned by returning an array of tokens. Each token will be passed + * to any downstream pipeline functions and all will returned tokens will be added to the index. + * + * Any number of pipeline functions may be chained together using a lunr.Pipeline. + * + * @interface lunr.PipelineFunction + * @param {lunr.Token} token - A token from the document being processed. + * @param {number} i - The index of this token in the complete list of tokens for this document/field. + * @param {lunr.Token[]} tokens - All tokens for this document/field. + * @returns {(?lunr.Token|lunr.Token[])} + */ + +/** + * Register a function with the pipeline. + * + * Functions that are used in the pipeline should be registered if the pipeline + * needs to be serialised, or a serialised pipeline needs to be loaded. + * + * Registering a function does not add it to a pipeline, functions must still be + * added to instances of the pipeline for them to be used when running a pipeline. + * + * @param {lunr.PipelineFunction} fn - The function to check for. + * @param {String} label - The label to register this function with + */ +lunr.Pipeline.registerFunction = function (fn, label) { + if (label in this.registeredFunctions) { + lunr.utils.warn('Overwriting existing registered function: ' + label) + } + + fn.label = label + lunr.Pipeline.registeredFunctions[fn.label] = fn +} + +/** + * Warns if the function is not registered as a Pipeline function. + * + * @param {lunr.PipelineFunction} fn - The function to check for. + * @private + */ +lunr.Pipeline.warnIfFunctionNotRegistered = function (fn) { + var isRegistered = fn.label && (fn.label in this.registeredFunctions) + + if (!isRegistered) { + lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\n', fn) + } +} + +/** + * Loads a previously serialised pipeline. + * + * All functions to be loaded must already be registered with lunr.Pipeline. + * If any function from the serialised data has not been registered then an + * error will be thrown. + * + * @param {Object} serialised - The serialised pipeline to load. + * @returns {lunr.Pipeline} + */ +lunr.Pipeline.load = function (serialised) { + var pipeline = new lunr.Pipeline + + serialised.forEach(function (fnName) { + var fn = lunr.Pipeline.registeredFunctions[fnName] + + if (fn) { + pipeline.add(fn) + } else { + throw new Error('Cannot load unregistered function: ' + fnName) + } + }) + + return pipeline +} + +/** + * Adds new functions to the end of the pipeline. + * + * Logs a warning if the function has not been registered. + * + * @param {lunr.PipelineFunction[]} functions - Any number of functions to add to the pipeline. + */ +lunr.Pipeline.prototype.add = function () { + var fns = Array.prototype.slice.call(arguments) + + fns.forEach(function (fn) { + lunr.Pipeline.warnIfFunctionNotRegistered(fn) + this._stack.push(fn) + }, this) +} + +/** + * Adds a single function after a function that already exists in the + * pipeline. + * + * Logs a warning if the function has not been registered. + * + * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline. + * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline. + */ +lunr.Pipeline.prototype.after = function (existingFn, newFn) { + lunr.Pipeline.warnIfFunctionNotRegistered(newFn) + + var pos = this._stack.indexOf(existingFn) + if (pos == -1) { + throw new Error('Cannot find existingFn') + } + + pos = pos + 1 + this._stack.splice(pos, 0, newFn) +} + +/** + * Adds a single function before a function that already exists in the + * pipeline. + * + * Logs a warning if the function has not been registered. + * + * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline. + * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline. + */ +lunr.Pipeline.prototype.before = function (existingFn, newFn) { + lunr.Pipeline.warnIfFunctionNotRegistered(newFn) + + var pos = this._stack.indexOf(existingFn) + if (pos == -1) { + throw new Error('Cannot find existingFn') + } + + this._stack.splice(pos, 0, newFn) +} + +/** + * Removes a function from the pipeline. + * + * @param {lunr.PipelineFunction} fn The function to remove from the pipeline. + */ +lunr.Pipeline.prototype.remove = function (fn) { + var pos = this._stack.indexOf(fn) + if (pos == -1) { + return + } + + this._stack.splice(pos, 1) +} + +/** + * Runs the current list of functions that make up the pipeline against the + * passed tokens. + * + * @param {Array} tokens The tokens to run through the pipeline. + * @returns {Array} + */ +lunr.Pipeline.prototype.run = function (tokens) { + var stackLength = this._stack.length + + for (var i = 0; i < stackLength; i++) { + var fn = this._stack[i] + var memo = [] + + for (var j = 0; j < tokens.length; j++) { + var result = fn(tokens[j], j, tokens) + + if (result === null || result === void 0 || result === '') continue + + if (Array.isArray(result)) { + for (var k = 0; k < result.length; k++) { + memo.push(result[k]) + } + } else { + memo.push(result) + } + } + + tokens = memo + } + + return tokens +} + +/** + * Convenience method for passing a string through a pipeline and getting + * strings out. This method takes care of wrapping the passed string in a + * token and mapping the resulting tokens back to strings. + * + * @param {string} str - The string to pass through the pipeline. + * @param {?object} metadata - Optional metadata to associate with the token + * passed to the pipeline. + * @returns {string[]} + */ +lunr.Pipeline.prototype.runString = function (str, metadata) { + var token = new lunr.Token (str, metadata) + + return this.run([token]).map(function (t) { + return t.toString() + }) +} + +/** + * Resets the pipeline by removing any existing processors. + * + */ +lunr.Pipeline.prototype.reset = function () { + this._stack = [] +} + +/** + * Returns a representation of the pipeline ready for serialisation. + * + * Logs a warning if the function has not been registered. + * + * @returns {Array} + */ +lunr.Pipeline.prototype.toJSON = function () { + return this._stack.map(function (fn) { + lunr.Pipeline.warnIfFunctionNotRegistered(fn) + + return fn.label + }) +} +/*! + * lunr.Vector + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * A vector is used to construct the vector space of documents and queries. These + * vectors support operations to determine the similarity between two documents or + * a document and a query. + * + * Normally no parameters are required for initializing a vector, but in the case of + * loading a previously dumped vector the raw elements can be provided to the constructor. + * + * For performance reasons vectors are implemented with a flat array, where an elements + * index is immediately followed by its value. E.g. [index, value, index, value]. This + * allows the underlying array to be as sparse as possible and still offer decent + * performance when being used for vector calculations. + * + * @constructor + * @param {Number[]} [elements] - The flat list of element index and element value pairs. + */ +lunr.Vector = function (elements) { + this._magnitude = 0 + this.elements = elements || [] +} + + +/** + * Calculates the position within the vector to insert a given index. + * + * This is used internally by insert and upsert. If there are duplicate indexes then + * the position is returned as if the value for that index were to be updated, but it + * is the callers responsibility to check whether there is a duplicate at that index + * + * @param {Number} insertIdx - The index at which the element should be inserted. + * @returns {Number} + */ +lunr.Vector.prototype.positionForIndex = function (index) { + // For an empty vector the tuple can be inserted at the beginning + if (this.elements.length == 0) { + return 0 + } + + var start = 0, + end = this.elements.length / 2, + sliceLength = end - start, + pivotPoint = Math.floor(sliceLength / 2), + pivotIndex = this.elements[pivotPoint * 2] + + while (sliceLength > 1) { + if (pivotIndex < index) { + start = pivotPoint + } + + if (pivotIndex > index) { + end = pivotPoint + } + + if (pivotIndex == index) { + break + } + + sliceLength = end - start + pivotPoint = start + Math.floor(sliceLength / 2) + pivotIndex = this.elements[pivotPoint * 2] + } + + if (pivotIndex == index) { + return pivotPoint * 2 + } + + if (pivotIndex > index) { + return pivotPoint * 2 + } + + if (pivotIndex < index) { + return (pivotPoint + 1) * 2 + } +} + +/** + * Inserts an element at an index within the vector. + * + * Does not allow duplicates, will throw an error if there is already an entry + * for this index. + * + * @param {Number} insertIdx - The index at which the element should be inserted. + * @param {Number} val - The value to be inserted into the vector. + */ +lunr.Vector.prototype.insert = function (insertIdx, val) { + this.upsert(insertIdx, val, function () { + throw "duplicate index" + }) +} + +/** + * Inserts or updates an existing index within the vector. + * + * @param {Number} insertIdx - The index at which the element should be inserted. + * @param {Number} val - The value to be inserted into the vector. + * @param {function} fn - A function that is called for updates, the existing value and the + * requested value are passed as arguments + */ +lunr.Vector.prototype.upsert = function (insertIdx, val, fn) { + this._magnitude = 0 + var position = this.positionForIndex(insertIdx) + + if (this.elements[position] == insertIdx) { + this.elements[position + 1] = fn(this.elements[position + 1], val) + } else { + this.elements.splice(position, 0, insertIdx, val) + } +} + +/** + * Calculates the magnitude of this vector. + * + * @returns {Number} + */ +lunr.Vector.prototype.magnitude = function () { + if (this._magnitude) return this._magnitude + + var sumOfSquares = 0, + elementsLength = this.elements.length + + for (var i = 1; i < elementsLength; i += 2) { + var val = this.elements[i] + sumOfSquares += val * val + } + + return this._magnitude = Math.sqrt(sumOfSquares) +} + +/** + * Calculates the dot product of this vector and another vector. + * + * @param {lunr.Vector} otherVector - The vector to compute the dot product with. + * @returns {Number} + */ +lunr.Vector.prototype.dot = function (otherVector) { + var dotProduct = 0, + a = this.elements, b = otherVector.elements, + aLen = a.length, bLen = b.length, + aVal = 0, bVal = 0, + i = 0, j = 0 + + while (i < aLen && j < bLen) { + aVal = a[i], bVal = b[j] + if (aVal < bVal) { + i += 2 + } else if (aVal > bVal) { + j += 2 + } else if (aVal == bVal) { + dotProduct += a[i + 1] * b[j + 1] + i += 2 + j += 2 + } + } + + return dotProduct +} + +/** + * Calculates the similarity between this vector and another vector. + * + * @param {lunr.Vector} otherVector - The other vector to calculate the + * similarity with. + * @returns {Number} + */ +lunr.Vector.prototype.similarity = function (otherVector) { + return this.dot(otherVector) / this.magnitude() || 0 +} + +/** + * Converts the vector to an array of the elements within the vector. + * + * @returns {Number[]} + */ +lunr.Vector.prototype.toArray = function () { + var output = new Array (this.elements.length / 2) + + for (var i = 1, j = 0; i < this.elements.length; i += 2, j++) { + output[j] = this.elements[i] + } + + return output +} + +/** + * A JSON serializable representation of the vector. + * + * @returns {Number[]} + */ +lunr.Vector.prototype.toJSON = function () { + return this.elements +} +/* eslint-disable */ +/*! + * lunr.stemmer + * Copyright (C) 2020 Oliver Nightingale + * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt + */ + +/** + * lunr.stemmer is an english language stemmer, this is a JavaScript + * implementation of the PorterStemmer taken from http://tartarus.org/~martin + * + * @static + * @implements {lunr.PipelineFunction} + * @param {lunr.Token} token - The string to stem + * @returns {lunr.Token} + * @see {@link lunr.Pipeline} + * @function + */ +lunr.stemmer = (function(){ + var step2list = { + "ational" : "ate", + "tional" : "tion", + "enci" : "ence", + "anci" : "ance", + "izer" : "ize", + "bli" : "ble", + "alli" : "al", + "entli" : "ent", + "eli" : "e", + "ousli" : "ous", + "ization" : "ize", + "ation" : "ate", + "ator" : "ate", + "alism" : "al", + "iveness" : "ive", + "fulness" : "ful", + "ousness" : "ous", + "aliti" : "al", + "iviti" : "ive", + "biliti" : "ble", + "logi" : "log" + }, + + step3list = { + "icate" : "ic", + "ative" : "", + "alize" : "al", + "iciti" : "ic", + "ical" : "ic", + "ful" : "", + "ness" : "" + }, + + c = "[^aeiou]", // consonant + v = "[aeiouy]", // vowel + C = c + "[^aeiouy]*", // consonant sequence + V = v + "[aeiou]*", // vowel sequence + + mgr0 = "^(" + C + ")?" + V + C, // [C]VC... is m>0 + meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$", // [C]VC[V] is m=1 + mgr1 = "^(" + C + ")?" + V + C + V + C, // [C]VCVC... is m>1 + s_v = "^(" + C + ")?" + v; // vowel in stem + + var re_mgr0 = new RegExp(mgr0); + var re_mgr1 = new RegExp(mgr1); + var re_meq1 = new RegExp(meq1); + var re_s_v = new RegExp(s_v); + + var re_1a = /^(.+?)(ss|i)es$/; + var re2_1a = /^(.+?)([^s])s$/; + var re_1b = /^(.+?)eed$/; + var re2_1b = /^(.+?)(ed|ing)$/; + var re_1b_2 = /.$/; + var re2_1b_2 = /(at|bl|iz)$/; + var re3_1b_2 = new RegExp("([^aeiouylsz])\\1$"); + var re4_1b_2 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + + var re_1c = /^(.+?[^aeiou])y$/; + var re_2 = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + + var re_3 = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + + var re_4 = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + var re2_4 = /^(.+?)(s|t)(ion)$/; + + var re_5 = /^(.+?)e$/; + var re_5_1 = /ll$/; + var re3_5 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + + var porterStemmer = function porterStemmer(w) { + var stem, + suffix, + firstch, + re, + re2, + re3, + re4; + + if (w.length < 3) { return w; } + + firstch = w.substr(0,1); + if (firstch == "y") { + w = firstch.toUpperCase() + w.substr(1); + } + + // Step 1a + re = re_1a + re2 = re2_1a; + + if (re.test(w)) { w = w.replace(re,"$1$2"); } + else if (re2.test(w)) { w = w.replace(re2,"$1$2"); } + + // Step 1b + re = re_1b; + re2 = re2_1b; + if (re.test(w)) { + var fp = re.exec(w); + re = re_mgr0; + if (re.test(fp[1])) { + re = re_1b_2; + w = w.replace(re,""); + } + } else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = re_s_v; + if (re2.test(stem)) { + w = stem; + re2 = re2_1b_2; + re3 = re3_1b_2; + re4 = re4_1b_2; + if (re2.test(w)) { w = w + "e"; } + else if (re3.test(w)) { re = re_1b_2; w = w.replace(re,""); } + else if (re4.test(w)) { w = w + "e"; } + } + } + + // Step 1c - replace suffix y or Y by i if preceded by a non-vowel which is not the first letter of the word (so cry -> cri, by -> by, say -> say) + re = re_1c; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + w = stem + "i"; + } + + // Step 2 + re = re_2; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = re_mgr0; + if (re.test(stem)) { + w = stem + step2list[suffix]; + } + } + + // Step 3 + re = re_3; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = re_mgr0; + if (re.test(stem)) { + w = stem + step3list[suffix]; + } + } + + // Step 4 + re = re_4; + re2 = re2_4; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = re_mgr1; + if (re.test(stem)) { + w = stem; + } + } else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = re_mgr1; + if (re2.test(stem)) { + w = stem; + } + } + + // Step 5 + re = re_5; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = re_mgr1; + re2 = re_meq1; + re3 = re3_5; + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) { + w = stem; + } + } + + re = re_5_1; + re2 = re_mgr1; + if (re.test(w) && re2.test(w)) { + re = re_1b_2; + w = w.replace(re,""); + } + + // and turn initial Y back to y + + if (firstch == "y") { + w = firstch.toLowerCase() + w.substr(1); + } + + return w; + }; + + return function (token) { + return token.update(porterStemmer); + } +})(); + +lunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer') +/*! + * lunr.stopWordFilter + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * lunr.generateStopWordFilter builds a stopWordFilter function from the provided + * list of stop words. + * + * The built in lunr.stopWordFilter is built using this generator and can be used + * to generate custom stopWordFilters for applications or non English languages. + * + * @function + * @param {Array} token The token to pass through the filter + * @returns {lunr.PipelineFunction} + * @see lunr.Pipeline + * @see lunr.stopWordFilter + */ +lunr.generateStopWordFilter = function (stopWords) { + var words = stopWords.reduce(function (memo, stopWord) { + memo[stopWord] = stopWord + return memo + }, {}) + + return function (token) { + if (token && words[token.toString()] !== token.toString()) return token + } +} + +/** + * lunr.stopWordFilter is an English language stop word list filter, any words + * contained in the list will not be passed through the filter. + * + * This is intended to be used in the Pipeline. If the token does not pass the + * filter then undefined will be returned. + * + * @function + * @implements {lunr.PipelineFunction} + * @params {lunr.Token} token - A token to check for being a stop word. + * @returns {lunr.Token} + * @see {@link lunr.Pipeline} + */ +lunr.stopWordFilter = lunr.generateStopWordFilter([ + 'a', + 'able', + 'about', + 'across', + 'after', + 'all', + 'almost', + 'also', + 'am', + 'among', + 'an', + 'and', + 'any', + 'are', + 'as', + 'at', + 'be', + 'because', + 'been', + 'but', + 'by', + 'can', + 'cannot', + 'could', + 'dear', + 'did', + 'do', + 'does', + 'either', + 'else', + 'ever', + 'every', + 'for', + 'from', + 'get', + 'got', + 'had', + 'has', + 'have', + 'he', + 'her', + 'hers', + 'him', + 'his', + 'how', + 'however', + 'i', + 'if', + 'in', + 'into', + 'is', + 'it', + 'its', + 'just', + 'least', + 'let', + 'like', + 'likely', + 'may', + 'me', + 'might', + 'most', + 'must', + 'my', + 'neither', + 'no', + 'nor', + 'not', + 'of', + 'off', + 'often', + 'on', + 'only', + 'or', + 'other', + 'our', + 'own', + 'rather', + 'said', + 'say', + 'says', + 'she', + 'should', + 'since', + 'so', + 'some', + 'than', + 'that', + 'the', + 'their', + 'them', + 'then', + 'there', + 'these', + 'they', + 'this', + 'tis', + 'to', + 'too', + 'twas', + 'us', + 'wants', + 'was', + 'we', + 'were', + 'what', + 'when', + 'where', + 'which', + 'while', + 'who', + 'whom', + 'why', + 'will', + 'with', + 'would', + 'yet', + 'you', + 'your' +]) + +lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter') +/*! + * lunr.trimmer + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * lunr.trimmer is a pipeline function for trimming non word + * characters from the beginning and end of tokens before they + * enter the index. + * + * This implementation may not work correctly for non latin + * characters and should either be removed or adapted for use + * with languages with non-latin characters. + * + * @static + * @implements {lunr.PipelineFunction} + * @param {lunr.Token} token The token to pass through the filter + * @returns {lunr.Token} + * @see lunr.Pipeline + */ +lunr.trimmer = function (token) { + return token.update(function (s) { + return s.replace(/^\W+/, '').replace(/\W+$/, '') + }) +} + +lunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer') +/*! + * lunr.TokenSet + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * A token set is used to store the unique list of all tokens + * within an index. Token sets are also used to represent an + * incoming query to the index, this query token set and index + * token set are then intersected to find which tokens to look + * up in the inverted index. + * + * A token set can hold multiple tokens, as in the case of the + * index token set, or it can hold a single token as in the + * case of a simple query token set. + * + * Additionally token sets are used to perform wildcard matching. + * Leading, contained and trailing wildcards are supported, and + * from this edit distance matching can also be provided. + * + * Token sets are implemented as a minimal finite state automata, + * where both common prefixes and suffixes are shared between tokens. + * This helps to reduce the space used for storing the token set. + * + * @constructor + */ +lunr.TokenSet = function () { + this.final = false + this.edges = {} + this.id = lunr.TokenSet._nextId + lunr.TokenSet._nextId += 1 +} + +/** + * Keeps track of the next, auto increment, identifier to assign + * to a new tokenSet. + * + * TokenSets require a unique identifier to be correctly minimised. + * + * @private + */ +lunr.TokenSet._nextId = 1 + +/** + * Creates a TokenSet instance from the given sorted array of words. + * + * @param {String[]} arr - A sorted array of strings to create the set from. + * @returns {lunr.TokenSet} + * @throws Will throw an error if the input array is not sorted. + */ +lunr.TokenSet.fromArray = function (arr) { + var builder = new lunr.TokenSet.Builder + + for (var i = 0, len = arr.length; i < len; i++) { + builder.insert(arr[i]) + } + + builder.finish() + return builder.root +} + +/** + * Creates a token set from a query clause. + * + * @private + * @param {Object} clause - A single clause from lunr.Query. + * @param {string} clause.term - The query clause term. + * @param {number} [clause.editDistance] - The optional edit distance for the term. + * @returns {lunr.TokenSet} + */ +lunr.TokenSet.fromClause = function (clause) { + if ('editDistance' in clause) { + return lunr.TokenSet.fromFuzzyString(clause.term, clause.editDistance) + } else { + return lunr.TokenSet.fromString(clause.term) + } +} + +/** + * Creates a token set representing a single string with a specified + * edit distance. + * + * Insertions, deletions, substitutions and transpositions are each + * treated as an edit distance of 1. + * + * Increasing the allowed edit distance will have a dramatic impact + * on the performance of both creating and intersecting these TokenSets. + * It is advised to keep the edit distance less than 3. + * + * @param {string} str - The string to create the token set from. + * @param {number} editDistance - The allowed edit distance to match. + * @returns {lunr.Vector} + */ +lunr.TokenSet.fromFuzzyString = function (str, editDistance) { + var root = new lunr.TokenSet + + var stack = [{ + node: root, + editsRemaining: editDistance, + str: str + }] + + while (stack.length) { + var frame = stack.pop() + + // no edit + if (frame.str.length > 0) { + var char = frame.str.charAt(0), + noEditNode + + if (char in frame.node.edges) { + noEditNode = frame.node.edges[char] + } else { + noEditNode = new lunr.TokenSet + frame.node.edges[char] = noEditNode + } + + if (frame.str.length == 1) { + noEditNode.final = true + } + + stack.push({ + node: noEditNode, + editsRemaining: frame.editsRemaining, + str: frame.str.slice(1) + }) + } + + if (frame.editsRemaining == 0) { + continue + } + + // insertion + if ("*" in frame.node.edges) { + var insertionNode = frame.node.edges["*"] + } else { + var insertionNode = new lunr.TokenSet + frame.node.edges["*"] = insertionNode + } + + if (frame.str.length == 0) { + insertionNode.final = true + } + + stack.push({ + node: insertionNode, + editsRemaining: frame.editsRemaining - 1, + str: frame.str + }) + + // deletion + // can only do a deletion if we have enough edits remaining + // and if there are characters left to delete in the string + if (frame.str.length > 1) { + stack.push({ + node: frame.node, + editsRemaining: frame.editsRemaining - 1, + str: frame.str.slice(1) + }) + } + + // deletion + // just removing the last character from the str + if (frame.str.length == 1) { + frame.node.final = true + } + + // substitution + // can only do a substitution if we have enough edits remaining + // and if there are characters left to substitute + if (frame.str.length >= 1) { + if ("*" in frame.node.edges) { + var substitutionNode = frame.node.edges["*"] + } else { + var substitutionNode = new lunr.TokenSet + frame.node.edges["*"] = substitutionNode + } + + if (frame.str.length == 1) { + substitutionNode.final = true + } + + stack.push({ + node: substitutionNode, + editsRemaining: frame.editsRemaining - 1, + str: frame.str.slice(1) + }) + } + + // transposition + // can only do a transposition if there are edits remaining + // and there are enough characters to transpose + if (frame.str.length > 1) { + var charA = frame.str.charAt(0), + charB = frame.str.charAt(1), + transposeNode + + if (charB in frame.node.edges) { + transposeNode = frame.node.edges[charB] + } else { + transposeNode = new lunr.TokenSet + frame.node.edges[charB] = transposeNode + } + + if (frame.str.length == 1) { + transposeNode.final = true + } + + stack.push({ + node: transposeNode, + editsRemaining: frame.editsRemaining - 1, + str: charA + frame.str.slice(2) + }) + } + } + + return root +} + +/** + * Creates a TokenSet from a string. + * + * The string may contain one or more wildcard characters (*) + * that will allow wildcard matching when intersecting with + * another TokenSet. + * + * @param {string} str - The string to create a TokenSet from. + * @returns {lunr.TokenSet} + */ +lunr.TokenSet.fromString = function (str) { + var node = new lunr.TokenSet, + root = node + + /* + * Iterates through all characters within the passed string + * appending a node for each character. + * + * When a wildcard character is found then a self + * referencing edge is introduced to continually match + * any number of any characters. + */ + for (var i = 0, len = str.length; i < len; i++) { + var char = str[i], + final = (i == len - 1) + + if (char == "*") { + node.edges[char] = node + node.final = final + + } else { + var next = new lunr.TokenSet + next.final = final + + node.edges[char] = next + node = next + } + } + + return root +} + +/** + * Converts this TokenSet into an array of strings + * contained within the TokenSet. + * + * This is not intended to be used on a TokenSet that + * contains wildcards, in these cases the results are + * undefined and are likely to cause an infinite loop. + * + * @returns {string[]} + */ +lunr.TokenSet.prototype.toArray = function () { + var words = [] + + var stack = [{ + prefix: "", + node: this + }] + + while (stack.length) { + var frame = stack.pop(), + edges = Object.keys(frame.node.edges), + len = edges.length + + if (frame.node.final) { + /* In Safari, at this point the prefix is sometimes corrupted, see: + * https://github.com/olivernn/lunr.js/issues/279 Calling any + * String.prototype method forces Safari to "cast" this string to what + * it's supposed to be, fixing the bug. */ + frame.prefix.charAt(0) + words.push(frame.prefix) + } + + for (var i = 0; i < len; i++) { + var edge = edges[i] + + stack.push({ + prefix: frame.prefix.concat(edge), + node: frame.node.edges[edge] + }) + } + } + + return words +} + +/** + * Generates a string representation of a TokenSet. + * + * This is intended to allow TokenSets to be used as keys + * in objects, largely to aid the construction and minimisation + * of a TokenSet. As such it is not designed to be a human + * friendly representation of the TokenSet. + * + * @returns {string} + */ +lunr.TokenSet.prototype.toString = function () { + // NOTE: Using Object.keys here as this.edges is very likely + // to enter 'hash-mode' with many keys being added + // + // avoiding a for-in loop here as it leads to the function + // being de-optimised (at least in V8). From some simple + // benchmarks the performance is comparable, but allowing + // V8 to optimize may mean easy performance wins in the future. + + if (this._str) { + return this._str + } + + var str = this.final ? '1' : '0', + labels = Object.keys(this.edges).sort(), + len = labels.length + + for (var i = 0; i < len; i++) { + var label = labels[i], + node = this.edges[label] + + str = str + label + node.id + } + + return str +} + +/** + * Returns a new TokenSet that is the intersection of + * this TokenSet and the passed TokenSet. + * + * This intersection will take into account any wildcards + * contained within the TokenSet. + * + * @param {lunr.TokenSet} b - An other TokenSet to intersect with. + * @returns {lunr.TokenSet} + */ +lunr.TokenSet.prototype.intersect = function (b) { + var output = new lunr.TokenSet, + frame = undefined + + var stack = [{ + qNode: b, + output: output, + node: this + }] + + while (stack.length) { + frame = stack.pop() + + // NOTE: As with the #toString method, we are using + // Object.keys and a for loop instead of a for-in loop + // as both of these objects enter 'hash' mode, causing + // the function to be de-optimised in V8 + var qEdges = Object.keys(frame.qNode.edges), + qLen = qEdges.length, + nEdges = Object.keys(frame.node.edges), + nLen = nEdges.length + + for (var q = 0; q < qLen; q++) { + var qEdge = qEdges[q] + + for (var n = 0; n < nLen; n++) { + var nEdge = nEdges[n] + + if (nEdge == qEdge || qEdge == '*') { + var node = frame.node.edges[nEdge], + qNode = frame.qNode.edges[qEdge], + final = node.final && qNode.final, + next = undefined + + if (nEdge in frame.output.edges) { + // an edge already exists for this character + // no need to create a new node, just set the finality + // bit unless this node is already final + next = frame.output.edges[nEdge] + next.final = next.final || final + + } else { + // no edge exists yet, must create one + // set the finality bit and insert it + // into the output + next = new lunr.TokenSet + next.final = final + frame.output.edges[nEdge] = next + } + + stack.push({ + qNode: qNode, + output: next, + node: node + }) + } + } + } + } + + return output +} +lunr.TokenSet.Builder = function () { + this.previousWord = "" + this.root = new lunr.TokenSet + this.uncheckedNodes = [] + this.minimizedNodes = {} +} + +lunr.TokenSet.Builder.prototype.insert = function (word) { + var node, + commonPrefix = 0 + + if (word < this.previousWord) { + throw new Error ("Out of order word insertion") + } + + for (var i = 0; i < word.length && i < this.previousWord.length; i++) { + if (word[i] != this.previousWord[i]) break + commonPrefix++ + } + + this.minimize(commonPrefix) + + if (this.uncheckedNodes.length == 0) { + node = this.root + } else { + node = this.uncheckedNodes[this.uncheckedNodes.length - 1].child + } + + for (var i = commonPrefix; i < word.length; i++) { + var nextNode = new lunr.TokenSet, + char = word[i] + + node.edges[char] = nextNode + + this.uncheckedNodes.push({ + parent: node, + char: char, + child: nextNode + }) + + node = nextNode + } + + node.final = true + this.previousWord = word +} + +lunr.TokenSet.Builder.prototype.finish = function () { + this.minimize(0) +} + +lunr.TokenSet.Builder.prototype.minimize = function (downTo) { + for (var i = this.uncheckedNodes.length - 1; i >= downTo; i--) { + var node = this.uncheckedNodes[i], + childKey = node.child.toString() + + if (childKey in this.minimizedNodes) { + node.parent.edges[node.char] = this.minimizedNodes[childKey] + } else { + // Cache the key for this node since + // we know it can't change anymore + node.child._str = childKey + + this.minimizedNodes[childKey] = node.child + } + + this.uncheckedNodes.pop() + } +} +/*! + * lunr.Index + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * An index contains the built index of all documents and provides a query interface + * to the index. + * + * Usually instances of lunr.Index will not be created using this constructor, instead + * lunr.Builder should be used to construct new indexes, or lunr.Index.load should be + * used to load previously built and serialized indexes. + * + * @constructor + * @param {Object} attrs - The attributes of the built search index. + * @param {Object} attrs.invertedIndex - An index of term/field to document reference. + * @param {Object} attrs.fieldVectors - Field vectors + * @param {lunr.TokenSet} attrs.tokenSet - An set of all corpus tokens. + * @param {string[]} attrs.fields - The names of indexed document fields. + * @param {lunr.Pipeline} attrs.pipeline - The pipeline to use for search terms. + */ +lunr.Index = function (attrs) { + this.invertedIndex = attrs.invertedIndex + this.fieldVectors = attrs.fieldVectors + this.tokenSet = attrs.tokenSet + this.fields = attrs.fields + this.pipeline = attrs.pipeline +} + +/** + * A result contains details of a document matching a search query. + * @typedef {Object} lunr.Index~Result + * @property {string} ref - The reference of the document this result represents. + * @property {number} score - A number between 0 and 1 representing how similar this document is to the query. + * @property {lunr.MatchData} matchData - Contains metadata about this match including which term(s) caused the match. + */ + +/** + * Although lunr provides the ability to create queries using lunr.Query, it also provides a simple + * query language which itself is parsed into an instance of lunr.Query. + * + * For programmatically building queries it is advised to directly use lunr.Query, the query language + * is best used for human entered text rather than program generated text. + * + * At its simplest queries can just be a single term, e.g. `hello`, multiple terms are also supported + * and will be combined with OR, e.g `hello world` will match documents that contain either 'hello' + * or 'world', though those that contain both will rank higher in the results. + * + * Wildcards can be included in terms to match one or more unspecified characters, these wildcards can + * be inserted anywhere within the term, and more than one wildcard can exist in a single term. Adding + * wildcards will increase the number of documents that will be found but can also have a negative + * impact on query performance, especially with wildcards at the beginning of a term. + * + * Terms can be restricted to specific fields, e.g. `title:hello`, only documents with the term + * hello in the title field will match this query. Using a field not present in the index will lead + * to an error being thrown. + * + * Modifiers can also be added to terms, lunr supports edit distance and boost modifiers on terms. A term + * boost will make documents matching that term score higher, e.g. `foo^5`. Edit distance is also supported + * to provide fuzzy matching, e.g. 'hello~2' will match documents with hello with an edit distance of 2. + * Avoid large values for edit distance to improve query performance. + * + * Each term also supports a presence modifier. By default a term's presence in document is optional, however + * this can be changed to either required or prohibited. For a term's presence to be required in a document the + * term should be prefixed with a '+', e.g. `+foo bar` is a search for documents that must contain 'foo' and + * optionally contain 'bar'. Conversely a leading '-' sets the terms presence to prohibited, i.e. it must not + * appear in a document, e.g. `-foo bar` is a search for documents that do not contain 'foo' but may contain 'bar'. + * + * To escape special characters the backslash character '\' can be used, this allows searches to include + * characters that would normally be considered modifiers, e.g. `foo\~2` will search for a term "foo~2" instead + * of attempting to apply a boost of 2 to the search term "foo". + * + * @typedef {string} lunr.Index~QueryString + * @example Simple single term query + * hello + * @example Multiple term query + * hello world + * @example term scoped to a field + * title:hello + * @example term with a boost of 10 + * hello^10 + * @example term with an edit distance of 2 + * hello~2 + * @example terms with presence modifiers + * -foo +bar baz + */ + +/** + * Performs a search against the index using lunr query syntax. + * + * Results will be returned sorted by their score, the most relevant results + * will be returned first. For details on how the score is calculated, please see + * the {@link https://lunrjs.com/guides/searching.html#scoring|guide}. + * + * For more programmatic querying use lunr.Index#query. + * + * @param {lunr.Index~QueryString} queryString - A string containing a lunr query. + * @throws {lunr.QueryParseError} If the passed query string cannot be parsed. + * @returns {lunr.Index~Result[]} + */ +lunr.Index.prototype.search = function (queryString) { + return this.query(function (query) { + var parser = new lunr.QueryParser(queryString, query) + parser.parse() + }) +} + +/** + * A query builder callback provides a query object to be used to express + * the query to perform on the index. + * + * @callback lunr.Index~queryBuilder + * @param {lunr.Query} query - The query object to build up. + * @this lunr.Query + */ + +/** + * Performs a query against the index using the yielded lunr.Query object. + * + * If performing programmatic queries against the index, this method is preferred + * over lunr.Index#search so as to avoid the additional query parsing overhead. + * + * A query object is yielded to the supplied function which should be used to + * express the query to be run against the index. + * + * Note that although this function takes a callback parameter it is _not_ an + * asynchronous operation, the callback is just yielded a query object to be + * customized. + * + * @param {lunr.Index~queryBuilder} fn - A function that is used to build the query. + * @returns {lunr.Index~Result[]} + */ +lunr.Index.prototype.query = function (fn) { + // for each query clause + // * process terms + // * expand terms from token set + // * find matching documents and metadata + // * get document vectors + // * score documents + + var query = new lunr.Query(this.fields), + matchingFields = Object.create(null), + queryVectors = Object.create(null), + termFieldCache = Object.create(null), + requiredMatches = Object.create(null), + prohibitedMatches = Object.create(null) + + /* + * To support field level boosts a query vector is created per + * field. An empty vector is eagerly created to support negated + * queries. + */ + for (var i = 0; i < this.fields.length; i++) { + queryVectors[this.fields[i]] = new lunr.Vector + } + + fn.call(query, query) + + for (var i = 0; i < query.clauses.length; i++) { + /* + * Unless the pipeline has been disabled for this term, which is + * the case for terms with wildcards, we need to pass the clause + * term through the search pipeline. A pipeline returns an array + * of processed terms. Pipeline functions may expand the passed + * term, which means we may end up performing multiple index lookups + * for a single query term. + */ + var clause = query.clauses[i], + terms = null, + clauseMatches = lunr.Set.empty + + if (clause.usePipeline) { + terms = this.pipeline.runString(clause.term, { + fields: clause.fields + }) + } else { + terms = [clause.term] + } + + for (var m = 0; m < terms.length; m++) { + var term = terms[m] + + /* + * Each term returned from the pipeline needs to use the same query + * clause object, e.g. the same boost and or edit distance. The + * simplest way to do this is to re-use the clause object but mutate + * its term property. + */ + clause.term = term + + /* + * From the term in the clause we create a token set which will then + * be used to intersect the indexes token set to get a list of terms + * to lookup in the inverted index + */ + var termTokenSet = lunr.TokenSet.fromClause(clause), + expandedTerms = this.tokenSet.intersect(termTokenSet).toArray() + + /* + * If a term marked as required does not exist in the tokenSet it is + * impossible for the search to return any matches. We set all the field + * scoped required matches set to empty and stop examining any further + * clauses. + */ + if (expandedTerms.length === 0 && clause.presence === lunr.Query.presence.REQUIRED) { + for (var k = 0; k < clause.fields.length; k++) { + var field = clause.fields[k] + requiredMatches[field] = lunr.Set.empty + } + + break + } + + for (var j = 0; j < expandedTerms.length; j++) { + /* + * For each term get the posting and termIndex, this is required for + * building the query vector. + */ + var expandedTerm = expandedTerms[j], + posting = this.invertedIndex[expandedTerm], + termIndex = posting._index + + for (var k = 0; k < clause.fields.length; k++) { + /* + * For each field that this query term is scoped by (by default + * all fields are in scope) we need to get all the document refs + * that have this term in that field. + * + * The posting is the entry in the invertedIndex for the matching + * term from above. + */ + var field = clause.fields[k], + fieldPosting = posting[field], + matchingDocumentRefs = Object.keys(fieldPosting), + termField = expandedTerm + "/" + field, + matchingDocumentsSet = new lunr.Set(matchingDocumentRefs) + + /* + * if the presence of this term is required ensure that the matching + * documents are added to the set of required matches for this clause. + * + */ + if (clause.presence == lunr.Query.presence.REQUIRED) { + clauseMatches = clauseMatches.union(matchingDocumentsSet) + + if (requiredMatches[field] === undefined) { + requiredMatches[field] = lunr.Set.complete + } + } + + /* + * if the presence of this term is prohibited ensure that the matching + * documents are added to the set of prohibited matches for this field, + * creating that set if it does not yet exist. + */ + if (clause.presence == lunr.Query.presence.PROHIBITED) { + if (prohibitedMatches[field] === undefined) { + prohibitedMatches[field] = lunr.Set.empty + } + + prohibitedMatches[field] = prohibitedMatches[field].union(matchingDocumentsSet) + + /* + * Prohibited matches should not be part of the query vector used for + * similarity scoring and no metadata should be extracted so we continue + * to the next field + */ + continue + } + + /* + * The query field vector is populated using the termIndex found for + * the term and a unit value with the appropriate boost applied. + * Using upsert because there could already be an entry in the vector + * for the term we are working with. In that case we just add the scores + * together. + */ + queryVectors[field].upsert(termIndex, clause.boost, function (a, b) { return a + b }) + + /** + * If we've already seen this term, field combo then we've already collected + * the matching documents and metadata, no need to go through all that again + */ + if (termFieldCache[termField]) { + continue + } + + for (var l = 0; l < matchingDocumentRefs.length; l++) { + /* + * All metadata for this term/field/document triple + * are then extracted and collected into an instance + * of lunr.MatchData ready to be returned in the query + * results + */ + var matchingDocumentRef = matchingDocumentRefs[l], + matchingFieldRef = new lunr.FieldRef (matchingDocumentRef, field), + metadata = fieldPosting[matchingDocumentRef], + fieldMatch + + if ((fieldMatch = matchingFields[matchingFieldRef]) === undefined) { + matchingFields[matchingFieldRef] = new lunr.MatchData (expandedTerm, field, metadata) + } else { + fieldMatch.add(expandedTerm, field, metadata) + } + + } + + termFieldCache[termField] = true + } + } + } + + /** + * If the presence was required we need to update the requiredMatches field sets. + * We do this after all fields for the term have collected their matches because + * the clause terms presence is required in _any_ of the fields not _all_ of the + * fields. + */ + if (clause.presence === lunr.Query.presence.REQUIRED) { + for (var k = 0; k < clause.fields.length; k++) { + var field = clause.fields[k] + requiredMatches[field] = requiredMatches[field].intersect(clauseMatches) + } + } + } + + /** + * Need to combine the field scoped required and prohibited + * matching documents into a global set of required and prohibited + * matches + */ + var allRequiredMatches = lunr.Set.complete, + allProhibitedMatches = lunr.Set.empty + + for (var i = 0; i < this.fields.length; i++) { + var field = this.fields[i] + + if (requiredMatches[field]) { + allRequiredMatches = allRequiredMatches.intersect(requiredMatches[field]) + } + + if (prohibitedMatches[field]) { + allProhibitedMatches = allProhibitedMatches.union(prohibitedMatches[field]) + } + } + + var matchingFieldRefs = Object.keys(matchingFields), + results = [], + matches = Object.create(null) + + /* + * If the query is negated (contains only prohibited terms) + * we need to get _all_ fieldRefs currently existing in the + * index. This is only done when we know that the query is + * entirely prohibited terms to avoid any cost of getting all + * fieldRefs unnecessarily. + * + * Additionally, blank MatchData must be created to correctly + * populate the results. + */ + if (query.isNegated()) { + matchingFieldRefs = Object.keys(this.fieldVectors) + + for (var i = 0; i < matchingFieldRefs.length; i++) { + var matchingFieldRef = matchingFieldRefs[i] + var fieldRef = lunr.FieldRef.fromString(matchingFieldRef) + matchingFields[matchingFieldRef] = new lunr.MatchData + } + } + + for (var i = 0; i < matchingFieldRefs.length; i++) { + /* + * Currently we have document fields that match the query, but we + * need to return documents. The matchData and scores are combined + * from multiple fields belonging to the same document. + * + * Scores are calculated by field, using the query vectors created + * above, and combined into a final document score using addition. + */ + var fieldRef = lunr.FieldRef.fromString(matchingFieldRefs[i]), + docRef = fieldRef.docRef + + if (!allRequiredMatches.contains(docRef)) { + continue + } + + if (allProhibitedMatches.contains(docRef)) { + continue + } + + var fieldVector = this.fieldVectors[fieldRef], + score = queryVectors[fieldRef.fieldName].similarity(fieldVector), + docMatch + + if ((docMatch = matches[docRef]) !== undefined) { + docMatch.score += score + docMatch.matchData.combine(matchingFields[fieldRef]) + } else { + var match = { + ref: docRef, + score: score, + matchData: matchingFields[fieldRef] + } + matches[docRef] = match + results.push(match) + } + } + + /* + * Sort the results objects by score, highest first. + */ + return results.sort(function (a, b) { + return b.score - a.score + }) +} + +/** + * Prepares the index for JSON serialization. + * + * The schema for this JSON blob will be described in a + * separate JSON schema file. + * + * @returns {Object} + */ +lunr.Index.prototype.toJSON = function () { + var invertedIndex = Object.keys(this.invertedIndex) + .sort() + .map(function (term) { + return [term, this.invertedIndex[term]] + }, this) + + var fieldVectors = Object.keys(this.fieldVectors) + .map(function (ref) { + return [ref, this.fieldVectors[ref].toJSON()] + }, this) + + return { + version: lunr.version, + fields: this.fields, + fieldVectors: fieldVectors, + invertedIndex: invertedIndex, + pipeline: this.pipeline.toJSON() + } +} + +/** + * Loads a previously serialized lunr.Index + * + * @param {Object} serializedIndex - A previously serialized lunr.Index + * @returns {lunr.Index} + */ +lunr.Index.load = function (serializedIndex) { + var attrs = {}, + fieldVectors = {}, + serializedVectors = serializedIndex.fieldVectors, + invertedIndex = Object.create(null), + serializedInvertedIndex = serializedIndex.invertedIndex, + tokenSetBuilder = new lunr.TokenSet.Builder, + pipeline = lunr.Pipeline.load(serializedIndex.pipeline) + + if (serializedIndex.version != lunr.version) { + lunr.utils.warn("Version mismatch when loading serialised index. Current version of lunr '" + lunr.version + "' does not match serialized index '" + serializedIndex.version + "'") + } + + for (var i = 0; i < serializedVectors.length; i++) { + var tuple = serializedVectors[i], + ref = tuple[0], + elements = tuple[1] + + fieldVectors[ref] = new lunr.Vector(elements) + } + + for (var i = 0; i < serializedInvertedIndex.length; i++) { + var tuple = serializedInvertedIndex[i], + term = tuple[0], + posting = tuple[1] + + tokenSetBuilder.insert(term) + invertedIndex[term] = posting + } + + tokenSetBuilder.finish() + + attrs.fields = serializedIndex.fields + + attrs.fieldVectors = fieldVectors + attrs.invertedIndex = invertedIndex + attrs.tokenSet = tokenSetBuilder.root + attrs.pipeline = pipeline + + return new lunr.Index(attrs) +} +/*! + * lunr.Builder + * Copyright (C) 2020 Oliver Nightingale + */ + +/** + * lunr.Builder performs indexing on a set of documents and + * returns instances of lunr.Index ready for querying. + * + * All configuration of the index is done via the builder, the + * fields to index, the document reference, the text processing + * pipeline and document scoring parameters are all set on the + * builder before indexing. + * + * @constructor + * @property {string} _ref - Internal reference to the document reference field. + * @property {string[]} _fields - Internal reference to the document fields to index. + * @property {object} invertedIndex - The inverted index maps terms to document fields. + * @property {object} documentTermFrequencies - Keeps track of document term frequencies. + * @property {object} documentLengths - Keeps track of the length of documents added to the index. + * @property {lunr.tokenizer} tokenizer - Function for splitting strings into tokens for indexing. + * @property {lunr.Pipeline} pipeline - The pipeline performs text processing on tokens before indexing. + * @property {lunr.Pipeline} searchPipeline - A pipeline for processing search terms before querying the index. + * @property {number} documentCount - Keeps track of the total number of documents indexed. + * @property {number} _b - A parameter to control field length normalization, setting this to 0 disabled normalization, 1 fully normalizes field lengths, the default value is 0.75. + * @property {number} _k1 - A parameter to control how quickly an increase in term frequency results in term frequency saturation, the default value is 1.2. + * @property {number} termIndex - A counter incremented for each unique term, used to identify a terms position in the vector space. + * @property {array} metadataWhitelist - A list of metadata keys that have been whitelisted for entry in the index. + */ +lunr.Builder = function () { + this._ref = "id" + this._fields = Object.create(null) + this._documents = Object.create(null) + this.invertedIndex = Object.create(null) + this.fieldTermFrequencies = {} + this.fieldLengths = {} + this.tokenizer = lunr.tokenizer + this.pipeline = new lunr.Pipeline + this.searchPipeline = new lunr.Pipeline + this.documentCount = 0 + this._b = 0.75 + this._k1 = 1.2 + this.termIndex = 0 + this.metadataWhitelist = [] +} + +/** + * Sets the document field used as the document reference. Every document must have this field. + * The type of this field in the document should be a string, if it is not a string it will be + * coerced into a string by calling toString. + * + * The default ref is 'id'. + * + * The ref should _not_ be changed during indexing, it should be set before any documents are + * added to the index. Changing it during indexing can lead to inconsistent results. + * + * @param {string} ref - The name of the reference field in the document. + */ +lunr.Builder.prototype.ref = function (ref) { + this._ref = ref +} + +/** + * A function that is used to extract a field from a document. + * + * Lunr expects a field to be at the top level of a document, if however the field + * is deeply nested within a document an extractor function can be used to extract + * the right field for indexing. + * + * @callback fieldExtractor + * @param {object} doc - The document being added to the index. + * @returns {?(string|object|object[])} obj - The object that will be indexed for this field. + * @example Extracting a nested field + * function (doc) { return doc.nested.field } + */ + +/** + * Adds a field to the list of document fields that will be indexed. Every document being + * indexed should have this field. Null values for this field in indexed documents will + * not cause errors but will limit the chance of that document being retrieved by searches. + * + * All fields should be added before adding documents to the index. Adding fields after + * a document has been indexed will have no effect on already indexed documents. + * + * Fields can be boosted at build time. This allows terms within that field to have more + * importance when ranking search results. Use a field boost to specify that matches within + * one field are more important than other fields. + * + * @param {string} fieldName - The name of a field to index in all documents. + * @param {object} attributes - Optional attributes associated with this field. + * @param {number} [attributes.boost=1] - Boost applied to all terms within this field. + * @param {fieldExtractor} [attributes.extractor] - Function to extract a field from a document. + * @throws {RangeError} fieldName cannot contain unsupported characters '/' + */ +lunr.Builder.prototype.field = function (fieldName, attributes) { + if (/\//.test(fieldName)) { + throw new RangeError ("Field '" + fieldName + "' contains illegal character '/'") + } + + this._fields[fieldName] = attributes || {} +} + +/** + * A parameter to tune the amount of field length normalisation that is applied when + * calculating relevance scores. A value of 0 will completely disable any normalisation + * and a value of 1 will fully normalise field lengths. The default is 0.75. Values of b + * will be clamped to the range 0 - 1. + * + * @param {number} number - The value to set for this tuning parameter. + */ +lunr.Builder.prototype.b = function (number) { + if (number < 0) { + this._b = 0 + } else if (number > 1) { + this._b = 1 + } else { + this._b = number + } +} + +/** + * A parameter that controls the speed at which a rise in term frequency results in term + * frequency saturation. The default value is 1.2. Setting this to a higher value will give + * slower saturation levels, a lower value will result in quicker saturation. + * + * @param {number} number - The value to set for this tuning parameter. + */ +lunr.Builder.prototype.k1 = function (number) { + this._k1 = number +} + +/** + * Adds a document to the index. + * + * Before adding fields to the index the index should have been fully setup, with the document + * ref and all fields to index already having been specified. + * + * The document must have a field name as specified by the ref (by default this is 'id') and + * it should have all fields defined for indexing, though null or undefined values will not + * cause errors. + * + * Entire documents can be boosted at build time. Applying a boost to a document indicates that + * this document should rank higher in search results than other documents. + * + * @param {object} doc - The document to add to the index. + * @param {object} attributes - Optional attributes associated with this document. + * @param {number} [attributes.boost=1] - Boost applied to all terms within this document. + */ +lunr.Builder.prototype.add = function (doc, attributes) { + var docRef = doc[this._ref], + fields = Object.keys(this._fields) + + this._documents[docRef] = attributes || {} + this.documentCount += 1 + + for (var i = 0; i < fields.length; i++) { + var fieldName = fields[i], + extractor = this._fields[fieldName].extractor, + field = extractor ? extractor(doc) : doc[fieldName], + tokens = this.tokenizer(field, { + fields: [fieldName] + }), + terms = this.pipeline.run(tokens), + fieldRef = new lunr.FieldRef (docRef, fieldName), + fieldTerms = Object.create(null) + + this.fieldTermFrequencies[fieldRef] = fieldTerms + this.fieldLengths[fieldRef] = 0 + + // store the length of this field for this document + this.fieldLengths[fieldRef] += terms.length + + // calculate term frequencies for this field + for (var j = 0; j < terms.length; j++) { + var term = terms[j] + + if (fieldTerms[term] == undefined) { + fieldTerms[term] = 0 + } + + fieldTerms[term] += 1 + + // add to inverted index + // create an initial posting if one doesn't exist + if (this.invertedIndex[term] == undefined) { + var posting = Object.create(null) + posting["_index"] = this.termIndex + this.termIndex += 1 + + for (var k = 0; k < fields.length; k++) { + posting[fields[k]] = Object.create(null) + } + + this.invertedIndex[term] = posting + } + + // add an entry for this term/fieldName/docRef to the invertedIndex + if (this.invertedIndex[term][fieldName][docRef] == undefined) { + this.invertedIndex[term][fieldName][docRef] = Object.create(null) + } + + // store all whitelisted metadata about this token in the + // inverted index + for (var l = 0; l < this.metadataWhitelist.length; l++) { + var metadataKey = this.metadataWhitelist[l], + metadata = term.metadata[metadataKey] + + if (this.invertedIndex[term][fieldName][docRef][metadataKey] == undefined) { + this.invertedIndex[term][fieldName][docRef][metadataKey] = [] + } + + this.invertedIndex[term][fieldName][docRef][metadataKey].push(metadata) + } + } + + } +} + +/** + * Calculates the average document length for this index + * + * @private + */ +lunr.Builder.prototype.calculateAverageFieldLengths = function () { + + var fieldRefs = Object.keys(this.fieldLengths), + numberOfFields = fieldRefs.length, + accumulator = {}, + documentsWithField = {} + + for (var i = 0; i < numberOfFields; i++) { + var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]), + field = fieldRef.fieldName + + documentsWithField[field] || (documentsWithField[field] = 0) + documentsWithField[field] += 1 + + accumulator[field] || (accumulator[field] = 0) + accumulator[field] += this.fieldLengths[fieldRef] + } + + var fields = Object.keys(this._fields) + + for (var i = 0; i < fields.length; i++) { + var fieldName = fields[i] + accumulator[fieldName] = accumulator[fieldName] / documentsWithField[fieldName] + } + + this.averageFieldLength = accumulator +} + +/** + * Builds a vector space model of every document using lunr.Vector + * + * @private + */ +lunr.Builder.prototype.createFieldVectors = function () { + var fieldVectors = {}, + fieldRefs = Object.keys(this.fieldTermFrequencies), + fieldRefsLength = fieldRefs.length, + termIdfCache = Object.create(null) + + for (var i = 0; i < fieldRefsLength; i++) { + var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]), + fieldName = fieldRef.fieldName, + fieldLength = this.fieldLengths[fieldRef], + fieldVector = new lunr.Vector, + termFrequencies = this.fieldTermFrequencies[fieldRef], + terms = Object.keys(termFrequencies), + termsLength = terms.length + + + var fieldBoost = this._fields[fieldName].boost || 1, + docBoost = this._documents[fieldRef.docRef].boost || 1 + + for (var j = 0; j < termsLength; j++) { + var term = terms[j], + tf = termFrequencies[term], + termIndex = this.invertedIndex[term]._index, + idf, score, scoreWithPrecision + + if (termIdfCache[term] === undefined) { + idf = lunr.idf(this.invertedIndex[term], this.documentCount) + termIdfCache[term] = idf + } else { + idf = termIdfCache[term] + } + + score = idf * ((this._k1 + 1) * tf) / (this._k1 * (1 - this._b + this._b * (fieldLength / this.averageFieldLength[fieldName])) + tf) + score *= fieldBoost + score *= docBoost + scoreWithPrecision = Math.round(score * 1000) / 1000 + // Converts 1.23456789 to 1.234. + // Reducing the precision so that the vectors take up less + // space when serialised. Doing it now so that they behave + // the same before and after serialisation. Also, this is + // the fastest approach to reducing a number's precision in + // JavaScript. + + fieldVector.insert(termIndex, scoreWithPrecision) + } + + fieldVectors[fieldRef] = fieldVector + } + + this.fieldVectors = fieldVectors +} + +/** + * Creates a token set of all tokens in the index using lunr.TokenSet + * + * @private + */ +lunr.Builder.prototype.createTokenSet = function () { + this.tokenSet = lunr.TokenSet.fromArray( + Object.keys(this.invertedIndex).sort() + ) +} + +/** + * Builds the index, creating an instance of lunr.Index. + * + * This completes the indexing process and should only be called + * once all documents have been added to the index. + * + * @returns {lunr.Index} + */ +lunr.Builder.prototype.build = function () { + this.calculateAverageFieldLengths() + this.createFieldVectors() + this.createTokenSet() + + return new lunr.Index({ + invertedIndex: this.invertedIndex, + fieldVectors: this.fieldVectors, + tokenSet: this.tokenSet, + fields: Object.keys(this._fields), + pipeline: this.searchPipeline + }) +} + +/** + * Applies a plugin to the index builder. + * + * A plugin is a function that is called with the index builder as its context. + * Plugins can be used to customise or extend the behaviour of the index + * in some way. A plugin is just a function, that encapsulated the custom + * behaviour that should be applied when building the index. + * + * The plugin function will be called with the index builder as its argument, additional + * arguments can also be passed when calling use. The function will be called + * with the index builder as its context. + * + * @param {Function} plugin The plugin to apply. + */ +lunr.Builder.prototype.use = function (fn) { + var args = Array.prototype.slice.call(arguments, 1) + args.unshift(this) + fn.apply(this, args) +} +/** + * Contains and collects metadata about a matching document. + * A single instance of lunr.MatchData is returned as part of every + * lunr.Index~Result. + * + * @constructor + * @param {string} term - The term this match data is associated with + * @param {string} field - The field in which the term was found + * @param {object} metadata - The metadata recorded about this term in this field + * @property {object} metadata - A cloned collection of metadata associated with this document. + * @see {@link lunr.Index~Result} + */ +lunr.MatchData = function (term, field, metadata) { + var clonedMetadata = Object.create(null), + metadataKeys = Object.keys(metadata || {}) + + // Cloning the metadata to prevent the original + // being mutated during match data combination. + // Metadata is kept in an array within the inverted + // index so cloning the data can be done with + // Array#slice + for (var i = 0; i < metadataKeys.length; i++) { + var key = metadataKeys[i] + clonedMetadata[key] = metadata[key].slice() + } + + this.metadata = Object.create(null) + + if (term !== undefined) { + this.metadata[term] = Object.create(null) + this.metadata[term][field] = clonedMetadata + } +} + +/** + * An instance of lunr.MatchData will be created for every term that matches a + * document. However only one instance is required in a lunr.Index~Result. This + * method combines metadata from another instance of lunr.MatchData with this + * objects metadata. + * + * @param {lunr.MatchData} otherMatchData - Another instance of match data to merge with this one. + * @see {@link lunr.Index~Result} + */ +lunr.MatchData.prototype.combine = function (otherMatchData) { + var terms = Object.keys(otherMatchData.metadata) + + for (var i = 0; i < terms.length; i++) { + var term = terms[i], + fields = Object.keys(otherMatchData.metadata[term]) + + if (this.metadata[term] == undefined) { + this.metadata[term] = Object.create(null) + } + + for (var j = 0; j < fields.length; j++) { + var field = fields[j], + keys = Object.keys(otherMatchData.metadata[term][field]) + + if (this.metadata[term][field] == undefined) { + this.metadata[term][field] = Object.create(null) + } + + for (var k = 0; k < keys.length; k++) { + var key = keys[k] + + if (this.metadata[term][field][key] == undefined) { + this.metadata[term][field][key] = otherMatchData.metadata[term][field][key] + } else { + this.metadata[term][field][key] = this.metadata[term][field][key].concat(otherMatchData.metadata[term][field][key]) + } + + } + } + } +} + +/** + * Add metadata for a term/field pair to this instance of match data. + * + * @param {string} term - The term this match data is associated with + * @param {string} field - The field in which the term was found + * @param {object} metadata - The metadata recorded about this term in this field + */ +lunr.MatchData.prototype.add = function (term, field, metadata) { + if (!(term in this.metadata)) { + this.metadata[term] = Object.create(null) + this.metadata[term][field] = metadata + return + } + + if (!(field in this.metadata[term])) { + this.metadata[term][field] = metadata + return + } + + var metadataKeys = Object.keys(metadata) + + for (var i = 0; i < metadataKeys.length; i++) { + var key = metadataKeys[i] + + if (key in this.metadata[term][field]) { + this.metadata[term][field][key] = this.metadata[term][field][key].concat(metadata[key]) + } else { + this.metadata[term][field][key] = metadata[key] + } + } +} +/** + * A lunr.Query provides a programmatic way of defining queries to be performed + * against a {@link lunr.Index}. + * + * Prefer constructing a lunr.Query using the {@link lunr.Index#query} method + * so the query object is pre-initialized with the right index fields. + * + * @constructor + * @property {lunr.Query~Clause[]} clauses - An array of query clauses. + * @property {string[]} allFields - An array of all available fields in a lunr.Index. + */ +lunr.Query = function (allFields) { + this.clauses = [] + this.allFields = allFields +} + +/** + * Constants for indicating what kind of automatic wildcard insertion will be used when constructing a query clause. + * + * This allows wildcards to be added to the beginning and end of a term without having to manually do any string + * concatenation. + * + * The wildcard constants can be bitwise combined to select both leading and trailing wildcards. + * + * @constant + * @default + * @property {number} wildcard.NONE - The term will have no wildcards inserted, this is the default behaviour + * @property {number} wildcard.LEADING - Prepend the term with a wildcard, unless a leading wildcard already exists + * @property {number} wildcard.TRAILING - Append a wildcard to the term, unless a trailing wildcard already exists + * @see lunr.Query~Clause + * @see lunr.Query#clause + * @see lunr.Query#term + * @example query term with trailing wildcard + * query.term('foo', { wildcard: lunr.Query.wildcard.TRAILING }) + * @example query term with leading and trailing wildcard + * query.term('foo', { + * wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING + * }) + */ + +lunr.Query.wildcard = new String ("*") +lunr.Query.wildcard.NONE = 0 +lunr.Query.wildcard.LEADING = 1 +lunr.Query.wildcard.TRAILING = 2 + +/** + * Constants for indicating what kind of presence a term must have in matching documents. + * + * @constant + * @enum {number} + * @see lunr.Query~Clause + * @see lunr.Query#clause + * @see lunr.Query#term + * @example query term with required presence + * query.term('foo', { presence: lunr.Query.presence.REQUIRED }) + */ +lunr.Query.presence = { + /** + * Term's presence in a document is optional, this is the default value. + */ + OPTIONAL: 1, + + /** + * Term's presence in a document is required, documents that do not contain + * this term will not be returned. + */ + REQUIRED: 2, + + /** + * Term's presence in a document is prohibited, documents that do contain + * this term will not be returned. + */ + PROHIBITED: 3 +} + +/** + * A single clause in a {@link lunr.Query} contains a term and details on how to + * match that term against a {@link lunr.Index}. + * + * @typedef {Object} lunr.Query~Clause + * @property {string[]} fields - The fields in an index this clause should be matched against. + * @property {number} [boost=1] - Any boost that should be applied when matching this clause. + * @property {number} [editDistance] - Whether the term should have fuzzy matching applied, and how fuzzy the match should be. + * @property {boolean} [usePipeline] - Whether the term should be passed through the search pipeline. + * @property {number} [wildcard=lunr.Query.wildcard.NONE] - Whether the term should have wildcards appended or prepended. + * @property {number} [presence=lunr.Query.presence.OPTIONAL] - The terms presence in any matching documents. + */ + +/** + * Adds a {@link lunr.Query~Clause} to this query. + * + * Unless the clause contains the fields to be matched all fields will be matched. In addition + * a default boost of 1 is applied to the clause. + * + * @param {lunr.Query~Clause} clause - The clause to add to this query. + * @see lunr.Query~Clause + * @returns {lunr.Query} + */ +lunr.Query.prototype.clause = function (clause) { + if (!('fields' in clause)) { + clause.fields = this.allFields + } + + if (!('boost' in clause)) { + clause.boost = 1 + } + + if (!('usePipeline' in clause)) { + clause.usePipeline = true + } + + if (!('wildcard' in clause)) { + clause.wildcard = lunr.Query.wildcard.NONE + } + + if ((clause.wildcard & lunr.Query.wildcard.LEADING) && (clause.term.charAt(0) != lunr.Query.wildcard)) { + clause.term = "*" + clause.term + } + + if ((clause.wildcard & lunr.Query.wildcard.TRAILING) && (clause.term.slice(-1) != lunr.Query.wildcard)) { + clause.term = "" + clause.term + "*" + } + + if (!('presence' in clause)) { + clause.presence = lunr.Query.presence.OPTIONAL + } + + this.clauses.push(clause) + + return this +} + +/** + * A negated query is one in which every clause has a presence of + * prohibited. These queries require some special processing to return + * the expected results. + * + * @returns boolean + */ +lunr.Query.prototype.isNegated = function () { + for (var i = 0; i < this.clauses.length; i++) { + if (this.clauses[i].presence != lunr.Query.presence.PROHIBITED) { + return false + } + } + + return true +} + +/** + * Adds a term to the current query, under the covers this will create a {@link lunr.Query~Clause} + * to the list of clauses that make up this query. + * + * The term is used as is, i.e. no tokenization will be performed by this method. Instead conversion + * to a token or token-like string should be done before calling this method. + * + * The term will be converted to a string by calling `toString`. Multiple terms can be passed as an + * array, each term in the array will share the same options. + * + * @param {object|object[]} term - The term(s) to add to the query. + * @param {object} [options] - Any additional properties to add to the query clause. + * @returns {lunr.Query} + * @see lunr.Query#clause + * @see lunr.Query~Clause + * @example adding a single term to a query + * query.term("foo") + * @example adding a single term to a query and specifying search fields, term boost and automatic trailing wildcard + * query.term("foo", { + * fields: ["title"], + * boost: 10, + * wildcard: lunr.Query.wildcard.TRAILING + * }) + * @example using lunr.tokenizer to convert a string to tokens before using them as terms + * query.term(lunr.tokenizer("foo bar")) + */ +lunr.Query.prototype.term = function (term, options) { + if (Array.isArray(term)) { + term.forEach(function (t) { this.term(t, lunr.utils.clone(options)) }, this) + return this + } + + var clause = options || {} + clause.term = term.toString() + + this.clause(clause) + + return this +} +lunr.QueryParseError = function (message, start, end) { + this.name = "QueryParseError" + this.message = message + this.start = start + this.end = end +} + +lunr.QueryParseError.prototype = new Error +lunr.QueryLexer = function (str) { + this.lexemes = [] + this.str = str + this.length = str.length + this.pos = 0 + this.start = 0 + this.escapeCharPositions = [] +} + +lunr.QueryLexer.prototype.run = function () { + var state = lunr.QueryLexer.lexText + + while (state) { + state = state(this) + } +} + +lunr.QueryLexer.prototype.sliceString = function () { + var subSlices = [], + sliceStart = this.start, + sliceEnd = this.pos + + for (var i = 0; i < this.escapeCharPositions.length; i++) { + sliceEnd = this.escapeCharPositions[i] + subSlices.push(this.str.slice(sliceStart, sliceEnd)) + sliceStart = sliceEnd + 1 + } + + subSlices.push(this.str.slice(sliceStart, this.pos)) + this.escapeCharPositions.length = 0 + + return subSlices.join('') +} + +lunr.QueryLexer.prototype.emit = function (type) { + this.lexemes.push({ + type: type, + str: this.sliceString(), + start: this.start, + end: this.pos + }) + + this.start = this.pos +} + +lunr.QueryLexer.prototype.escapeCharacter = function () { + this.escapeCharPositions.push(this.pos - 1) + this.pos += 1 +} + +lunr.QueryLexer.prototype.next = function () { + if (this.pos >= this.length) { + return lunr.QueryLexer.EOS + } + + var char = this.str.charAt(this.pos) + this.pos += 1 + return char +} + +lunr.QueryLexer.prototype.width = function () { + return this.pos - this.start +} + +lunr.QueryLexer.prototype.ignore = function () { + if (this.start == this.pos) { + this.pos += 1 + } + + this.start = this.pos +} + +lunr.QueryLexer.prototype.backup = function () { + this.pos -= 1 +} + +lunr.QueryLexer.prototype.acceptDigitRun = function () { + var char, charCode + + do { + char = this.next() + charCode = char.charCodeAt(0) + } while (charCode > 47 && charCode < 58) + + if (char != lunr.QueryLexer.EOS) { + this.backup() + } +} + +lunr.QueryLexer.prototype.more = function () { + return this.pos < this.length +} + +lunr.QueryLexer.EOS = 'EOS' +lunr.QueryLexer.FIELD = 'FIELD' +lunr.QueryLexer.TERM = 'TERM' +lunr.QueryLexer.EDIT_DISTANCE = 'EDIT_DISTANCE' +lunr.QueryLexer.BOOST = 'BOOST' +lunr.QueryLexer.PRESENCE = 'PRESENCE' + +lunr.QueryLexer.lexField = function (lexer) { + lexer.backup() + lexer.emit(lunr.QueryLexer.FIELD) + lexer.ignore() + return lunr.QueryLexer.lexText +} + +lunr.QueryLexer.lexTerm = function (lexer) { + if (lexer.width() > 1) { + lexer.backup() + lexer.emit(lunr.QueryLexer.TERM) + } + + lexer.ignore() + + if (lexer.more()) { + return lunr.QueryLexer.lexText + } +} + +lunr.QueryLexer.lexEditDistance = function (lexer) { + lexer.ignore() + lexer.acceptDigitRun() + lexer.emit(lunr.QueryLexer.EDIT_DISTANCE) + return lunr.QueryLexer.lexText +} + +lunr.QueryLexer.lexBoost = function (lexer) { + lexer.ignore() + lexer.acceptDigitRun() + lexer.emit(lunr.QueryLexer.BOOST) + return lunr.QueryLexer.lexText +} + +lunr.QueryLexer.lexEOS = function (lexer) { + if (lexer.width() > 0) { + lexer.emit(lunr.QueryLexer.TERM) + } +} + +// This matches the separator used when tokenising fields +// within a document. These should match otherwise it is +// not possible to search for some tokens within a document. +// +// It is possible for the user to change the separator on the +// tokenizer so it _might_ clash with any other of the special +// characters already used within the search string, e.g. :. +// +// This means that it is possible to change the separator in +// such a way that makes some words unsearchable using a search +// string. +lunr.QueryLexer.termSeparator = lunr.tokenizer.separator + +lunr.QueryLexer.lexText = function (lexer) { + while (true) { + var char = lexer.next() + + if (char == lunr.QueryLexer.EOS) { + return lunr.QueryLexer.lexEOS + } + + // Escape character is '\' + if (char.charCodeAt(0) == 92) { + lexer.escapeCharacter() + continue + } + + if (char == ":") { + return lunr.QueryLexer.lexField + } + + if (char == "~") { + lexer.backup() + if (lexer.width() > 0) { + lexer.emit(lunr.QueryLexer.TERM) + } + return lunr.QueryLexer.lexEditDistance + } + + if (char == "^") { + lexer.backup() + if (lexer.width() > 0) { + lexer.emit(lunr.QueryLexer.TERM) + } + return lunr.QueryLexer.lexBoost + } + + // "+" indicates term presence is required + // checking for length to ensure that only + // leading "+" are considered + if (char == "+" && lexer.width() === 1) { + lexer.emit(lunr.QueryLexer.PRESENCE) + return lunr.QueryLexer.lexText + } + + // "-" indicates term presence is prohibited + // checking for length to ensure that only + // leading "-" are considered + if (char == "-" && lexer.width() === 1) { + lexer.emit(lunr.QueryLexer.PRESENCE) + return lunr.QueryLexer.lexText + } + + if (char.match(lunr.QueryLexer.termSeparator)) { + return lunr.QueryLexer.lexTerm + } + } +} + +lunr.QueryParser = function (str, query) { + this.lexer = new lunr.QueryLexer (str) + this.query = query + this.currentClause = {} + this.lexemeIdx = 0 +} + +lunr.QueryParser.prototype.parse = function () { + this.lexer.run() + this.lexemes = this.lexer.lexemes + + var state = lunr.QueryParser.parseClause + + while (state) { + state = state(this) + } + + return this.query +} + +lunr.QueryParser.prototype.peekLexeme = function () { + return this.lexemes[this.lexemeIdx] +} + +lunr.QueryParser.prototype.consumeLexeme = function () { + var lexeme = this.peekLexeme() + this.lexemeIdx += 1 + return lexeme +} + +lunr.QueryParser.prototype.nextClause = function () { + var completedClause = this.currentClause + this.query.clause(completedClause) + this.currentClause = {} +} + +lunr.QueryParser.parseClause = function (parser) { + var lexeme = parser.peekLexeme() + + if (lexeme == undefined) { + return + } + + switch (lexeme.type) { + case lunr.QueryLexer.PRESENCE: + return lunr.QueryParser.parsePresence + case lunr.QueryLexer.FIELD: + return lunr.QueryParser.parseField + case lunr.QueryLexer.TERM: + return lunr.QueryParser.parseTerm + default: + var errorMessage = "expected either a field or a term, found " + lexeme.type + + if (lexeme.str.length >= 1) { + errorMessage += " with value '" + lexeme.str + "'" + } + + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } +} + +lunr.QueryParser.parsePresence = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + switch (lexeme.str) { + case "-": + parser.currentClause.presence = lunr.Query.presence.PROHIBITED + break + case "+": + parser.currentClause.presence = lunr.Query.presence.REQUIRED + break + default: + var errorMessage = "unrecognised presence operator'" + lexeme.str + "'" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + var errorMessage = "expecting term or field, found nothing" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.FIELD: + return lunr.QueryParser.parseField + case lunr.QueryLexer.TERM: + return lunr.QueryParser.parseTerm + default: + var errorMessage = "expecting term or field, found '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseField = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + if (parser.query.allFields.indexOf(lexeme.str) == -1) { + var possibleFields = parser.query.allFields.map(function (f) { return "'" + f + "'" }).join(', '), + errorMessage = "unrecognised field '" + lexeme.str + "', possible fields: " + possibleFields + + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + parser.currentClause.fields = [lexeme.str] + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + var errorMessage = "expecting term, found nothing" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + return lunr.QueryParser.parseTerm + default: + var errorMessage = "expecting term, found '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseTerm = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + parser.currentClause.term = lexeme.str.toLowerCase() + + if (lexeme.str.indexOf("*") != -1) { + parser.currentClause.usePipeline = false + } + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + parser.nextClause() + return + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + parser.nextClause() + return lunr.QueryParser.parseTerm + case lunr.QueryLexer.FIELD: + parser.nextClause() + return lunr.QueryParser.parseField + case lunr.QueryLexer.EDIT_DISTANCE: + return lunr.QueryParser.parseEditDistance + case lunr.QueryLexer.BOOST: + return lunr.QueryParser.parseBoost + case lunr.QueryLexer.PRESENCE: + parser.nextClause() + return lunr.QueryParser.parsePresence + default: + var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseEditDistance = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + var editDistance = parseInt(lexeme.str, 10) + + if (isNaN(editDistance)) { + var errorMessage = "edit distance must be numeric" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + parser.currentClause.editDistance = editDistance + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + parser.nextClause() + return + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + parser.nextClause() + return lunr.QueryParser.parseTerm + case lunr.QueryLexer.FIELD: + parser.nextClause() + return lunr.QueryParser.parseField + case lunr.QueryLexer.EDIT_DISTANCE: + return lunr.QueryParser.parseEditDistance + case lunr.QueryLexer.BOOST: + return lunr.QueryParser.parseBoost + case lunr.QueryLexer.PRESENCE: + parser.nextClause() + return lunr.QueryParser.parsePresence + default: + var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + +lunr.QueryParser.parseBoost = function (parser) { + var lexeme = parser.consumeLexeme() + + if (lexeme == undefined) { + return + } + + var boost = parseInt(lexeme.str, 10) + + if (isNaN(boost)) { + var errorMessage = "boost must be numeric" + throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end) + } + + parser.currentClause.boost = boost + + var nextLexeme = parser.peekLexeme() + + if (nextLexeme == undefined) { + parser.nextClause() + return + } + + switch (nextLexeme.type) { + case lunr.QueryLexer.TERM: + parser.nextClause() + return lunr.QueryParser.parseTerm + case lunr.QueryLexer.FIELD: + parser.nextClause() + return lunr.QueryParser.parseField + case lunr.QueryLexer.EDIT_DISTANCE: + return lunr.QueryParser.parseEditDistance + case lunr.QueryLexer.BOOST: + return lunr.QueryParser.parseBoost + case lunr.QueryLexer.PRESENCE: + parser.nextClause() + return lunr.QueryParser.parsePresence + default: + var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'" + throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end) + } +} + + /** + * export the module via AMD, CommonJS or as a browser global + * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js + */ + ;(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(factory) + } else if (typeof exports === 'object') { + /** + * Node. Does not work with strict CommonJS, but + * only CommonJS-like environments that support module.exports, + * like Node. + */ + module.exports = factory() + } else { + // Browser globals (root is window) + root.lunr = factory() + } + }(this, function () { + /** + * Just return a value to define the module export. + * This example returns an object, but the module + * can return a function as the exported value. + */ + return lunr + })) +})(); diff --git a/search/main.js b/search/main.js new file mode 100644 index 0000000..a5e469d --- /dev/null +++ b/search/main.js @@ -0,0 +1,109 @@ +function getSearchTermFromLocation() { + var sPageURL = window.location.search.substring(1); + var sURLVariables = sPageURL.split('&'); + for (var i = 0; i < sURLVariables.length; i++) { + var sParameterName = sURLVariables[i].split('='); + if (sParameterName[0] == 'q') { + return decodeURIComponent(sParameterName[1].replace(/\+/g, '%20')); + } + } +} + +function joinUrl (base, path) { + if (path.substring(0, 1) === "/") { + // path starts with `/`. Thus it is absolute. + return path; + } + if (base.substring(base.length-1) === "/") { + // base ends with `/` + return base + path; + } + return base + "/" + path; +} + +function escapeHtml (value) { + return value.replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); +} + +function formatResult (location, title, summary) { + return ''; +} + +function displayResults (results) { + var search_results = document.getElementById("mkdocs-search-results"); + while (search_results.firstChild) { + search_results.removeChild(search_results.firstChild); + } + if (results.length > 0){ + for (var i=0; i < results.length; i++){ + var result = results[i]; + var html = formatResult(result.location, result.title, result.summary); + search_results.insertAdjacentHTML('beforeend', html); + } + } else { + var noResultsText = search_results.getAttribute('data-no-results-text'); + if (!noResultsText) { + noResultsText = "No results found"; + } + search_results.insertAdjacentHTML('beforeend', '

' + noResultsText + '

'); + } +} + +function doSearch () { + var query = document.getElementById('mkdocs-search-query').value; + if (query.length > min_search_length) { + if (!window.Worker) { + displayResults(search(query)); + } else { + searchWorker.postMessage({query: query}); + } + } else { + // Clear results for short queries + displayResults([]); + } +} + +function initSearch () { + var search_input = document.getElementById('mkdocs-search-query'); + if (search_input) { + search_input.addEventListener("keyup", doSearch); + } + var term = getSearchTermFromLocation(); + if (term) { + search_input.value = term; + doSearch(); + } +} + +function onWorkerMessage (e) { + if (e.data.allowSearch) { + initSearch(); + } else if (e.data.results) { + var results = e.data.results; + displayResults(results); + } else if (e.data.config) { + min_search_length = e.data.config.min_search_length-1; + } +} + +if (!window.Worker) { + console.log('Web Worker API not supported'); + // load index in main thread + $.getScript(joinUrl(base_url, "search/worker.js")).done(function () { + console.log('Loaded worker'); + init(); + window.postMessage = function (msg) { + onWorkerMessage({data: msg}); + }; + }).fail(function (jqxhr, settings, exception) { + console.error('Could not load worker.js'); + }); +} else { + // Wrap search in a web worker + var searchWorker = new Worker(joinUrl(base_url, "search/worker.js")); + searchWorker.postMessage({init: true}); + searchWorker.onmessage = onWorkerMessage; +} diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 0000000..2bb6497 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"index.html","text":"Introductions # The unofficial FIRST Robotics Competition Java Programming Tutorial. Info Updated for the 2021 Season Last updated: 9/30/21 Disclaimer: Some screenshots may have different colors, icons, more/less folders/files than you due to themes or personal settings. This is normal and should not impact the tutorial. If you still have any questions please contact us. Powered by # Contributors # Name Team Team Role Tayler Uva 3255 Coach Isaac Sayasane 3255 Alumni Sharon Riggs 6995 Mentor","title":"Introductions"},{"location":"index.html#introductions","text":"The unofficial FIRST Robotics Competition Java Programming Tutorial. Info Updated for the 2021 Season Last updated: 9/30/21 Disclaimer: Some screenshots may have different colors, icons, more/less folders/files than you due to themes or personal settings. This is normal and should not impact the tutorial. If you still have any questions please contact us.","title":"Introductions"},{"location":"index.html#powered-by","text":"","title":"Powered by"},{"location":"index.html#contributors","text":"Name Team Team Role Tayler Uva 3255 Coach Isaac Sayasane 3255 Alumni Sharon Riggs 6995 Mentor","title":"Contributors"},{"location":"contributing.html","text":"Contributing # Helping out with the project! Example Project Code # If you make Example Project Code changes please contribute changes that reflect this in the Documentation. This will make it easier for us and more likely that your contribution will be approved. Documentation # If you make documentation changes please contribute changes that reflect this in the Example Project Code. This will make it easier for us and more likely that your contribution will be approved. There are a couple of ways to contribute to this project: Via the web Via local source Via the web # Editing Pages # On each page there is an option to edit the page. Any changes you make through this option will be submitted and become live once they are approved. The edit icon looks like this: Alternatively you could create a pull request and clone the repository New Pages # You can help the project by making new pages. Any pages you make will become live once they are approved. Click here to create a new page Please use the New Page Template Click here to see tips on creating markdown documents Warning Make sure all documentation files end in .md Tip You can add to a certain tab by appending /tab_name/ to the file name Tip Visit Admonitions (call-out) references for a list off call-outs like this one. Via local source # Prerequisites # Install GitHub Desktop (Beginner) or Install Git (Expert) Install Python Install pip requirements Run one of the following commands. Try each one in order until successful. pip install -r requirements.txt python -m pip install -r requirements.txt py -m pip install -r requirements.txt Creating local edits # Visit https://github.com/FRCTeam3255/FRC-Java-Tutorial/tree/main/ and fork the repository. Clone your the newly created fork to your machine and open it Run the command mkdocs serve to open up a live local version of the project in your browser If mkdocs serve does not work on its own, try each one in order until successful: python -m mkdocs serve py -m mkdocs serve Make your changes or additions in the docs directory. Please maintain the organizational folder structure. If added a new page, add the relative url to the mkdocs.yml file in the # Navigation ( nav: ) section. For new pages please use the New Page Template Click here to see tips on creating markdown documents Pushing your local edits to the web # Commit your changes Push your changes to GitHub Back on the webpage for your fork of the project select Pull Request Create a new pull request Wait for the pull request to be approved. New Page Template # Please copy this code as a template to create your new page # Page title Subtitle ![Image Title](imageURL) ## Overview This section will help you learn to BLANK. **See table of contents for a breakdown of this section.** *** ## Section One - Some info - Some other into - Some sub info ### Section One Subsection *** ## Section Two - Info - Info 2 !!! Tip This is a tip.","title":"Contributing"},{"location":"contributing.html#contributing","text":"Helping out with the project!","title":"Contributing"},{"location":"contributing.html#example-project-code","text":"If you make Example Project Code changes please contribute changes that reflect this in the Documentation. This will make it easier for us and more likely that your contribution will be approved.","title":"Example Project Code"},{"location":"contributing.html#documentation","text":"If you make documentation changes please contribute changes that reflect this in the Example Project Code. This will make it easier for us and more likely that your contribution will be approved. There are a couple of ways to contribute to this project: Via the web Via local source","title":"Documentation"},{"location":"contributing.html#via-the-web","text":"","title":"Via the web"},{"location":"contributing.html#editing-pages","text":"On each page there is an option to edit the page. Any changes you make through this option will be submitted and become live once they are approved. The edit icon looks like this: Alternatively you could create a pull request and clone the repository","title":"Editing Pages"},{"location":"contributing.html#new-pages","text":"You can help the project by making new pages. Any pages you make will become live once they are approved. Click here to create a new page Please use the New Page Template Click here to see tips on creating markdown documents Warning Make sure all documentation files end in .md Tip You can add to a certain tab by appending /tab_name/ to the file name Tip Visit Admonitions (call-out) references for a list off call-outs like this one.","title":"New Pages"},{"location":"contributing.html#via-local-source","text":"","title":"Via local source"},{"location":"contributing.html#prerequisites","text":"Install GitHub Desktop (Beginner) or Install Git (Expert) Install Python Install pip requirements Run one of the following commands. Try each one in order until successful. pip install -r requirements.txt python -m pip install -r requirements.txt py -m pip install -r requirements.txt","title":"Prerequisites"},{"location":"contributing.html#creating-local-edits","text":"Visit https://github.com/FRCTeam3255/FRC-Java-Tutorial/tree/main/ and fork the repository. Clone your the newly created fork to your machine and open it Run the command mkdocs serve to open up a live local version of the project in your browser If mkdocs serve does not work on its own, try each one in order until successful: python -m mkdocs serve py -m mkdocs serve Make your changes or additions in the docs directory. Please maintain the organizational folder structure. If added a new page, add the relative url to the mkdocs.yml file in the # Navigation ( nav: ) section. For new pages please use the New Page Template Click here to see tips on creating markdown documents","title":"Creating local edits"},{"location":"contributing.html#pushing-your-local-edits-to-the-web","text":"Commit your changes Push your changes to GitHub Back on the webpage for your fork of the project select Pull Request Create a new pull request Wait for the pull request to be approved.","title":"Pushing your local edits to the web"},{"location":"contributing.html#new-page-template","text":"Please copy this code as a template to create your new page # Page title Subtitle ![Image Title](imageURL) ## Overview This section will help you learn to BLANK. **See table of contents for a breakdown of this section.** *** ## Section One - Some info - Some other into - Some sub info ### Section One Subsection *** ## Section Two - Info - Info 2 !!! Tip This is a tip.","title":"New Page Template"},{"location":"why_software.html","text":"What is software # What is software? Why should you care? Where is software # Everywhere! Games, apps, websites All smart devices It\u2019s the stuff on your phone and computer that you see and interact with It is also handling complex parts behind what you see and interact with Even things that didn't before have software now Cars, Planes, Ovens, Light Bulbs, Garbage Trucks, Children's Toys, etc. But what is it # It\u2019s the language of computers! Logical building blocks for controlling the devices around us Software makes devices smart Software allows us to automate things Why should you do software # Learn Creative Problem Solving Peek be hind the curtain of what makes technology tick Better understanding and appreciation of technology Programming is the FUTURE Your imagination is your only limitation Low barrier to entry Only need a computing device (can be a cheap $35 computer or even your phone) Lucrative job opportunities Many job opportunities from Game Development to App Development to Robotics","title":"Why software"},{"location":"why_software.html#what-is-software","text":"What is software? Why should you care?","title":"What is software"},{"location":"why_software.html#where-is-software","text":"Everywhere! Games, apps, websites All smart devices It\u2019s the stuff on your phone and computer that you see and interact with It is also handling complex parts behind what you see and interact with Even things that didn't before have software now Cars, Planes, Ovens, Light Bulbs, Garbage Trucks, Children's Toys, etc.","title":"Where is software"},{"location":"why_software.html#but-what-is-it","text":"It\u2019s the language of computers! Logical building blocks for controlling the devices around us Software makes devices smart Software allows us to automate things","title":"But what is it"},{"location":"why_software.html#why-should-you-do-software","text":"Learn Creative Problem Solving Peek be hind the curtain of what makes technology tick Better understanding and appreciation of technology Programming is the FUTURE Your imagination is your only limitation Low barrier to entry Only need a computing device (can be a cheap $35 computer or even your phone) Lucrative job opportunities Many job opportunities from Game Development to App Development to Robotics","title":"Why should you do software"},{"location":"basics/driverstation_tips.html","text":"","title":"Driverstation tips"},{"location":"basics/java_basics.html","text":"Java Programming Basics # Learning What's What Overview # Objects, variables, and classes (in Java) make up our programs. We define, modify, and use these variables and objects to make our programs run. Programs use key words to define characteristics of variables or objects. Basic keywords: public - an object accessible by other classes (files) private - an object only accessible by its containing class (file). protected - like private but can be seen by subclasses return - value to return or give back after method execution (run). void - a method that returns no value null - a value that means empty or nothing IMPORTANT NOTE Java is case sensitive, meaning capitalization matters! Classes # Classes are the files that contain our programming A program can be made up of one class but can also be made up of many classes All programs run a main class that can optionally load additional classes either directly or indirectly Example main loads class1, class1 loads class2 Classes are made up of variables and methods and are often used to separate and organize your code. Classes can also call (use) variables or methods of other classes if those have been set to public. Constructors # Classes can also have a constructor which is a special type of method that has the same name (case sensitive) as the class file Constructors are always called when the class is loaded into the program for the first time. This is often the only time they are called. Constructors are called when trying to access the class in other files. They can be called again if the class is programmed to be unloaded (destroyed) and reloaded. Calls to methods, and assignment of values, within the constructor will run as soon as the class is called (loaded) in the code. The new operator creates an object of a type of class using a constructor Example classObject = new className(); Methods # Methods, also known as functions, can be thought of as subprograms or routines that run inside of your main program. Methods are used when you want to run the same code multiple times. Copying and pasting code is BAD! Use methods instead! Methods are also useful to access only certain parts or functions of another class. Methods can also have their own variables ( local ) or use variables available throughout the whole class ( global variables ), this will be explained more in the scope section . Methods can call (use) other methods, even multiple times. Example int value; void increment(){ value++; } Parameters # Parameters are variables that are passed (sent) to a method for it to use. You can pass more than one parameter but order matters when calling the method. Example // Example of a method with a parameter double half(int num1){ double multiplier = 0.5; return num1*multiplier; } int newNumber = half(12); // <---- Method being called (used) in code Variables # Variables are objects that contain data, they are characterized by data types Variables are assigned names and data types on creation Names can be anything with the exception of pre-existing keywords such as public or int Data types define what type of data is being stored in the variables: int - integers (whole numbers) double - double precision floating point (fractional/decimal values) boolean - true or false (true = 1 or false = 0) values. string - text values contained in parentheses Example: int sum; A variable that can hold whole number values Example: boolean isFull = true; A variable can either hold a true or false value and is being assigned a true value Constants # Most variables can have their values assigned or reassigned at any point elsewhere in your program. To avoid having a variable change its value during runtime you can make it a constant In Java you can create constants using the static final keywords together in front of the data type of the variable The static modifier causes the variable to be available without loading the class where it is defined. The final modifier causes the variable to be unchangeable. Java constants are normally declared in ALL CAPS. Words in Java constants are normally separated by underscores. Example: public static final double PI_VALUE = 3.14159; A variable that cannot be modified during code run time. Scope # When creating a variable, where you create it matters. This is known as the scope of a variable. The scope is where a variable can be seen within a class A variable created in a method can only be seen in that method. This is a local variable. A variable created outside a method can be seen in all methods of that class (file). This is a global variable. It is good practice to put them all at the top before your first method. Example of a Local Variable public int testMethod() { int example = 12; // Inside of method example = example + 1; return example } Example of a Global Variable int example = 12; // Outside of method public void testMethod() { example = example + 1; return example } Comments # Comments are a programmer-readable explanation or annotation in the source code of a program. Comments do not affect what the code does. Comments are often used to leave notes or explanations of what methods or classes are doing so that it is easier to understand the code. Example: Single Line Comments // This is what a single line comment looks like // You can also have multiple // single line comments in a row Example: Multi Line Comments /* This is what a multiline comment looks like */ Example: Doc Comments /** * This is a doc comment * *
    *
  • They can be viewed by hovering over code they are attached to
  • *
  • They can be formatted with HTML
  • *
*/ Conventions # There are also many different conventions when programming, this ensures that programs are readable between different people. A common naming convention: Programming is often done in CamelCase or lowerCamelCase Instead of adding spaces, capitalize the first letter of each word Example ThreeMotorDrive, driveForward, setSpeed Info There are other naming conventions, but for this tutorial we will use the camel cases","title":"Java Programming Basics"},{"location":"basics/java_basics.html#java-programming-basics","text":"Learning What's What","title":"Java Programming Basics"},{"location":"basics/java_basics.html#overview","text":"Objects, variables, and classes (in Java) make up our programs. We define, modify, and use these variables and objects to make our programs run. Programs use key words to define characteristics of variables or objects. Basic keywords: public - an object accessible by other classes (files) private - an object only accessible by its containing class (file). protected - like private but can be seen by subclasses return - value to return or give back after method execution (run). void - a method that returns no value null - a value that means empty or nothing IMPORTANT NOTE Java is case sensitive, meaning capitalization matters!","title":"Overview"},{"location":"basics/java_basics.html#classes","text":"Classes are the files that contain our programming A program can be made up of one class but can also be made up of many classes All programs run a main class that can optionally load additional classes either directly or indirectly Example main loads class1, class1 loads class2 Classes are made up of variables and methods and are often used to separate and organize your code. Classes can also call (use) variables or methods of other classes if those have been set to public.","title":"Classes"},{"location":"basics/java_basics.html#constructors","text":"Classes can also have a constructor which is a special type of method that has the same name (case sensitive) as the class file Constructors are always called when the class is loaded into the program for the first time. This is often the only time they are called. Constructors are called when trying to access the class in other files. They can be called again if the class is programmed to be unloaded (destroyed) and reloaded. Calls to methods, and assignment of values, within the constructor will run as soon as the class is called (loaded) in the code. The new operator creates an object of a type of class using a constructor Example classObject = new className();","title":"Constructors"},{"location":"basics/java_basics.html#methods","text":"Methods, also known as functions, can be thought of as subprograms or routines that run inside of your main program. Methods are used when you want to run the same code multiple times. Copying and pasting code is BAD! Use methods instead! Methods are also useful to access only certain parts or functions of another class. Methods can also have their own variables ( local ) or use variables available throughout the whole class ( global variables ), this will be explained more in the scope section . Methods can call (use) other methods, even multiple times. Example int value; void increment(){ value++; }","title":"Methods"},{"location":"basics/java_basics.html#parameters","text":"Parameters are variables that are passed (sent) to a method for it to use. You can pass more than one parameter but order matters when calling the method. Example // Example of a method with a parameter double half(int num1){ double multiplier = 0.5; return num1*multiplier; } int newNumber = half(12); // <---- Method being called (used) in code","title":"Parameters"},{"location":"basics/java_basics.html#variables","text":"Variables are objects that contain data, they are characterized by data types Variables are assigned names and data types on creation Names can be anything with the exception of pre-existing keywords such as public or int Data types define what type of data is being stored in the variables: int - integers (whole numbers) double - double precision floating point (fractional/decimal values) boolean - true or false (true = 1 or false = 0) values. string - text values contained in parentheses Example: int sum; A variable that can hold whole number values Example: boolean isFull = true; A variable can either hold a true or false value and is being assigned a true value","title":"Variables"},{"location":"basics/java_basics.html#constants","text":"Most variables can have their values assigned or reassigned at any point elsewhere in your program. To avoid having a variable change its value during runtime you can make it a constant In Java you can create constants using the static final keywords together in front of the data type of the variable The static modifier causes the variable to be available without loading the class where it is defined. The final modifier causes the variable to be unchangeable. Java constants are normally declared in ALL CAPS. Words in Java constants are normally separated by underscores. Example: public static final double PI_VALUE = 3.14159; A variable that cannot be modified during code run time.","title":"Constants"},{"location":"basics/java_basics.html#scope","text":"When creating a variable, where you create it matters. This is known as the scope of a variable. The scope is where a variable can be seen within a class A variable created in a method can only be seen in that method. This is a local variable. A variable created outside a method can be seen in all methods of that class (file). This is a global variable. It is good practice to put them all at the top before your first method. Example of a Local Variable public int testMethod() { int example = 12; // Inside of method example = example + 1; return example } Example of a Global Variable int example = 12; // Outside of method public void testMethod() { example = example + 1; return example }","title":"Scope"},{"location":"basics/java_basics.html#comments","text":"Comments are a programmer-readable explanation or annotation in the source code of a program. Comments do not affect what the code does. Comments are often used to leave notes or explanations of what methods or classes are doing so that it is easier to understand the code. Example: Single Line Comments // This is what a single line comment looks like // You can also have multiple // single line comments in a row Example: Multi Line Comments /* This is what a multiline comment looks like */ Example: Doc Comments /** * This is a doc comment * *
    *
  • They can be viewed by hovering over code they are attached to
  • *
  • They can be formatted with HTML
  • *
*/","title":"Comments"},{"location":"basics/java_basics.html#conventions","text":"There are also many different conventions when programming, this ensures that programs are readable between different people. A common naming convention: Programming is often done in CamelCase or lowerCamelCase Instead of adding spaces, capitalize the first letter of each word Example ThreeMotorDrive, driveForward, setSpeed Info There are other naming conventions, but for this tutorial we will use the camel cases","title":"Conventions"},{"location":"basics/roboRIO.html","text":"roboRIO # The Brains of the Bot! The roboRIO Basics # The roboRIO is the brain of an FRC robot. It is the main processing unit and is where the code is stored and run. It is very similar to something like a Raspberry Pi, it\u2019s a mini computer! The roboRIO can connect to many different devices such as motor controllers, servos, and sensors through its various interface connections such as: Digital I/O, PWM, CAN Bus, Ethernet, USB, MXP The roboRIO IO # Digital IO (DIO) used for sensors and switches PWM used for motor controllers and servos CAN used for motor controllers and sensors MXP used for functionality expansion Check the roboRIO user manual for more details","title":"roboRIO"},{"location":"basics/roboRIO.html#roborio","text":"The Brains of the Bot!","title":"roboRIO"},{"location":"basics/roboRIO.html#the-roborio-basics","text":"The roboRIO is the brain of an FRC robot. It is the main processing unit and is where the code is stored and run. It is very similar to something like a Raspberry Pi, it\u2019s a mini computer! The roboRIO can connect to many different devices such as motor controllers, servos, and sensors through its various interface connections such as: Digital I/O, PWM, CAN Bus, Ethernet, USB, MXP","title":"The roboRIO Basics"},{"location":"basics/roboRIO.html#the-roborio-io","text":"Digital IO (DIO) used for sensors and switches PWM used for motor controllers and servos CAN used for motor controllers and sensors MXP used for functionality expansion Check the roboRIO user manual for more details","title":"The roboRIO IO"},{"location":"basics/sensors.html","text":"Sensors # How does the robot see? Some types of sensors # Limit Switches - detects contact Camera - provides sight Encoders - measures rotational or linear motion Ultrasonic - measures distances Gyroscope - measures orientation Processed Vision - measures target's distance, angle, and offset from robot For more info on sensors see: High Tech High Top Hat Technicians - Electrical Tutorial Limit Switch Grayhill brand Quadrature Encoder Kauai Labs navX Gyro/ Accelerometer","title":"Sensors"},{"location":"basics/sensors.html#sensors","text":"How does the robot see?","title":"Sensors"},{"location":"basics/sensors.html#some-types-of-sensors","text":"Limit Switches - detects contact Camera - provides sight Encoders - measures rotational or linear motion Ultrasonic - measures distances Gyroscope - measures orientation Processed Vision - measures target's distance, angle, and offset from robot For more info on sensors see: High Tech High Top Hat Technicians - Electrical Tutorial Limit Switch Grayhill brand Quadrature Encoder Kauai Labs navX Gyro/ Accelerometer","title":"Some types of sensors"},{"location":"basics/vscode_tips.html","text":"Visual Studio Code Tips # Making life easier \ud83d\udca1 Using the light bulb (quick fixes) # When auto complete is available, click enter on the correct completion to auto import classes. If this is not done an error can occur denoted by an underline on what you just typed. To fix this, click on the error and click the light bulb that pops up. Then click import. From this point on this tutorial will assume you are doing this on your own so if errors occur, click the light bulb and see if it suggest importing something. The light bulb can also be used to make programming easier by auto creating things for us. This tutorial will go over that in future sections","title":"Visual Studio Code Tips"},{"location":"basics/vscode_tips.html#visual-studio-code-tips","text":"Making life easier","title":"Visual Studio Code Tips"},{"location":"basics/vscode_tips.html#using-the-light-bulb-quick-fixes","text":"When auto complete is available, click enter on the correct completion to auto import classes. If this is not done an error can occur denoted by an underline on what you just typed. To fix this, click on the error and click the light bulb that pops up. Then click import. From this point on this tutorial will assume you are doing this on your own so if errors occur, click the light bulb and see if it suggest importing something. The light bulb can also be used to make programming easier by auto creating things for us. This tutorial will go over that in future sections","title":"\ud83d\udca1 Using the light bulb (quick fixes)"},{"location":"basics/wpilib.html","text":"WPILib Programming Basics # Making FRC Programming Easy What is WPILib # The WPI Robotics library (WPILib) is a set of software classes that interfaces with the hardware and software in your FRC RoboRIO. There are classes to handle sensors, motor speed controllers, the driver station, and a number of other utility functions. Documentation is available at http://first.wpi.edu/FRC/roborio/release/docs/java WPILib adds those sensors and controllers as additional data types (like int or double ) and classes. Examples Talon , Solenoid , Encoder ... Command Based Robot # For our programming tutorial we will be creating a Command based robot Command Based Robots are much like Lego, with very basic pieces you can make something simple like a small house or complicated like an entire Lego city. A command based robot is broken down into subsystem classes and command classes. In the code, a command based robot is made up of 3 packages ( folders ) labeled robot, commands, and subsystems There are other types of robots but we will use Command Based Subsystems # A subsystem is a special template class made by FRC. In robotics, subsystems are sections of the whole robot. For example every FRC robot has a Drivetrain subsystem which is what controls the robot\u2019s driving both physically and programmatically. To avoid confusion between software and mechanical teams, subsystems should be called the same thing. If we have a ball intake system, we will both call it Intake or Collector . Subsystems of a robot can contain parts to control or read data from. The Drivetrain subsystem could contain motor controllers and encoders both physically and programmatically. Using a dog as an example: the legs , tail , and head are subsystems . The head subsystem has the parts: eyes , ears , and nose . When programming subsystems we use variables and methods to tell our subsystem what it has and what it is capable of or should do. These variables will be the parts in the subsystem These methods will define what those parts are capable of. Using a dog head subsystem as an example: Some variables (parts) would be: leftEye , rightEye , nose , leftEar , rightEar . Some example methods would be closeEyes or openEyes since these are things the dog are capable of. These methods would use both the leftEye and rightEye and close them. Example //This method closes the dog eyes public void closeEyes(){ leftEye.close(); rightEye.close(); A robot example of a Drivetrain subsystem would have leftMotor , and rightMotor as variables and setSpeed as a method telling it how to set the speed of those motor controllers. Having the setSpeed method tells our program that our Drivetrain subsystem can set its speed. Example //This method sets the speed of the drivetrain public void setSpeed(double speed){ leftMotor.set(speed); rightMotor.set(speed); } Commands # A command is a special template class (file) made by FRC. In robotics, commands are actions you want a robot to do (just like a real life command). A command is an action a subsystem(s) performs. For example you may want your robot to drive full speed forward so you make a command class called DriveForward . Since a robot uses a Drivetrain subsystem to control its motors, this command would call our previously created setSpeed method from that subsystem. Tip Subsystems define what the robot is made of and what it can do while commands actually tell the robot to do those things Using a dog as an example we can tell the dog to blink by creating a BlinkEyes command The command would call the method, closeEyes() then the method openEyes() BlinkEyes Command //This command will continuously run the two methods in execute protected void execute() { dog.head.closeEyes(); dog.head.openEyes(); } A robot example of a DriveForward command would call (use) the setSpeed methods that we created in the Drivetrain subsystem DriveForward , when executed, will tell our robot to drive forward using the Drivetrain subsystem DriveForward Command //This command tells the robot to drive forward full speed protected void initialize(){ robot.drivetrain.setSpeed(1.0); } Default Command Structure # The template for FRC commands actually come with some pre-defined methods that have special properties for FRC robots, they are: void initialize() - Methods in here are called just before this Command runs the first time. void execute() - Methods in here are called repeatedly when this Command is scheduled to run boolean isFinished() - When this returns true, the Command stops running execute() void end() - Methods in here are called once after isFinished returns true void interrupted() - Methods in here are called when another command which requires one or more of the same subsystems is scheduled to run Tip It is good practice to call end() in interrupted() Overview of execution # In FRC programming our main class is Robot.java and all other classes (command files and subsystem files) must be loaded from Robot.java either directly or indirectly Example Robot.java loads RobotContainer.java , RobotContainer.java loads DriveForward.java . All subsystem files must be added to RobotContainer.java . This loads our subsystems into the code and allow its public methods to be useable by other files such as commands later by typing RobotContainer.nameOfSubsystem.desiredMethod(); New Project Files # See Default Project Contents Summary # Command based robots are broken down into subsystems and commands Subsystems define what the robot is made of and what it can do while commands actually tell the robot to do those things All classes must directly or indirectly connect to Robot.java . All Subsystems must be added to RobotContainer.java RobotMap.java holds port numbers and IDs accessible throughout the program by typing: RobotMap.NameOfMotor() RobotContainer.java contains our publicly accessible instances of our subsystems. It also connects our commands to physical controllers.","title":"WPILib Programming Basics"},{"location":"basics/wpilib.html#wpilib-programming-basics","text":"Making FRC Programming Easy","title":"WPILib Programming Basics"},{"location":"basics/wpilib.html#what-is-wpilib","text":"The WPI Robotics library (WPILib) is a set of software classes that interfaces with the hardware and software in your FRC RoboRIO. There are classes to handle sensors, motor speed controllers, the driver station, and a number of other utility functions. Documentation is available at http://first.wpi.edu/FRC/roborio/release/docs/java WPILib adds those sensors and controllers as additional data types (like int or double ) and classes. Examples Talon , Solenoid , Encoder ...","title":"What is WPILib"},{"location":"basics/wpilib.html#command-based-robot","text":"For our programming tutorial we will be creating a Command based robot Command Based Robots are much like Lego, with very basic pieces you can make something simple like a small house or complicated like an entire Lego city. A command based robot is broken down into subsystem classes and command classes. In the code, a command based robot is made up of 3 packages ( folders ) labeled robot, commands, and subsystems There are other types of robots but we will use Command Based","title":"Command Based Robot"},{"location":"basics/wpilib.html#subsystems","text":"A subsystem is a special template class made by FRC. In robotics, subsystems are sections of the whole robot. For example every FRC robot has a Drivetrain subsystem which is what controls the robot\u2019s driving both physically and programmatically. To avoid confusion between software and mechanical teams, subsystems should be called the same thing. If we have a ball intake system, we will both call it Intake or Collector . Subsystems of a robot can contain parts to control or read data from. The Drivetrain subsystem could contain motor controllers and encoders both physically and programmatically. Using a dog as an example: the legs , tail , and head are subsystems . The head subsystem has the parts: eyes , ears , and nose . When programming subsystems we use variables and methods to tell our subsystem what it has and what it is capable of or should do. These variables will be the parts in the subsystem These methods will define what those parts are capable of. Using a dog head subsystem as an example: Some variables (parts) would be: leftEye , rightEye , nose , leftEar , rightEar . Some example methods would be closeEyes or openEyes since these are things the dog are capable of. These methods would use both the leftEye and rightEye and close them. Example //This method closes the dog eyes public void closeEyes(){ leftEye.close(); rightEye.close(); A robot example of a Drivetrain subsystem would have leftMotor , and rightMotor as variables and setSpeed as a method telling it how to set the speed of those motor controllers. Having the setSpeed method tells our program that our Drivetrain subsystem can set its speed. Example //This method sets the speed of the drivetrain public void setSpeed(double speed){ leftMotor.set(speed); rightMotor.set(speed); }","title":"Subsystems"},{"location":"basics/wpilib.html#commands","text":"A command is a special template class (file) made by FRC. In robotics, commands are actions you want a robot to do (just like a real life command). A command is an action a subsystem(s) performs. For example you may want your robot to drive full speed forward so you make a command class called DriveForward . Since a robot uses a Drivetrain subsystem to control its motors, this command would call our previously created setSpeed method from that subsystem. Tip Subsystems define what the robot is made of and what it can do while commands actually tell the robot to do those things Using a dog as an example we can tell the dog to blink by creating a BlinkEyes command The command would call the method, closeEyes() then the method openEyes() BlinkEyes Command //This command will continuously run the two methods in execute protected void execute() { dog.head.closeEyes(); dog.head.openEyes(); } A robot example of a DriveForward command would call (use) the setSpeed methods that we created in the Drivetrain subsystem DriveForward , when executed, will tell our robot to drive forward using the Drivetrain subsystem DriveForward Command //This command tells the robot to drive forward full speed protected void initialize(){ robot.drivetrain.setSpeed(1.0); }","title":"Commands"},{"location":"basics/wpilib.html#default-command-structure","text":"The template for FRC commands actually come with some pre-defined methods that have special properties for FRC robots, they are: void initialize() - Methods in here are called just before this Command runs the first time. void execute() - Methods in here are called repeatedly when this Command is scheduled to run boolean isFinished() - When this returns true, the Command stops running execute() void end() - Methods in here are called once after isFinished returns true void interrupted() - Methods in here are called when another command which requires one or more of the same subsystems is scheduled to run Tip It is good practice to call end() in interrupted()","title":"Default Command Structure"},{"location":"basics/wpilib.html#overview-of-execution","text":"In FRC programming our main class is Robot.java and all other classes (command files and subsystem files) must be loaded from Robot.java either directly or indirectly Example Robot.java loads RobotContainer.java , RobotContainer.java loads DriveForward.java . All subsystem files must be added to RobotContainer.java . This loads our subsystems into the code and allow its public methods to be useable by other files such as commands later by typing RobotContainer.nameOfSubsystem.desiredMethod();","title":"Overview of execution"},{"location":"basics/wpilib.html#new-project-files","text":"See Default Project Contents","title":"New Project Files"},{"location":"basics/wpilib.html#summary","text":"Command based robots are broken down into subsystems and commands Subsystems define what the robot is made of and what it can do while commands actually tell the robot to do those things All classes must directly or indirectly connect to Robot.java . All Subsystems must be added to RobotContainer.java RobotMap.java holds port numbers and IDs accessible throughout the program by typing: RobotMap.NameOfMotor() RobotContainer.java contains our publicly accessible instances of our subsystems. It also connects our commands to physical controllers.","title":"Summary"},{"location":"examples/basic_elevator.html","text":"[WIP] Basic Elevator Subsystem # Subtitle Overview # This section will help you learn to create a basic elevator or lift subsystem. This subsystem will contain: Two motors in a single gear box Use single encoder to lift to specific distances 3 distances 2, 5, 10 inches 3 buttons to get to those distances Hard stop safeties using limit switches Top and bottom Run motors at 25% speed so we can watch easier See table of contents for a breakdown of this section. Section One # Some info Some other into Some sub info Section One Subsection # Section Two # Info Info 2 Tip This is a tip.","title":"[WIP] Basic Elevator Subsystem"},{"location":"examples/basic_elevator.html#wip-basic-elevator-subsystem","text":"Subtitle","title":"[WIP] Basic Elevator Subsystem"},{"location":"examples/basic_elevator.html#overview","text":"This section will help you learn to create a basic elevator or lift subsystem. This subsystem will contain: Two motors in a single gear box Use single encoder to lift to specific distances 3 distances 2, 5, 10 inches 3 buttons to get to those distances Hard stop safeties using limit switches Top and bottom Run motors at 25% speed so we can watch easier See table of contents for a breakdown of this section.","title":"Overview"},{"location":"examples/basic_elevator.html#section-one","text":"Some info Some other into Some sub info","title":"Section One"},{"location":"examples/basic_elevator.html#section-one-subsection","text":"","title":"Section One Subsection"},{"location":"examples/basic_elevator.html#section-two","text":"Info Info 2 Tip This is a tip.","title":"Section Two"},{"location":"examples/basic_shooter.html","text":"[WIP] Basic Shooting Subsystem # Subtitle Overview # This section will help you learn to BLANK. See table of contents for a breakdown of this section. Section One # Some info Some other into Some sub info Section One Subsection # Section Two # Info Info 2 Tip This is a tip.","title":"[WIP] Basic Shooting Subsystem"},{"location":"examples/basic_shooter.html#wip-basic-shooting-subsystem","text":"Subtitle","title":"[WIP] Basic Shooting Subsystem"},{"location":"examples/basic_shooter.html#overview","text":"This section will help you learn to BLANK. See table of contents for a breakdown of this section.","title":"Overview"},{"location":"examples/basic_shooter.html#section-one","text":"Some info Some other into Some sub info","title":"Section One"},{"location":"examples/basic_shooter.html#section-one-subsection","text":"","title":"Section One Subsection"},{"location":"examples/basic_shooter.html#section-two","text":"Info Info 2 Tip This is a tip.","title":"Section Two"},{"location":"examples/pid_elevator.html","text":"[WIP] PID Driven Elevator # Subtitle Overview # This section will help you learn to BLANK. See table of contents for a breakdown of this section. Section One # Some info Some other into Some sub info Section One Subsection # Section Two # Info Info 2 Tip This is a tip.","title":"[WIP] PID Driven Elevator"},{"location":"examples/pid_elevator.html#wip-pid-driven-elevator","text":"Subtitle","title":"[WIP] PID Driven Elevator"},{"location":"examples/pid_elevator.html#overview","text":"This section will help you learn to BLANK. See table of contents for a breakdown of this section.","title":"Overview"},{"location":"examples/pid_elevator.html#section-one","text":"Some info Some other into Some sub info","title":"Section One"},{"location":"examples/pid_elevator.html#section-one-subsection","text":"","title":"Section One Subsection"},{"location":"examples/pid_elevator.html#section-two","text":"Info Info 2 Tip This is a tip.","title":"Section Two"},{"location":"examples/pid_shooter.html","text":"[WIP] PID Driven Shooter # Subtitle Overview # This section will help you learn to BLANK. See table of contents for a breakdown of this section. Section One # Some info Some other into Some sub info Section One Subsection # Section Two # Info Info 2 Tip This is a tip.","title":"[WIP] PID Driven Shooter"},{"location":"examples/pid_shooter.html#wip-pid-driven-shooter","text":"Subtitle","title":"[WIP] PID Driven Shooter"},{"location":"examples/pid_shooter.html#overview","text":"This section will help you learn to BLANK. See table of contents for a breakdown of this section.","title":"Overview"},{"location":"examples/pid_shooter.html#section-one","text":"Some info Some other into Some sub info","title":"Section One"},{"location":"examples/pid_shooter.html#section-one-subsection","text":"","title":"Section One Subsection"},{"location":"examples/pid_shooter.html#section-two","text":"Info Info 2 Tip This is a tip.","title":"Section Two"},{"location":"programming/autonomous.html","text":"[WIP] Creating an Autonomous Command # Overview # In this section we will be going over Creating an autonomous command group Using RobotPreferences to quickly change our autonomous values Using an encoder to autonomously drive Creating a delay timer to pace our commands in autonomous What Is an Autonomous Command # An autonomous command is a command that is ran during \"autonomous mode\" under the autonomousInit method in Robot.java It could be a single command or a command group It's especially helpful to have if you don't have any cameras to drive the robot during a \"sandstorm\" period (2019 game mechanic where the drivers couldn't see during the pre tele-op phase) For this tutorial we will create an autonomous command group that makes the robot drive forward 5 feet, wait 5 seconds, and then pitch the shooter up during autonomous Creating Commands For Autonomous # Since we can't control our robot during an autonomous command we will want to create commands that allow the robot to move independently of a driver Creating the DriveDistance Command # 1) Create a new command called DriveDistance 2) Before the constructor create a double called distance We will use this to tell the command to finish when the robot drives the inputted distance 3) In the DriveDistance constructor add a double parameter called inches 4) Inside type: distance = inches; 5) In initialize add our resetDriveEncoder method We want to reset the encoder before we drive so that it counts the distance from zero 6) In execute add our arcadeDrive method and change the moveSpeed parameter to a RobotPreference named driveDistanceSpeed and rotateSpeed to 0.0 We only want to drive the robot forward; a RobotPreference will help us tune the drive speed 7) In isFinished type: return Robot.m_drivetrain.getDriveEncoderDistance() == distance; 8) In end stop the Drivetrain and call end in interrupted Example Your full DriveDistance.java should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.Command; import frc.robot.Robot; import frc.robot.RobotPreferences; public class DriveDistance extends Command { private double distance; public DriveDistance(double inches) { // Use requires() here to declare subsystem dependencies // eg. requires(chassis); requires(Robot.m_drivetrain); distance = inches; } // Called just before this Command runs the first time @Override protected void initialize() { Robot.m_drivetrain.resetDriveEncoder(); } // Called repeatedly when this Command is scheduled to run @Override protected void execute() { Robot.m_drivetrain.arcadeDrive(RobotPreferences.driveDistanceSpeed(), 0.0); } // Make this return true when this Command no longer needs to run execute() @Override protected boolean isFinished() { return Robot.m_drivetrain.getDriveEncoderDistance() == distance; } // Called once after isFinished returns true @Override protected void end() { Robot.m_drivetrain.arcadeDrive(0.0, 0.0); } // Called when another command which requires one or more of the same // subsystems is scheduled to run @Override protected void interrupted() { end(); } } The code you typed in RobotPreferences.java should be this public static final double driveDistanceSpeed() { return Preferences.getInstance().getDouble(\"driveDistanceSpeed\", 0.5); } Creating The Autonomous Command # We will create an Autonomous command group with the DriveDistance command and the ShooterPitchUp command 1) Create a new Command Group named Autonomous 2) In the constructor type addSequential(new DriveDistance(RobotPreferences.autoDriveDistance())); addSequential(new ShooterUp()); To add a command to run in a command group use addSequential to execute commands in order Creating the DoDelay Command # In order to add timing in between our commands in our command groups we will need to create a DoDelay command Unlike regular delays the DoDelay command will not stall our robot, but wait a certain amount of time before running a command 1) Create a new command called DoDelay 2) Before the constructor add two private doubles called expireTime and timeout 3) In the constructor add a double called seconds in the parameter 4) Inside the constructor set timeout equal to seconds 5) Create a protected void method called startTimer 6) Inside set expireTime equal to timeSinceInitialized + timeout This will let the robot know how much time will have passed since the command was initialized when it finishes 7) In initialized add our startTimer method 8) In isFinished return timeSinceInitialized is greater or equal to expireTime Example Your full DoDelay.java should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.Command; public class DoDelay extends Command { private double expireTime; private double timeout; public DoDelay(double seconds) { // Use requires() here to declare subsystem dependencies // eg. requires(chassis); timeout = seconds; } protected void startTimer() { expireTime = timeSinceInitialized() + timeout; } // Called just before this Command runs the first time @Override protected void initialize() { startTimer(); } // Called repeatedly when this Command is scheduled to run @Override protected void execute() { } // Make this return true when this Command no longer needs to run execute() @Override protected boolean isFinished() { return (timeSinceInitialized() >= expireTime); } // Called once after isFinished returns true @Override protected void end() { } // Called when another command which requires one or more of the same // subsystems is scheduled to run @Override protected void interrupted() { } } Adding the DoDelay Command to Autonomous.java # Add our DoDelay command in between DriveDistance and ShooterPitchUp with a RobotPreference called autoDelay Example Your full Autonomous.java should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.CommandGroup; import frc.robot.RobotPreferences; public class Autonomous extends CommandGroup { /** * Add your docs here. */ public Autonomous() { addSequential(new DriveDistance(RobotPreferences.autoDriveDistance())); addSequential(new DoDelay(RobotPreferences.autoDelay())); addSequential(new ShooterUp()); } } The code you typed in RobotPreferences.java should look like this public static double autoDelay() { return Preferences.getInstance().getDouble(\"autoDelay\", 5.0); } public static double autoDriveDistance() { return Preferences.getInstance().getDouble(\"autoDriveDistance\", 12.0); } Adding Our Autonomous Command to Robot.java # In order to run our Autonomous command in autonomous we will have to put it in Robot.java so that it will run as soon as the robot enters the autonomous mode In Robot.java under autonomousInit find m_autonomousCommand = m_chooser.getSelected(); and change it to public void autonomousInit() { m_autonomousCommand = new Autonomous(); ... Testing Our Autonomous Command # Now that we have finished coding our Autonomous command deploy code and add our new RobotPreferences to the widget on the ShuffleBoard We have three preferences that change our autonomous behavior driveDistanceSpeed , autoDriveDistance and autoDelay driveDistanceSpeed will determine the direction and how fast the robot drives autoDriveDistance will determine how many inches the robot drives forward or backward autoDelay will determine how long the robot waits before executing ShooterPitchUp Change these values before enabling your robot in autonomous to make you get the desired results Tips For Debugging Our Autonomous Command # If the robot doesn't seem to stop moving or drive in the right direction check for inversions in your Drive commands or in the Drivetrain subsystem You may also need to check if your encoder is working, if there are inversions, or if you are using the getEncoderCount method instead of the getEncoderDistanceMethod If your robot doesn't move make sure you typed in the RobotPreference names exactly or check your talon IDs/Connection If nothing happens after your robot is finished driving check your autoDelay preference and whether your Shooter piston is already actuated or if your solenoids are working","title":"[WIP] Creating an Autonomous Command"},{"location":"programming/autonomous.html#wip-creating-an-autonomous-command","text":"","title":"[WIP] Creating an Autonomous Command"},{"location":"programming/autonomous.html#overview","text":"In this section we will be going over Creating an autonomous command group Using RobotPreferences to quickly change our autonomous values Using an encoder to autonomously drive Creating a delay timer to pace our commands in autonomous","title":"Overview"},{"location":"programming/autonomous.html#what-is-an-autonomous-command","text":"An autonomous command is a command that is ran during \"autonomous mode\" under the autonomousInit method in Robot.java It could be a single command or a command group It's especially helpful to have if you don't have any cameras to drive the robot during a \"sandstorm\" period (2019 game mechanic where the drivers couldn't see during the pre tele-op phase) For this tutorial we will create an autonomous command group that makes the robot drive forward 5 feet, wait 5 seconds, and then pitch the shooter up during autonomous","title":"What Is an Autonomous Command"},{"location":"programming/autonomous.html#creating-commands-for-autonomous","text":"Since we can't control our robot during an autonomous command we will want to create commands that allow the robot to move independently of a driver","title":"Creating Commands For Autonomous"},{"location":"programming/autonomous.html#creating-the-drivedistance-command","text":"1) Create a new command called DriveDistance 2) Before the constructor create a double called distance We will use this to tell the command to finish when the robot drives the inputted distance 3) In the DriveDistance constructor add a double parameter called inches 4) Inside type: distance = inches; 5) In initialize add our resetDriveEncoder method We want to reset the encoder before we drive so that it counts the distance from zero 6) In execute add our arcadeDrive method and change the moveSpeed parameter to a RobotPreference named driveDistanceSpeed and rotateSpeed to 0.0 We only want to drive the robot forward; a RobotPreference will help us tune the drive speed 7) In isFinished type: return Robot.m_drivetrain.getDriveEncoderDistance() == distance; 8) In end stop the Drivetrain and call end in interrupted Example Your full DriveDistance.java should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.Command; import frc.robot.Robot; import frc.robot.RobotPreferences; public class DriveDistance extends Command { private double distance; public DriveDistance(double inches) { // Use requires() here to declare subsystem dependencies // eg. requires(chassis); requires(Robot.m_drivetrain); distance = inches; } // Called just before this Command runs the first time @Override protected void initialize() { Robot.m_drivetrain.resetDriveEncoder(); } // Called repeatedly when this Command is scheduled to run @Override protected void execute() { Robot.m_drivetrain.arcadeDrive(RobotPreferences.driveDistanceSpeed(), 0.0); } // Make this return true when this Command no longer needs to run execute() @Override protected boolean isFinished() { return Robot.m_drivetrain.getDriveEncoderDistance() == distance; } // Called once after isFinished returns true @Override protected void end() { Robot.m_drivetrain.arcadeDrive(0.0, 0.0); } // Called when another command which requires one or more of the same // subsystems is scheduled to run @Override protected void interrupted() { end(); } } The code you typed in RobotPreferences.java should be this public static final double driveDistanceSpeed() { return Preferences.getInstance().getDouble(\"driveDistanceSpeed\", 0.5); }","title":"Creating the DriveDistance Command"},{"location":"programming/autonomous.html#creating-the-autonomous-command","text":"We will create an Autonomous command group with the DriveDistance command and the ShooterPitchUp command 1) Create a new Command Group named Autonomous 2) In the constructor type addSequential(new DriveDistance(RobotPreferences.autoDriveDistance())); addSequential(new ShooterUp()); To add a command to run in a command group use addSequential to execute commands in order","title":"Creating The Autonomous Command"},{"location":"programming/autonomous.html#creating-the-dodelay-command","text":"In order to add timing in between our commands in our command groups we will need to create a DoDelay command Unlike regular delays the DoDelay command will not stall our robot, but wait a certain amount of time before running a command 1) Create a new command called DoDelay 2) Before the constructor add two private doubles called expireTime and timeout 3) In the constructor add a double called seconds in the parameter 4) Inside the constructor set timeout equal to seconds 5) Create a protected void method called startTimer 6) Inside set expireTime equal to timeSinceInitialized + timeout This will let the robot know how much time will have passed since the command was initialized when it finishes 7) In initialized add our startTimer method 8) In isFinished return timeSinceInitialized is greater or equal to expireTime Example Your full DoDelay.java should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.Command; public class DoDelay extends Command { private double expireTime; private double timeout; public DoDelay(double seconds) { // Use requires() here to declare subsystem dependencies // eg. requires(chassis); timeout = seconds; } protected void startTimer() { expireTime = timeSinceInitialized() + timeout; } // Called just before this Command runs the first time @Override protected void initialize() { startTimer(); } // Called repeatedly when this Command is scheduled to run @Override protected void execute() { } // Make this return true when this Command no longer needs to run execute() @Override protected boolean isFinished() { return (timeSinceInitialized() >= expireTime); } // Called once after isFinished returns true @Override protected void end() { } // Called when another command which requires one or more of the same // subsystems is scheduled to run @Override protected void interrupted() { } }","title":"Creating the DoDelay Command"},{"location":"programming/autonomous.html#adding-the-dodelay-command-to-autonomousjava","text":"Add our DoDelay command in between DriveDistance and ShooterPitchUp with a RobotPreference called autoDelay Example Your full Autonomous.java should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.CommandGroup; import frc.robot.RobotPreferences; public class Autonomous extends CommandGroup { /** * Add your docs here. */ public Autonomous() { addSequential(new DriveDistance(RobotPreferences.autoDriveDistance())); addSequential(new DoDelay(RobotPreferences.autoDelay())); addSequential(new ShooterUp()); } } The code you typed in RobotPreferences.java should look like this public static double autoDelay() { return Preferences.getInstance().getDouble(\"autoDelay\", 5.0); } public static double autoDriveDistance() { return Preferences.getInstance().getDouble(\"autoDriveDistance\", 12.0); }","title":"Adding the DoDelay Command to Autonomous.java"},{"location":"programming/autonomous.html#adding-our-autonomous-command-to-robotjava","text":"In order to run our Autonomous command in autonomous we will have to put it in Robot.java so that it will run as soon as the robot enters the autonomous mode In Robot.java under autonomousInit find m_autonomousCommand = m_chooser.getSelected(); and change it to public void autonomousInit() { m_autonomousCommand = new Autonomous(); ...","title":"Adding Our Autonomous Command to Robot.java"},{"location":"programming/autonomous.html#testing-our-autonomous-command","text":"Now that we have finished coding our Autonomous command deploy code and add our new RobotPreferences to the widget on the ShuffleBoard We have three preferences that change our autonomous behavior driveDistanceSpeed , autoDriveDistance and autoDelay driveDistanceSpeed will determine the direction and how fast the robot drives autoDriveDistance will determine how many inches the robot drives forward or backward autoDelay will determine how long the robot waits before executing ShooterPitchUp Change these values before enabling your robot in autonomous to make you get the desired results","title":"Testing Our Autonomous Command"},{"location":"programming/autonomous.html#tips-for-debugging-our-autonomous-command","text":"If the robot doesn't seem to stop moving or drive in the right direction check for inversions in your Drive commands or in the Drivetrain subsystem You may also need to check if your encoder is working, if there are inversions, or if you are using the getEncoderCount method instead of the getEncoderDistanceMethod If your robot doesn't move make sure you typed in the RobotPreference names exactly or check your talon IDs/Connection If nothing happens after your robot is finished driving check your autoDelay preference and whether your Shooter piston is already actuated or if your solenoids are working","title":"Tips For Debugging Our Autonomous Command"},{"location":"programming/deploying.html","text":"Deploying Robot Code # Bring your creation to life! Overview # This section will help you learn to deploy code to your robot. See table of contents for a breakdown of this section. How to deploy # Hardware # To deploy code, first make sure your computer is connected to the robot in ONE of the following ways: USB Ethernet Robot's Wireless Network Software # Note Make sure your team number in **wpilib_preferences.json** in the **.wpilib** folder is set to the same team number your roboRIO was programmed for (it should be the number you set when creating the project and you will NOT need to check this every time as it should not change by itself). 1) Select the W icon from the tab bar or use the shortcut by holding down Ctrl+Shift+P at the same time. (Replace ctrl with cmd on macOS) 2) Type and hit enter or select: WPILib: Deploy Robot Code Tip Alternatively you can do one of the following: - Use **Shift+F5** at any time to deploy. (you may also need to hold fn depending on your computer configuration) - Right-click on the build.gradle file in the project hierarchy and select \"Build Robot Code\u201d - Open the shortcut menu indicated by the ellipses in the top right corner of the VS Code window and select \"Build Robot Code\" Testing # Open up the DriverStation software on any computer that has it installed. Enable the robot Try moving the joysticks on your controller when enabled. If it doesn\u2019t, check your port numbers for your controller, axes, and motor controllers","title":"Deploying Robot Code"},{"location":"programming/deploying.html#deploying-robot-code","text":"Bring your creation to life!","title":"Deploying Robot Code"},{"location":"programming/deploying.html#overview","text":"This section will help you learn to deploy code to your robot. See table of contents for a breakdown of this section.","title":"Overview"},{"location":"programming/deploying.html#how-to-deploy","text":"","title":"How to deploy"},{"location":"programming/deploying.html#hardware","text":"To deploy code, first make sure your computer is connected to the robot in ONE of the following ways: USB Ethernet Robot's Wireless Network","title":"Hardware"},{"location":"programming/deploying.html#software","text":"Note Make sure your team number in **wpilib_preferences.json** in the **.wpilib** folder is set to the same team number your roboRIO was programmed for (it should be the number you set when creating the project and you will NOT need to check this every time as it should not change by itself). 1) Select the W icon from the tab bar or use the shortcut by holding down Ctrl+Shift+P at the same time. (Replace ctrl with cmd on macOS) 2) Type and hit enter or select: WPILib: Deploy Robot Code Tip Alternatively you can do one of the following: - Use **Shift+F5** at any time to deploy. (you may also need to hold fn depending on your computer configuration) - Right-click on the build.gradle file in the project hierarchy and select \"Build Robot Code\u201d - Open the shortcut menu indicated by the ellipses in the top right corner of the VS Code window and select \"Build Robot Code\"","title":"Software"},{"location":"programming/deploying.html#testing","text":"Open up the DriverStation software on any computer that has it installed. Enable the robot Try moving the joysticks on your controller when enabled. If it doesn\u2019t, check your port numbers for your controller, axes, and motor controllers","title":"Testing"},{"location":"programming/driving_robot.html","text":"Creating a Basic Driving Robot # Lets get moving! Picture source: Team 2984 Overview # This section is designed to help you program a basic driving robot, start to finish. See table of contents for a breakdown of this section. Creating the Drivetrain Subsystem # Before we begin we must create the class file for the drivetrain subsystem. See Creating a New Subsystem for info on how to do this. What will be added to the Drivetrain # In the Drivetrain class we will tell the subsystem what type of components it will be using. A Drivetrain needs motor controllers. In our case we will use 4 Talon SRs (a brand of controller for motors). You could use other motor controllers such as Victor SPs or Talon SRXs but we will be using Talon SRs If you are using other motor controllers, replace Talon with TalonSRX, Victor, or VictorSP in the code you write depending on the type you use. You can use 2 motors (left and right), but for this tutorial we will use 4. Tip Be sure to read [Visual Studio Code Tips](../basics/vscode_tips.md){target=_blank} before getting started! It will make your life a lot easier. Creating the Talon Variables # 1) Create 4 global variables of data type Talon and name them: leftFrontTalon , rightFrontTalon , leftBackTalon , rightBackTalon To get started type the word Talon followed by the name i.e. Talon leftFrontTalon; These will eventually hold the object values for Talons and their port numbers. 2) Next assign their values to null ( more info on null ). We do this to make sure it is empty at this point. When we assign these variables a value, we will be getting the motor controller's port numbers out of Constants This means we cannot assign them at the global level Example The code you typed should be this: Talon leftFrontTalon = null; Talon leftBackTalon = null; Talon rightFrontTalon = null; Talon rightBackTalon = null; Your full Drivetrain.java should look like this: package frc.robot.subsystems; import edu.wpi.first.wpilibj.Talon; import edu.wpi.first.wpilibj.command.Subsystem; /** * Add your docs here. */ public class Drivetrain extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. Talon leftFrontTalon = null; Talon leftBackTalon = null; Talon rightFrontTalon = null; Talon rightBackTalon = null; @Override public void periodic() { // This method will be called once per scheduler run } } If an error occurs (red squiggles) Click the word Talon \ud83d\udca1 Click the light bulb Select \"Import 'Talon' (edu.wpi.first.wpilibj)\" Your error should be gone! Creating and filling the constructor # 1) Create the constructor for Drivetrain.java ( more info on constructors ) The constructor is where we will assign values to our talon variables. Now that we have created the Talons we must initialize them and tell them what port on the roboRIO they are on. 2) Initialize (set value of) leftFrontTalon to new Talon(0) . This initializes a new talon, leftFrontTalon , in a new piece of memory and states it is on port 0 of the roboRIO. This should be done within the constructor Drivetrain() The constructor Talon(int) takes a variable of type int . In this case the int (integer) refers to the port number on the roboRIO. This calls the constructor Talon(int) in the Talon class. roboRIO port diagram Example The code you typed should be this: public Drivetrain() { // Talons leftFrontTalon = new Talon(0); } Your full Drivetrain.java should look like this: package frc.robot.subsystems; import edu.wpi.first.wpilibj.Talon; import edu.wpi.first.wpilibj.command.Subsystem; /** * Add your docs here. */ public class Drivetrain extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. Talon leftFrontTalon = null; Talon leftBackTalon = null; Talon rightFrontTalon = null; Talon rightBackTalon = null; public Drivetrain() { // Talons leftFrontTalon = new Talon(0); } @Override public void periodic() { // This method will be called once per scheduler run } Using Constants # Since each subsystem has its own components with their own ports, it is easy to lose track of which ports are being used and for what. To counter this you can use a class called Constants to hold all these values in a single location. 1) To use Constants, instead of putting 0 for the port on the Talon type: Constants.DRIVETRAIN_LEFT_FRONT_TALON Names should follow the pattern SUBSYSTEM_NAME_OF_COMPONENT The name is all caps since it is a constant ( more info on constants ). 2) Click on the underlined text 3) Click on the \ud83d\udca1light bulb and select \u201ccreate constant\u2026\u201d 4) Click on Constants.java tab that just popped up 5) Change the 0 to the correct port for that motor controller on your robot/roboRIO Danger If you set this to the wrong value, you could damage your robot when it tries to move! 6) Repeat these steps for the remaining Talons. Tip Remember to save both Drivetrain.java and Constants.java Example The code you type should be this: leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON); Your full Drivetrain.java should look like this: package frc.robot.subsystems; import edu.wpi.first.wpilibj.Talon; import edu.wpi.first.wpilibj.command.Subsystem; import frc.robot.Constants; /** * Add your docs here. */ public class Drivetrain extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. Talon leftFrontTalon = null; Talon leftBackTalon = null; Talon rightFrontTalon = null; Talon rightBackTalon = null; public Drivetrain() { // Talons leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON); leftBackTalon = new Talon(Constants.DRIVETRAIN_LEFT_BACK_TALON); rightFrontTalon = new Talon(Constants.DRIVETRAIN_RIGHT_FRONT_TALON); rightBackTalon = new Talon(Constants.DRIVETRAIN_RIGHT_BACK_TALON); } @Override public void periodic() { // This method will be called once per scheduler run } } Your full Constants.java should look similar to this: package frc.robot; public class Constants { // Talons public static final int DRIVETRAIN_LEFT_FRONT_TALON = 0; public static final int DRIVETRAIN_LEFT_BACK_TALON = 1; public static final int DRIVETRAIN_RIGHT_FRONT_TALON = 2; public static final int DRIVETRAIN_RIGHT_BACK_TALON = 3; } Warning Remember to use the values for YOUR specific robot or you could risk damaging it! Creating the arcade drive # What is the Drive Class # The FIRST Drive class has many pre-configured methods available to us including DifferentialDrive, and many alterations of MecanumDrive. DifferentialDrive contains subsections such as TankDrive and ArcadeDrive. For our tutorial we will be creating an ArcadeDrive Arcade drives run by taking a moveSpeed and rotateSpeed. moveSpeed defines the forward and reverse speed and rotateSpeed defines the turning left and right speed. To create an arcade drive we will be using our already existing Drivetrain class and adding to it. Programing a RobotDrive # 1) In the same place we created our talons (outside of the constructor) we will create a DifferentialDrive and SpeedControllerGroups for our left and right motor controllers. Outside of the constructor type: SpeedControllerGroup leftMotors = null; SpeedControllerGroup rightMotors = null; DifferentialDrive differentialDrive = null; Since DifferentialDrive only takes 2 parameters we need to create speed controller groups to combine like motor controllers together. In this case we will combine the left motors together and the right motors together. Warning You should only group motors that are spinning the same direction physically when positive power is being applied otherwise you could damage your robot. 2) Now we must initialize the SpeedControllerGroups and DifferentialDrive like we did our talons. ... In the constructor type: leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon); rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon); differentialDrive = new DifferentialDrive(leftMotors, rightMotors); Example The code you type outside the constructor should be this: SpeedControllerGroup leftMotors = null; SpeedControllerGroup rightMotors = null; DifferentialDrive differentialDrive = null; The code you type inside the constructor should be this: leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon); rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon); differentialDrive = new DifferentialDrive(leftMotors, rightMotors); Your full Drivetrain.java should look like this: package frc.robot.subsystems; import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.Talon; import edu.wpi.first.wpilibj.command.Subsystem; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import frc.robot.Constants; /** * Add your docs here. */ public class Drivetrain extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. Talon leftFrontTalon = null; Talon leftBackTalon = null; Talon rightFrontTalon = null; Talon rightBackTalon = null; SpeedControllerGroup leftMotors = null; SpeedControllerGroup rightMotors = null; DifferentialDrive differentialDrive = null; public Drivetrain() { // Talons leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON); leftBackTalon = new Talon(Constants.DRIVETRAIN_LEFT_BACK_TALON); rightFrontTalon = new Talon(Constants.DRIVETRAIN_RIGHT_FRONT_TALON); rightBackTalon = new Talon(Constants.DRIVETRAIN_RIGHT_BACK_TALON); leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon); rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon); differentialDrive = new DifferentialDrive(leftMotors, rightMotors); } @Override public void periodic() { // This method will be called once per scheduler run } } Creating the arcadeDrive method # Now it\u2019s time to make an arcadeDrive from our differentialDrive! 1) Let\u2019s create a public void method called \u201carcadeDrive\u201d with type \u201cdouble\u201d parameters moveSpeed and rotateSpeed. Below the constructor type: public void arcadeDrive(double moveSpeed, double rotateSpeed) { } Tip By putting something in the parentheses it makes the method require a parameter when it is used. When the method gets used and parameters are passed, they will be store in moveSpeed and rotateSpeed (in that order). See parameters for more info. 2) Now lets make our method call the differentialDrive's arcadeDrive method. Inside our method type: differentialDrive.arcadeDrive(moveSpeed, rotateSpeed); DifferentialDrive's arcadeDrive method takes parameters moveValue and rotateValue. Note At this point you could instead create a tank drive, however implementation differs slightly. To do so type differentialDrive.tankDrive(moveSpeed, rotateSpeed); instead of differentialDrive.arcadeDrive(moveSpeed, rotateSpeed); and change the method name reflect this. Tip If you want to limit the max speed you can multiple the speeds by a decimal (i.e. 0.5*moveSpeed will make the motors only move half of their maximum speed) You may want to do this for initial testing to make sure everything is going the right direction. Example The code you type should be this: public void arcadeDrive(double moveSpeed, double rotateSpeed) { differentialDrive.arcadeDrive(moveSpeed, rotateSpeed); } Your full Drivetrain.java should look like this: package frc.robot.subsystems; import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.Talon; import edu.wpi.first.wpilibj.command.Subsystem; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import frc.robot.Constants; /** * Add your docs here. */ public class Drivetrain extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. Talon leftFrontTalon = null; Talon leftBackTalon = null; Talon rightFrontTalon = null; Talon rightBackTalon = null; SpeedControllerGroup leftMotors = null; SpeedControllerGroup rightMotors = null; DifferentialDrive differentialDrive = null; public Drivetrain() { // Talons leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON); leftBackTalon = new Talon(Constants.DRIVETRAIN_LEFT_BACK_TALON); rightFrontTalon = new Talon(Constants.DRIVETRAIN_RIGHT_FRONT_TALON); rightBackTalon = new Talon(Constants.DRIVETRAIN_RIGHT_BACK_TALON); leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon); rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon); differentialDrive = new DifferentialDrive(leftMotors, rightMotors); } public void arcadeDrive(double moveSpeed, double rotateSpeed) { differentialDrive.arcadeDrive(moveSpeed, rotateSpeed); } @Override public void periodic() { // This method will be called once per scheduler run } } Making our robot controllable # Creating the Joystick # In order to drive our robot, it needs to know what will be controlling it. To do so, we will create a new joystick in RobotContainer.java 1) Open RobotContainer.java 2) Type: public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER); Import any classes if necessary such as: import edu.wpi.first.wpilibj.Joystick; A variable driverController of type Joystick pointing to a joystick on port DRIVER_CONTROLLER from Constants 3) Click the \ud83d\udca1 light bulb to create a new CONSTANT and set the value to the port number the joystick uses on the laptop (this can be found in the Driverstation software). Example The code you type should be this: public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER); Your full RobotContainer.java should look like this: package frc.robot; import edu.wpi.first.wpilibj.Joystick; /** * This class is where the bulk of the robot should be declared. Since * Command-based is a \"declarative\" paradigm, very little robot logic should * actually be handled in the {@link Robot} periodic methods (other than the * scheduler calls). Instead, the structure of the robot (including subsystems, * commands, and button mappings) should be declared here. */ public class RobotContainer { // The robot's subsystems and commands are defined here... public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER); } Your full Constants.java should look similar to this: package frc.robot; public class Constants { // Talons public static final int DRIVETRAIN_LEFT_FRONT_TALON = 0; public static final int DRIVETRAIN_LEFT_BACK_TALON = 1; public static final int DRIVETRAIN_RIGHT_FRONT_TALON = 2; public static final int DRIVETRAIN_RIGHT_BACK_TALON = 3; // Joysticks public static final int DRIVER_CONTROLLER = 0; } Creating the DriveArcade Command # Remember that methods tell the robot what it can do but in order to make it do these things we must give it a command . See Command Based Robot Now that we have created the method, we need to create a command to call and use that method. Let\u2019s create a new command called DriveArcade that calls arcadeDrive method we just created! Before we begin we must create the class file for the DriveArcade command. See Creating a New Command for info on how to do this and info on what each pre-created method does. In the constructor # 1) In the constructor DriveArcade() type: addRequirements(RobotContainer.m_drivetrain); This means, this command will end all other commands currently using drivetrain and will run instead when executed. It also means, other commands that require drivetrain will stop this command and run instead when executed. Warning If you use the light bulb to import \u2018Robot', be sure to import the one with \u201cfrc.robot\u201d In the execute method # 1) In the execute method we will create 2 variables of type double called moveSpeed and rotateSpeed. We want these variables to be the value of the axis of the controller we are using to drive the robot. So we will set them equal to that by using the joystick getRawAxis method. Controllers return an axis value between 1 and -1 to indicate how far the joystick is pushed up or down. Our personal controller returns up as -1 so we want to invert it. In Java you can put a negative \u201c - \u201cin front of a numeric value to invert it (value * -1) The joystick\u2019s getRawAxis method will get the position value of the axis as you move it. The method takes parameter \u201caxis number.\u201d (This can be found in the Driverstation software and we will store it in Constants). In the execute() method type: double moveSpeed = -RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_MOVE_AXIS); double rotateSpeed = RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_ROTATE_AXIS); Tip Remember to use the light bulb for importing and creating constants if needed! 2) Also in the execute method we will we want to call the arcadeDrive method we created in Drivetrain and give it the variables moveSpeed and rotateSpeed we created as parameters. In the execute() method below rotateSpeed type: RobotContainer.m_drivetrain.arcadeDrive(moveSpeed, rotateSpeed); In the isFinished method # Since we will be using this command to control the robot we want it to run indefinitely. 1) To do this we are going to continue having isFinished return false, meaning the command will never finish. (We don't need to change anything as this is the default) Tip If we did want a command to finish, we make this return true. This can be done by replacing false with true to make it finish instantly Alternatively we can make a condition which can return true For example (timePassed > 10) will return true after 10 seconds but return false anytime before 10 seconds have passed. In the end method # 1) We will call the arcadeDrive method and give it 0 and 0 as the parameters. In the end() method type: RobotContainer.m_drivetrain.arcadeDrive(0, 0); This make the motors stop running when the command ends by setting the movement speed to zero and rotation speed to zero. Completed Example # Example Your full Constants.java should look similar to this: package frc.robot; public class Constants { // Talons public static final int DRIVETRAIN_LEFT_FRONT_TALON = 0; public static final int DRIVETRAIN_LEFT_BACK_TALON = 1; public static final int DRIVETRAIN_RIGHT_FRONT_TALON = 2; public static final int DRIVETRAIN_RIGHT_BACK_TALON = 3; // Joysticks public static final int DRIVER_CONTROLLER = 0; public static final int DRIVER_CONTROLLER_MOVE_AXIS = 1; // Change for your controller public static final int DRIVER_CONTROLLER_ROTATE_AXIS = 2; // Change for your controller } Your full DriveArcade.java should look like this: package frc.robot.commands; import edu.wpi.first.wpilibj.command.Command; import frc.robot.RobotContainer; import frc.robot.Constants; public class DriveArcade extends Command { public DriveArcade() { // Use addRequirements() here to declare subsystem dependencies. addRequirements(RobotContainer.m_drivetrain); } // Called just before this Command runs the first time @Override protected void initialize() { } // Called repeatedly when this Command is scheduled to run @Override protected void execute() { double moveSpeed = -RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_MOVE_AXIS); double rotateSpeed = RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_ROTATE_AXIS); RobotContainer.m_drivetrain.arcadeDrive(moveSpeed, rotateSpeed); } // Called once the command ends or is interrupted. @Override protected void end(boolean interrupted) { Robot.m_drivetrain.arcadeDrive(0, 0); } // Make this return true when this Command no longer needs to run execute() @Override protected boolean isFinished() { return false; } } Using setDefaultCommand # Commands passed to this method will run when the robot is enabled. They also run if no other commands using the subsystem are running. This is why we write addRequirements(Robot.m_subsystemName) in the commands we create, it ends currently running commands using that subsystem to allow a new command is run. 1) Back in RobotContainer.java in the constructor we will call the setDefaultCommand of m_drivetrain and pass it the DriveArcade command In the RobotContainer.java constructor type: m_drivetrain.setDefaultCommand(new DriveArcade()); Tip Remember to use the light bulb for importing if needed! Example Your full RobotContainer.java should look like this: package frc.robot; import edu.wpi.first.wpilibj.Joystick; import frc.robot.commands.*; import frc.robot.subsystems.*; import edu.wpi.first.wpilibj2.command.Command; /** * This class is where the bulk of the robot should be declared. Since Command-based is a * \"declarative\" paradigm, very little robot logic should actually be handled in the {@link Robot} * periodic methods (other than the scheduler calls). Instead, the structure of the robot * (including subsystems, commands, and button mappings) should be declared here. */ public class RobotContainer { // The robot's subsystems and commands are defined here... public static final Drivetrain m_drivetrain = new Drivetrain(); private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem(); private final ExampleCommand m_autoCommand = new ExampleCommand(m_exampleSubsystem); public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER); /** * The container for the robot. Contains subsystems, OI devices, and commands. */ public RobotContainer() { // Configure the button bindings configureButtonBindings(); // Set default commands on subsystems m_drivetrain.setDefaultCommand(new DriveArcade()); } /** * Use this method to define your button->command mappings. Buttons can be created by * instantiating a {@link GenericHID} or one of its subclasses ({@link * edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then passing it to a * {@link edu.wpi.first.wpilibj2.command.button.JoystickButton}. */ private void configureButtonBindings() { } /** * Use this to pass the autonomous command to the main {@link Robot} class. * * @return the command to run in autonomous */ public Command getAutonomousCommand() { // An ExampleCommand will run in autonomous return m_autoCommand; } }","title":"Creating a Basic Driving Robot"},{"location":"programming/driving_robot.html#creating-a-basic-driving-robot","text":"Lets get moving! Picture source: Team 2984","title":"Creating a Basic Driving Robot"},{"location":"programming/driving_robot.html#overview","text":"This section is designed to help you program a basic driving robot, start to finish. See table of contents for a breakdown of this section.","title":"Overview"},{"location":"programming/driving_robot.html#creating-the-drivetrain-subsystem","text":"Before we begin we must create the class file for the drivetrain subsystem. See Creating a New Subsystem for info on how to do this.","title":"Creating the Drivetrain Subsystem"},{"location":"programming/driving_robot.html#what-will-be-added-to-the-drivetrain","text":"In the Drivetrain class we will tell the subsystem what type of components it will be using. A Drivetrain needs motor controllers. In our case we will use 4 Talon SRs (a brand of controller for motors). You could use other motor controllers such as Victor SPs or Talon SRXs but we will be using Talon SRs If you are using other motor controllers, replace Talon with TalonSRX, Victor, or VictorSP in the code you write depending on the type you use. You can use 2 motors (left and right), but for this tutorial we will use 4. Tip Be sure to read [Visual Studio Code Tips](../basics/vscode_tips.md){target=_blank} before getting started! It will make your life a lot easier.","title":"What will be added to the Drivetrain"},{"location":"programming/driving_robot.html#creating-the-talon-variables","text":"1) Create 4 global variables of data type Talon and name them: leftFrontTalon , rightFrontTalon , leftBackTalon , rightBackTalon To get started type the word Talon followed by the name i.e. Talon leftFrontTalon; These will eventually hold the object values for Talons and their port numbers. 2) Next assign their values to null ( more info on null ). We do this to make sure it is empty at this point. When we assign these variables a value, we will be getting the motor controller's port numbers out of Constants This means we cannot assign them at the global level Example The code you typed should be this: Talon leftFrontTalon = null; Talon leftBackTalon = null; Talon rightFrontTalon = null; Talon rightBackTalon = null; Your full Drivetrain.java should look like this: package frc.robot.subsystems; import edu.wpi.first.wpilibj.Talon; import edu.wpi.first.wpilibj.command.Subsystem; /** * Add your docs here. */ public class Drivetrain extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. Talon leftFrontTalon = null; Talon leftBackTalon = null; Talon rightFrontTalon = null; Talon rightBackTalon = null; @Override public void periodic() { // This method will be called once per scheduler run } } If an error occurs (red squiggles) Click the word Talon \ud83d\udca1 Click the light bulb Select \"Import 'Talon' (edu.wpi.first.wpilibj)\" Your error should be gone!","title":"Creating the Talon Variables"},{"location":"programming/driving_robot.html#creating-and-filling-the-constructor","text":"1) Create the constructor for Drivetrain.java ( more info on constructors ) The constructor is where we will assign values to our talon variables. Now that we have created the Talons we must initialize them and tell them what port on the roboRIO they are on. 2) Initialize (set value of) leftFrontTalon to new Talon(0) . This initializes a new talon, leftFrontTalon , in a new piece of memory and states it is on port 0 of the roboRIO. This should be done within the constructor Drivetrain() The constructor Talon(int) takes a variable of type int . In this case the int (integer) refers to the port number on the roboRIO. This calls the constructor Talon(int) in the Talon class. roboRIO port diagram Example The code you typed should be this: public Drivetrain() { // Talons leftFrontTalon = new Talon(0); } Your full Drivetrain.java should look like this: package frc.robot.subsystems; import edu.wpi.first.wpilibj.Talon; import edu.wpi.first.wpilibj.command.Subsystem; /** * Add your docs here. */ public class Drivetrain extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. Talon leftFrontTalon = null; Talon leftBackTalon = null; Talon rightFrontTalon = null; Talon rightBackTalon = null; public Drivetrain() { // Talons leftFrontTalon = new Talon(0); } @Override public void periodic() { // This method will be called once per scheduler run }","title":"Creating and filling the constructor"},{"location":"programming/driving_robot.html#using-constants","text":"Since each subsystem has its own components with their own ports, it is easy to lose track of which ports are being used and for what. To counter this you can use a class called Constants to hold all these values in a single location. 1) To use Constants, instead of putting 0 for the port on the Talon type: Constants.DRIVETRAIN_LEFT_FRONT_TALON Names should follow the pattern SUBSYSTEM_NAME_OF_COMPONENT The name is all caps since it is a constant ( more info on constants ). 2) Click on the underlined text 3) Click on the \ud83d\udca1light bulb and select \u201ccreate constant\u2026\u201d 4) Click on Constants.java tab that just popped up 5) Change the 0 to the correct port for that motor controller on your robot/roboRIO Danger If you set this to the wrong value, you could damage your robot when it tries to move! 6) Repeat these steps for the remaining Talons. Tip Remember to save both Drivetrain.java and Constants.java Example The code you type should be this: leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON); Your full Drivetrain.java should look like this: package frc.robot.subsystems; import edu.wpi.first.wpilibj.Talon; import edu.wpi.first.wpilibj.command.Subsystem; import frc.robot.Constants; /** * Add your docs here. */ public class Drivetrain extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. Talon leftFrontTalon = null; Talon leftBackTalon = null; Talon rightFrontTalon = null; Talon rightBackTalon = null; public Drivetrain() { // Talons leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON); leftBackTalon = new Talon(Constants.DRIVETRAIN_LEFT_BACK_TALON); rightFrontTalon = new Talon(Constants.DRIVETRAIN_RIGHT_FRONT_TALON); rightBackTalon = new Talon(Constants.DRIVETRAIN_RIGHT_BACK_TALON); } @Override public void periodic() { // This method will be called once per scheduler run } } Your full Constants.java should look similar to this: package frc.robot; public class Constants { // Talons public static final int DRIVETRAIN_LEFT_FRONT_TALON = 0; public static final int DRIVETRAIN_LEFT_BACK_TALON = 1; public static final int DRIVETRAIN_RIGHT_FRONT_TALON = 2; public static final int DRIVETRAIN_RIGHT_BACK_TALON = 3; } Warning Remember to use the values for YOUR specific robot or you could risk damaging it!","title":"Using Constants"},{"location":"programming/driving_robot.html#creating-the-arcade-drive","text":"","title":"Creating the arcade drive"},{"location":"programming/driving_robot.html#what-is-the-drive-class","text":"The FIRST Drive class has many pre-configured methods available to us including DifferentialDrive, and many alterations of MecanumDrive. DifferentialDrive contains subsections such as TankDrive and ArcadeDrive. For our tutorial we will be creating an ArcadeDrive Arcade drives run by taking a moveSpeed and rotateSpeed. moveSpeed defines the forward and reverse speed and rotateSpeed defines the turning left and right speed. To create an arcade drive we will be using our already existing Drivetrain class and adding to it.","title":"What is the Drive Class"},{"location":"programming/driving_robot.html#programing-a-robotdrive","text":"1) In the same place we created our talons (outside of the constructor) we will create a DifferentialDrive and SpeedControllerGroups for our left and right motor controllers. Outside of the constructor type: SpeedControllerGroup leftMotors = null; SpeedControllerGroup rightMotors = null; DifferentialDrive differentialDrive = null; Since DifferentialDrive only takes 2 parameters we need to create speed controller groups to combine like motor controllers together. In this case we will combine the left motors together and the right motors together. Warning You should only group motors that are spinning the same direction physically when positive power is being applied otherwise you could damage your robot. 2) Now we must initialize the SpeedControllerGroups and DifferentialDrive like we did our talons. ... In the constructor type: leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon); rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon); differentialDrive = new DifferentialDrive(leftMotors, rightMotors); Example The code you type outside the constructor should be this: SpeedControllerGroup leftMotors = null; SpeedControllerGroup rightMotors = null; DifferentialDrive differentialDrive = null; The code you type inside the constructor should be this: leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon); rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon); differentialDrive = new DifferentialDrive(leftMotors, rightMotors); Your full Drivetrain.java should look like this: package frc.robot.subsystems; import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.Talon; import edu.wpi.first.wpilibj.command.Subsystem; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import frc.robot.Constants; /** * Add your docs here. */ public class Drivetrain extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. Talon leftFrontTalon = null; Talon leftBackTalon = null; Talon rightFrontTalon = null; Talon rightBackTalon = null; SpeedControllerGroup leftMotors = null; SpeedControllerGroup rightMotors = null; DifferentialDrive differentialDrive = null; public Drivetrain() { // Talons leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON); leftBackTalon = new Talon(Constants.DRIVETRAIN_LEFT_BACK_TALON); rightFrontTalon = new Talon(Constants.DRIVETRAIN_RIGHT_FRONT_TALON); rightBackTalon = new Talon(Constants.DRIVETRAIN_RIGHT_BACK_TALON); leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon); rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon); differentialDrive = new DifferentialDrive(leftMotors, rightMotors); } @Override public void periodic() { // This method will be called once per scheduler run } }","title":"Programing a RobotDrive"},{"location":"programming/driving_robot.html#creating-the-arcadedrive-method","text":"Now it\u2019s time to make an arcadeDrive from our differentialDrive! 1) Let\u2019s create a public void method called \u201carcadeDrive\u201d with type \u201cdouble\u201d parameters moveSpeed and rotateSpeed. Below the constructor type: public void arcadeDrive(double moveSpeed, double rotateSpeed) { } Tip By putting something in the parentheses it makes the method require a parameter when it is used. When the method gets used and parameters are passed, they will be store in moveSpeed and rotateSpeed (in that order). See parameters for more info. 2) Now lets make our method call the differentialDrive's arcadeDrive method. Inside our method type: differentialDrive.arcadeDrive(moveSpeed, rotateSpeed); DifferentialDrive's arcadeDrive method takes parameters moveValue and rotateValue. Note At this point you could instead create a tank drive, however implementation differs slightly. To do so type differentialDrive.tankDrive(moveSpeed, rotateSpeed); instead of differentialDrive.arcadeDrive(moveSpeed, rotateSpeed); and change the method name reflect this. Tip If you want to limit the max speed you can multiple the speeds by a decimal (i.e. 0.5*moveSpeed will make the motors only move half of their maximum speed) You may want to do this for initial testing to make sure everything is going the right direction. Example The code you type should be this: public void arcadeDrive(double moveSpeed, double rotateSpeed) { differentialDrive.arcadeDrive(moveSpeed, rotateSpeed); } Your full Drivetrain.java should look like this: package frc.robot.subsystems; import edu.wpi.first.wpilibj.SpeedControllerGroup; import edu.wpi.first.wpilibj.Talon; import edu.wpi.first.wpilibj.command.Subsystem; import edu.wpi.first.wpilibj.drive.DifferentialDrive; import frc.robot.Constants; /** * Add your docs here. */ public class Drivetrain extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. Talon leftFrontTalon = null; Talon leftBackTalon = null; Talon rightFrontTalon = null; Talon rightBackTalon = null; SpeedControllerGroup leftMotors = null; SpeedControllerGroup rightMotors = null; DifferentialDrive differentialDrive = null; public Drivetrain() { // Talons leftFrontTalon = new Talon(Constants.DRIVETRAIN_LEFT_FRONT_TALON); leftBackTalon = new Talon(Constants.DRIVETRAIN_LEFT_BACK_TALON); rightFrontTalon = new Talon(Constants.DRIVETRAIN_RIGHT_FRONT_TALON); rightBackTalon = new Talon(Constants.DRIVETRAIN_RIGHT_BACK_TALON); leftMotors = new SpeedControllerGroup(leftFrontTalon, leftBackTalon); rightMotors = new SpeedControllerGroup(rightFrontTalon, rightBackTalon); differentialDrive = new DifferentialDrive(leftMotors, rightMotors); } public void arcadeDrive(double moveSpeed, double rotateSpeed) { differentialDrive.arcadeDrive(moveSpeed, rotateSpeed); } @Override public void periodic() { // This method will be called once per scheduler run } }","title":"Creating the arcadeDrive method"},{"location":"programming/driving_robot.html#making-our-robot-controllable","text":"","title":"Making our robot controllable"},{"location":"programming/driving_robot.html#creating-the-joystick","text":"In order to drive our robot, it needs to know what will be controlling it. To do so, we will create a new joystick in RobotContainer.java 1) Open RobotContainer.java 2) Type: public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER); Import any classes if necessary such as: import edu.wpi.first.wpilibj.Joystick; A variable driverController of type Joystick pointing to a joystick on port DRIVER_CONTROLLER from Constants 3) Click the \ud83d\udca1 light bulb to create a new CONSTANT and set the value to the port number the joystick uses on the laptop (this can be found in the Driverstation software). Example The code you type should be this: public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER); Your full RobotContainer.java should look like this: package frc.robot; import edu.wpi.first.wpilibj.Joystick; /** * This class is where the bulk of the robot should be declared. Since * Command-based is a \"declarative\" paradigm, very little robot logic should * actually be handled in the {@link Robot} periodic methods (other than the * scheduler calls). Instead, the structure of the robot (including subsystems, * commands, and button mappings) should be declared here. */ public class RobotContainer { // The robot's subsystems and commands are defined here... public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER); } Your full Constants.java should look similar to this: package frc.robot; public class Constants { // Talons public static final int DRIVETRAIN_LEFT_FRONT_TALON = 0; public static final int DRIVETRAIN_LEFT_BACK_TALON = 1; public static final int DRIVETRAIN_RIGHT_FRONT_TALON = 2; public static final int DRIVETRAIN_RIGHT_BACK_TALON = 3; // Joysticks public static final int DRIVER_CONTROLLER = 0; }","title":"Creating the Joystick"},{"location":"programming/driving_robot.html#creating-the-drivearcade-command","text":"Remember that methods tell the robot what it can do but in order to make it do these things we must give it a command . See Command Based Robot Now that we have created the method, we need to create a command to call and use that method. Let\u2019s create a new command called DriveArcade that calls arcadeDrive method we just created! Before we begin we must create the class file for the DriveArcade command. See Creating a New Command for info on how to do this and info on what each pre-created method does.","title":"Creating the DriveArcade Command"},{"location":"programming/driving_robot.html#in-the-constructor","text":"1) In the constructor DriveArcade() type: addRequirements(RobotContainer.m_drivetrain); This means, this command will end all other commands currently using drivetrain and will run instead when executed. It also means, other commands that require drivetrain will stop this command and run instead when executed. Warning If you use the light bulb to import \u2018Robot', be sure to import the one with \u201cfrc.robot\u201d","title":"In the constructor"},{"location":"programming/driving_robot.html#in-the-execute-method","text":"1) In the execute method we will create 2 variables of type double called moveSpeed and rotateSpeed. We want these variables to be the value of the axis of the controller we are using to drive the robot. So we will set them equal to that by using the joystick getRawAxis method. Controllers return an axis value between 1 and -1 to indicate how far the joystick is pushed up or down. Our personal controller returns up as -1 so we want to invert it. In Java you can put a negative \u201c - \u201cin front of a numeric value to invert it (value * -1) The joystick\u2019s getRawAxis method will get the position value of the axis as you move it. The method takes parameter \u201caxis number.\u201d (This can be found in the Driverstation software and we will store it in Constants). In the execute() method type: double moveSpeed = -RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_MOVE_AXIS); double rotateSpeed = RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_ROTATE_AXIS); Tip Remember to use the light bulb for importing and creating constants if needed! 2) Also in the execute method we will we want to call the arcadeDrive method we created in Drivetrain and give it the variables moveSpeed and rotateSpeed we created as parameters. In the execute() method below rotateSpeed type: RobotContainer.m_drivetrain.arcadeDrive(moveSpeed, rotateSpeed);","title":"In the execute method"},{"location":"programming/driving_robot.html#in-the-isfinished-method","text":"Since we will be using this command to control the robot we want it to run indefinitely. 1) To do this we are going to continue having isFinished return false, meaning the command will never finish. (We don't need to change anything as this is the default) Tip If we did want a command to finish, we make this return true. This can be done by replacing false with true to make it finish instantly Alternatively we can make a condition which can return true For example (timePassed > 10) will return true after 10 seconds but return false anytime before 10 seconds have passed.","title":"In the isFinished method"},{"location":"programming/driving_robot.html#in-the-end-method","text":"1) We will call the arcadeDrive method and give it 0 and 0 as the parameters. In the end() method type: RobotContainer.m_drivetrain.arcadeDrive(0, 0); This make the motors stop running when the command ends by setting the movement speed to zero and rotation speed to zero.","title":"In the end method"},{"location":"programming/driving_robot.html#completed-example","text":"Example Your full Constants.java should look similar to this: package frc.robot; public class Constants { // Talons public static final int DRIVETRAIN_LEFT_FRONT_TALON = 0; public static final int DRIVETRAIN_LEFT_BACK_TALON = 1; public static final int DRIVETRAIN_RIGHT_FRONT_TALON = 2; public static final int DRIVETRAIN_RIGHT_BACK_TALON = 3; // Joysticks public static final int DRIVER_CONTROLLER = 0; public static final int DRIVER_CONTROLLER_MOVE_AXIS = 1; // Change for your controller public static final int DRIVER_CONTROLLER_ROTATE_AXIS = 2; // Change for your controller } Your full DriveArcade.java should look like this: package frc.robot.commands; import edu.wpi.first.wpilibj.command.Command; import frc.robot.RobotContainer; import frc.robot.Constants; public class DriveArcade extends Command { public DriveArcade() { // Use addRequirements() here to declare subsystem dependencies. addRequirements(RobotContainer.m_drivetrain); } // Called just before this Command runs the first time @Override protected void initialize() { } // Called repeatedly when this Command is scheduled to run @Override protected void execute() { double moveSpeed = -RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_MOVE_AXIS); double rotateSpeed = RobotContainer.driverController.getRawAxis(Constants.DRIVER_CONTROLLER_ROTATE_AXIS); RobotContainer.m_drivetrain.arcadeDrive(moveSpeed, rotateSpeed); } // Called once the command ends or is interrupted. @Override protected void end(boolean interrupted) { Robot.m_drivetrain.arcadeDrive(0, 0); } // Make this return true when this Command no longer needs to run execute() @Override protected boolean isFinished() { return false; } }","title":"Completed Example"},{"location":"programming/driving_robot.html#using-setdefaultcommand","text":"Commands passed to this method will run when the robot is enabled. They also run if no other commands using the subsystem are running. This is why we write addRequirements(Robot.m_subsystemName) in the commands we create, it ends currently running commands using that subsystem to allow a new command is run. 1) Back in RobotContainer.java in the constructor we will call the setDefaultCommand of m_drivetrain and pass it the DriveArcade command In the RobotContainer.java constructor type: m_drivetrain.setDefaultCommand(new DriveArcade()); Tip Remember to use the light bulb for importing if needed! Example Your full RobotContainer.java should look like this: package frc.robot; import edu.wpi.first.wpilibj.Joystick; import frc.robot.commands.*; import frc.robot.subsystems.*; import edu.wpi.first.wpilibj2.command.Command; /** * This class is where the bulk of the robot should be declared. Since Command-based is a * \"declarative\" paradigm, very little robot logic should actually be handled in the {@link Robot} * periodic methods (other than the scheduler calls). Instead, the structure of the robot * (including subsystems, commands, and button mappings) should be declared here. */ public class RobotContainer { // The robot's subsystems and commands are defined here... public static final Drivetrain m_drivetrain = new Drivetrain(); private final ExampleSubsystem m_exampleSubsystem = new ExampleSubsystem(); private final ExampleCommand m_autoCommand = new ExampleCommand(m_exampleSubsystem); public Joystick driverController = new Joystick(Constants.DRIVER_CONTROLLER); /** * The container for the robot. Contains subsystems, OI devices, and commands. */ public RobotContainer() { // Configure the button bindings configureButtonBindings(); // Set default commands on subsystems m_drivetrain.setDefaultCommand(new DriveArcade()); } /** * Use this method to define your button->command mappings. Buttons can be created by * instantiating a {@link GenericHID} or one of its subclasses ({@link * edu.wpi.first.wpilibj.Joystick} or {@link XboxController}), and then passing it to a * {@link edu.wpi.first.wpilibj2.command.button.JoystickButton}. */ private void configureButtonBindings() { } /** * Use this to pass the autonomous command to the main {@link Robot} class. * * @return the command to run in autonomous */ public Command getAutonomousCommand() { // An ExampleCommand will run in autonomous return m_autoCommand; } }","title":"Using setDefaultCommand"},{"location":"programming/new_project.html","text":"Creating Project Files # Lets get started Overview # Before we can start programing a robot, we must create a new project in Visual Studio Code (VSCode). See table of contents for a breakdown of this section. Creating a New Project # 1) Select the W icon from the tab bar or use the shortcut by holding down Ctrl+Shift+P at the same time. (Replace ctrl with command on macOS) 2) Type and hit enter or select WPILib: Create a new project 3) Click Select a Project Type and choose Template 4) Click Select a Language and choose Java 5) Click Select a project base and choose Command Robot 6) Click Select a new project folder and choose where on your computer you would like to store the program 7) Enter a project name in the text field labeled as such 8) Enter your team number in the text field labeled as such 9) Select Generate Project 10) When prompted \u201cWould you like to open the folder?\u201d , select Yes (Current Window) Default Project Contents # Newly created projects have many files within them. We only care about the contents within the src/main/java/frc/robot/ folder. Everything else can be ignored at this point in the tutorial. Files in the robot folder: ExampleCommand.java An example Command ExampleSubsystem.java An example SubSystem Constants.java (new in 2020, replaces RobotMap.java) Used to map physical ports (digital if using the CAN bus) of sensors or devices connected to the robot and assign them a variable name to be used in other parts of the code. This provides flexibility for changing wiring, makes checking the wiring easier, and significantly reduces the number of magic numbers floating around. Can also be used to store generic constant values as variables in the code Main.java Used for advanced programming Will be ignored/left as is for this tutorial RobotContainer.java (new in 2020, replaces OI.java) Used to declare our subsystem Used to create a connection between commands and Operator Interfaces (OI) such as Joysticks or buttons Robot.java The main class of the robot which is run when a robot boots up. Used to run special methods in the init and period phases of the auto, teleop, and disabled states Example Creating a New Subsystem # 1) Click on the src folder to expand it. 2) Do the same for java then subsystems 3) Right click on subsystems and select Create a new class/ command. 4) Select Subsystem (New) and type your DesiredSubsystemName (i.e. Drivetrain ) for the name and hit enter on your keyboard. 5) Click on the newly created DesiredSubsystemName.java (or Drivetrain.java if you named it that) Adding the Subsystem to RobotContainer.java # Do not forget this step! When a robot program runs on the roboRIO it only runs the main file Robot.java and anything Robot.java links to such as RobotContainer.java. We have created a new subsystem but we have not yet linked it to Robot.java through RobotContainer.java. We must do this for EVERY subsystem we create 1) In RobotContainer.java we will create a new public global constant variable of type DesiredSubsystemName (i.e. Drivetrain ): public static final m_desiredSubsystemName = new DesiredSubsystemName(); (i.e. public static final m_drivetrain = new Drivetrain(); ) Now when we use this subsystem in commands, we must call RobotContainer.m_desiredSubsystemName. to get access to it and its methods. (i.e. RobotContainer.m_drivetrain.someMethod() ) Default Subsystem Contents # Newly created subsystems are empty with the exception of the periodic. Currently there is no constructor, we will create a constructor ourselves later. periodic - a method that will be called periodically (once per robot scheduler run) Useful for adding/updating data to Driverstation dashboard Useful updating variables that need to always up to date Example package frc.robot.subsystems; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class Drivetrain extends SubsystemBase { /** * Creates a new Drivetrain. */ public Drivetrain() { } @Override public void periodic() { // This method will be called once per scheduler run } } Creating a New Command # 1) Click on the src folder to expand it (if it isn't already). 2) Do the same for commands 3) Right click on commands and select Create a new class/ command. 4) Select Command (New) and type DesiredCommandName (i.e. DriveArcade) for the name and hit enter on your keyboard. 5) Click on the newly created DesiredCommandName.java (or DriveArcade.java if you named it that) Default Command Contents # Newly created commands have some predefined methods in them specific for a command based robot. Constructor - Called when the robot program is FIRST loaded. Subsystem dependencies are declared here. initialize() - Called ONCE just before this Command runs the first time. execute() - Called REPEATEDLY when this Command is scheduled to run end() - Called ONCE after isFinished returns true or when another command which requires one or more of the same subsystems is scheduled to run isFinished() - Make this return TRUE when this Command no longer needs to run execute() (initialize always runs once regardless). Example package frc.robot.commands; import edu.wpi.first.wpilibj2.command.CommandBase; public class DriveArcade extends CommandBase { /** * Creates a new DriveArcade. */ public DriveArcade() { // Use addRequirements() here to declare subsystem dependencies. } // Called when the command is initially scheduled. @Override public void initialize() { } // Called every time the scheduler runs while the command is scheduled. @Override public void execute() { } // Called once the command ends or is interrupted. @Override public void end(boolean interrupted) { } // Returns true when the command should end. @Override public boolean isFinished() { return false; } }","title":"Creating Project Files"},{"location":"programming/new_project.html#creating-project-files","text":"Lets get started","title":"Creating Project Files"},{"location":"programming/new_project.html#overview","text":"Before we can start programing a robot, we must create a new project in Visual Studio Code (VSCode). See table of contents for a breakdown of this section.","title":"Overview"},{"location":"programming/new_project.html#creating-a-new-project","text":"1) Select the W icon from the tab bar or use the shortcut by holding down Ctrl+Shift+P at the same time. (Replace ctrl with command on macOS) 2) Type and hit enter or select WPILib: Create a new project 3) Click Select a Project Type and choose Template 4) Click Select a Language and choose Java 5) Click Select a project base and choose Command Robot 6) Click Select a new project folder and choose where on your computer you would like to store the program 7) Enter a project name in the text field labeled as such 8) Enter your team number in the text field labeled as such 9) Select Generate Project 10) When prompted \u201cWould you like to open the folder?\u201d , select Yes (Current Window)","title":"Creating a New Project"},{"location":"programming/new_project.html#default-project-contents","text":"Newly created projects have many files within them. We only care about the contents within the src/main/java/frc/robot/ folder. Everything else can be ignored at this point in the tutorial. Files in the robot folder: ExampleCommand.java An example Command ExampleSubsystem.java An example SubSystem Constants.java (new in 2020, replaces RobotMap.java) Used to map physical ports (digital if using the CAN bus) of sensors or devices connected to the robot and assign them a variable name to be used in other parts of the code. This provides flexibility for changing wiring, makes checking the wiring easier, and significantly reduces the number of magic numbers floating around. Can also be used to store generic constant values as variables in the code Main.java Used for advanced programming Will be ignored/left as is for this tutorial RobotContainer.java (new in 2020, replaces OI.java) Used to declare our subsystem Used to create a connection between commands and Operator Interfaces (OI) such as Joysticks or buttons Robot.java The main class of the robot which is run when a robot boots up. Used to run special methods in the init and period phases of the auto, teleop, and disabled states Example","title":"Default Project Contents"},{"location":"programming/new_project.html#creating-a-new-subsystem","text":"1) Click on the src folder to expand it. 2) Do the same for java then subsystems 3) Right click on subsystems and select Create a new class/ command. 4) Select Subsystem (New) and type your DesiredSubsystemName (i.e. Drivetrain ) for the name and hit enter on your keyboard. 5) Click on the newly created DesiredSubsystemName.java (or Drivetrain.java if you named it that)","title":"Creating a New Subsystem"},{"location":"programming/new_project.html#adding-the-subsystem-to-robotcontainerjava","text":"Do not forget this step! When a robot program runs on the roboRIO it only runs the main file Robot.java and anything Robot.java links to such as RobotContainer.java. We have created a new subsystem but we have not yet linked it to Robot.java through RobotContainer.java. We must do this for EVERY subsystem we create 1) In RobotContainer.java we will create a new public global constant variable of type DesiredSubsystemName (i.e. Drivetrain ): public static final m_desiredSubsystemName = new DesiredSubsystemName(); (i.e. public static final m_drivetrain = new Drivetrain(); ) Now when we use this subsystem in commands, we must call RobotContainer.m_desiredSubsystemName. to get access to it and its methods. (i.e. RobotContainer.m_drivetrain.someMethod() )","title":"Adding the Subsystem to RobotContainer.java"},{"location":"programming/new_project.html#default-subsystem-contents","text":"Newly created subsystems are empty with the exception of the periodic. Currently there is no constructor, we will create a constructor ourselves later. periodic - a method that will be called periodically (once per robot scheduler run) Useful for adding/updating data to Driverstation dashboard Useful updating variables that need to always up to date Example package frc.robot.subsystems; import edu.wpi.first.wpilibj2.command.SubsystemBase; public class Drivetrain extends SubsystemBase { /** * Creates a new Drivetrain. */ public Drivetrain() { } @Override public void periodic() { // This method will be called once per scheduler run } }","title":"Default Subsystem Contents"},{"location":"programming/new_project.html#creating-a-new-command","text":"1) Click on the src folder to expand it (if it isn't already). 2) Do the same for commands 3) Right click on commands and select Create a new class/ command. 4) Select Command (New) and type DesiredCommandName (i.e. DriveArcade) for the name and hit enter on your keyboard. 5) Click on the newly created DesiredCommandName.java (or DriveArcade.java if you named it that)","title":"Creating a New Command"},{"location":"programming/new_project.html#default-command-contents","text":"Newly created commands have some predefined methods in them specific for a command based robot. Constructor - Called when the robot program is FIRST loaded. Subsystem dependencies are declared here. initialize() - Called ONCE just before this Command runs the first time. execute() - Called REPEATEDLY when this Command is scheduled to run end() - Called ONCE after isFinished returns true or when another command which requires one or more of the same subsystems is scheduled to run isFinished() - Make this return TRUE when this Command no longer needs to run execute() (initialize always runs once regardless). Example package frc.robot.commands; import edu.wpi.first.wpilibj2.command.CommandBase; public class DriveArcade extends CommandBase { /** * Creates a new DriveArcade. */ public DriveArcade() { // Use addRequirements() here to declare subsystem dependencies. } // Called when the command is initially scheduled. @Override public void initialize() { } // Called every time the scheduler runs while the command is scheduled. @Override public void execute() { } // Called once the command ends or is interrupted. @Override public void end(boolean interrupted) { } // Returns true when the command should end. @Override public boolean isFinished() { return false; } }","title":"Default Command Contents"},{"location":"programming/pid.html","text":"[WIP] Getting started with PID # This page is currently a work in progress. Check back later","title":"[WIP] Getting started with PID"},{"location":"programming/pid.html#wip-getting-started-with-pid","text":"This page is currently a work in progress. Check back later","title":"[WIP] Getting started with PID"},{"location":"programming/pneumatics.html","text":"[WIP] Using Pneumatics # Check the air pressure Overview # This section will help you learn to program pneumatic for your robot. For this section we are going to create a new subsystem called shooter and add one pneumatic piston (cylinder) which will be used for changing the pitch of the shooter. See table of contents for a breakdown of this section. Background info # What Are Pneumatics # You have probably heard of hydraulics before (which is based on water pressure). Pneumatics are essentially the same but with air pressure. Unlike motors and gears which are commonly infinitely positional, pneumatic cylinders are typically dual-positional or sometimes tri-positional. Pneumatic cylinders are actuated through devices called solenoids. Solenoids are used to control pneumatic pistons (air cylinders) similar to how Talons control motors. What Are Solenoids # Cylinders are actuated with either single solenoids or double solenoids . A single solenoid actuates with one air line, using air to switch to and hold the extended state and releasing air (sometimes paired with a spring) to allow the cylinder to return to the retracted state. A single solenoid valve has one solenoid, and shifts when voltage is CONSTANTLY supplied to that solenoid. When voltage is removed, it shifts back to a \"home\" position. A double solenoid actuates with two air lines, using air to switch and hold states between retracted and extended. A double solenoid has two solenoids, and when voltage is supplied to one (and not the other) the valve shifts. Solenoids are connected to the Pneumatics Control Module (PCM) The PCM is connected to the roboRIO via the CAN bus. Programming Solenoids # For this section we are going to create a new subsystem called shooter and add one pneumatic piston (cylinder) which will be used for changing the pitch of the shooter. See Creating a New Subsystem . What will be added to the Shooter subsystem # 1) Create a new Shooter subsystem. It will be controlled through a double solenoid. We are going to create a DoubleSolenoid named pitchSolenoid. DoubleSolenoids have 2 controllable positions (deployed(forward) and retracted(reverse)). The DoubleSolenoid constructor takes 2 parameters - (new DoubleSolenoid(port1, port2) ) Port 1 and Port 2 refer to Forward control and Reverse control ports on the PCM. Like all ports we use, we will store this in the RobotMap. 2) Create your DoubleSolenoid named pitchSolenoid now using the same technique used to create a talon but replacing Talon with DoubleSolenoid. (For single solenoids just use Solenoid). Example Your full Shooter.java should look like this package frc.robot.subsystems; import edu.wpi.first.wpilibj.DoubleSolenoid; import edu.wpi.first.wpilibj.command.Subsystem; import frc.robot.RobotMap; /** * Add your docs here. */ public class Shooter extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. DoubleSolenoid pitchSolenoid = null; public Shooter() { pitchSolenoid = new DoubleSolenoid(RobotMap.SHOOTER_PITCH_SOLENOID_DEPLOY, RobotMap.SHOOTER_PITCH_SOLENOID_RETRACT); } @Override public void initDefaultCommand() { // Set the default command for a subsystem here. // setDefaultCommand(new MySpecialCommand()); } } The code you typed in Robot.java should be this Outside robotInit public static Shooter m_shooter = null; Inside robotInit m_shooter = new Shooter(); The code you typed in RobotMap.java should be this // Solenoids public static final int SHOOTER_PITCH_SOLENOID_DEPLOY = 0; public static final int SHOOTER_PITCH_SOLENOID_RETRACT = 1; Creating Pitch Up/Down Methods # 1) Create a public void method called pitchUp. 2) Inside type: pitchSolenoid.set(Value.kForward); This sets the value of the solenoid to forward (deployed) !!! Note if you wanted multiple solenoids to deploy at the same time also have them do .set(Value.kForward); 3) Do the same for the pitchDown method but change kForward to kReverse . Example The code you typed should be this public void pitchUp(){ pitchSolenoid.set(Value.kForward); } public void pitchDown(){ pitchSolenoid.set(Value.kForward); } Creating The Commands to Use Pneumatics # Creating Deploy/Retract Instant Commands # Now that we have created the methods we must create commands to use them. Since changing the state of a solenoid only requires us to send a signal once (not continuously) we will create an InstantCommand instead of a Command InstantCommands work the same as regular commands but hide everything except for initialize(). (InstantCommand extends Command) Internally, they set isFinished to return always true so execute never runs. 1) Create a new InstantCommand called ShooterUp Alternatively: Create a regular Command and set isFinished to true 2) In the constructor adds requires(Robot.m_shooter) 3) In initialize() add our newly created method pitchUp method 4) Repeat steps for ShooterDown command but change pitchUp* to **pitchDown Example Your full ShooterUp.java should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.InstantCommand; import frc.robot.Robot; /** * Add your docs here. */ public class ShooterUp extends InstantCommand { /** * Add your docs here. */ public ShooterUp() { super(); // Use requires() here to declare subsystem dependencies // eg. requires(chassis); requires(Robot.m_shooter); } // Called once when the command executes @Override protected void initialize() { Robot.m_shooter.pitchUp(); } } Your full ShooterDown.java should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.InstantCommand; import frc.robot.Robot; /** * Add your docs here. */ public class ShooterDown extends InstantCommand { /** * Add your docs here. */ public ShooterDown() { super(); // Use requires() here to declare subsystem dependencies // eg. requires(chassis); requires(Robot.m_shooter); } // Called once when the command executes @Override protected void initialize() { Robot.m_shooter.pitchDown(); } } Mapping Commands to Buttons # Creating Joystick Buttons # Now that we have created our ShooterUp and ShooterDown commands we need a way to run them. Lets map them to buttons on our controller! 1) Open OI.java 2) Under our created joystick we will create Button variables and assign them to a button on our joystick 3) Type: Button D1 = new JoystickButton(driverController, 1); This creates a new Button named D1 (D representing driverController and 1 representing the button number) and sets it as a JoystickButton on the controller \u2018driverController\u2019 and button value 1 (this can be found in the Driverstation software). 4) Do this for the rest of the buttons on your controller. Example Your full OI.Java should look like this package frc.robot; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.buttons.Button; import edu.wpi.first.wpilibj.buttons.JoystickButton; /** * This class is the glue that binds the controls on the physical operator * interface to the commands and command groups that allow control of the robot. */ public class OI { public Joystick driverController = new Joystick(RobotMap.OI_DRIVER_CONTROLLER); Button D1 = new JoystickButton(driverController, 1); Button D2 = new JoystickButton(driverController, 2); Button D3 = new JoystickButton(driverController, 3); Button D4 = new JoystickButton(driverController, 4); Button D5 = new JoystickButton(driverController, 5); Button D6 = new JoystickButton(driverController, 6); Button D7 = new JoystickButton(driverController, 7); Button D8 = new JoystickButton(driverController, 8); Button D9 = new JoystickButton(driverController, 9); Button D10 = new JoystickButton(driverController, 10); } Mapping Joystick Buttons # Now that we have created the buttons in the code we can map certain commands to them. 1) Create a constructor for OI 2) In the constructor type: D1.whenPressed(new ShooterUp()); This means when the button D1 is pressed it runs the ShooterUp command and deploys our pneumatic piston. There are other types of activations for buttons besides whenPressed like: whenRelease, whileHeld, etc . 3) Create a whenPressed button for ShooterDown as well Example The code you typed should be this public OI(){ D1.whenPressed(new ShooterUp()); D2.whenPressed(new ShooterDown()); } Tip You can change your import at the top of the file from: import frc.robot.commands.ShooterUp; to import frc.robot.commands.*; The asterisk (wildcard) makes it so all files in the .command package (folder) are imported. This way you only have to import once.","title":"[WIP] Using Pneumatics"},{"location":"programming/pneumatics.html#wip-using-pneumatics","text":"Check the air pressure","title":"[WIP] Using Pneumatics"},{"location":"programming/pneumatics.html#overview","text":"This section will help you learn to program pneumatic for your robot. For this section we are going to create a new subsystem called shooter and add one pneumatic piston (cylinder) which will be used for changing the pitch of the shooter. See table of contents for a breakdown of this section.","title":"Overview"},{"location":"programming/pneumatics.html#background-info","text":"","title":"Background info"},{"location":"programming/pneumatics.html#what-are-pneumatics","text":"You have probably heard of hydraulics before (which is based on water pressure). Pneumatics are essentially the same but with air pressure. Unlike motors and gears which are commonly infinitely positional, pneumatic cylinders are typically dual-positional or sometimes tri-positional. Pneumatic cylinders are actuated through devices called solenoids. Solenoids are used to control pneumatic pistons (air cylinders) similar to how Talons control motors.","title":"What Are Pneumatics"},{"location":"programming/pneumatics.html#what-are-solenoids","text":"Cylinders are actuated with either single solenoids or double solenoids . A single solenoid actuates with one air line, using air to switch to and hold the extended state and releasing air (sometimes paired with a spring) to allow the cylinder to return to the retracted state. A single solenoid valve has one solenoid, and shifts when voltage is CONSTANTLY supplied to that solenoid. When voltage is removed, it shifts back to a \"home\" position. A double solenoid actuates with two air lines, using air to switch and hold states between retracted and extended. A double solenoid has two solenoids, and when voltage is supplied to one (and not the other) the valve shifts. Solenoids are connected to the Pneumatics Control Module (PCM) The PCM is connected to the roboRIO via the CAN bus.","title":"What Are Solenoids"},{"location":"programming/pneumatics.html#programming-solenoids","text":"For this section we are going to create a new subsystem called shooter and add one pneumatic piston (cylinder) which will be used for changing the pitch of the shooter. See Creating a New Subsystem .","title":"Programming Solenoids"},{"location":"programming/pneumatics.html#what-will-be-added-to-the-shooter-subsystem","text":"1) Create a new Shooter subsystem. It will be controlled through a double solenoid. We are going to create a DoubleSolenoid named pitchSolenoid. DoubleSolenoids have 2 controllable positions (deployed(forward) and retracted(reverse)). The DoubleSolenoid constructor takes 2 parameters - (new DoubleSolenoid(port1, port2) ) Port 1 and Port 2 refer to Forward control and Reverse control ports on the PCM. Like all ports we use, we will store this in the RobotMap. 2) Create your DoubleSolenoid named pitchSolenoid now using the same technique used to create a talon but replacing Talon with DoubleSolenoid. (For single solenoids just use Solenoid). Example Your full Shooter.java should look like this package frc.robot.subsystems; import edu.wpi.first.wpilibj.DoubleSolenoid; import edu.wpi.first.wpilibj.command.Subsystem; import frc.robot.RobotMap; /** * Add your docs here. */ public class Shooter extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. DoubleSolenoid pitchSolenoid = null; public Shooter() { pitchSolenoid = new DoubleSolenoid(RobotMap.SHOOTER_PITCH_SOLENOID_DEPLOY, RobotMap.SHOOTER_PITCH_SOLENOID_RETRACT); } @Override public void initDefaultCommand() { // Set the default command for a subsystem here. // setDefaultCommand(new MySpecialCommand()); } } The code you typed in Robot.java should be this Outside robotInit public static Shooter m_shooter = null; Inside robotInit m_shooter = new Shooter(); The code you typed in RobotMap.java should be this // Solenoids public static final int SHOOTER_PITCH_SOLENOID_DEPLOY = 0; public static final int SHOOTER_PITCH_SOLENOID_RETRACT = 1;","title":"What will be added to the Shooter subsystem"},{"location":"programming/pneumatics.html#creating-pitch-updown-methods","text":"1) Create a public void method called pitchUp. 2) Inside type: pitchSolenoid.set(Value.kForward); This sets the value of the solenoid to forward (deployed) !!! Note if you wanted multiple solenoids to deploy at the same time also have them do .set(Value.kForward); 3) Do the same for the pitchDown method but change kForward to kReverse . Example The code you typed should be this public void pitchUp(){ pitchSolenoid.set(Value.kForward); } public void pitchDown(){ pitchSolenoid.set(Value.kForward); }","title":"Creating Pitch Up/Down Methods"},{"location":"programming/pneumatics.html#creating-the-commands-to-use-pneumatics","text":"","title":"Creating The Commands to Use Pneumatics"},{"location":"programming/pneumatics.html#creating-deployretract-instant-commands","text":"Now that we have created the methods we must create commands to use them. Since changing the state of a solenoid only requires us to send a signal once (not continuously) we will create an InstantCommand instead of a Command InstantCommands work the same as regular commands but hide everything except for initialize(). (InstantCommand extends Command) Internally, they set isFinished to return always true so execute never runs. 1) Create a new InstantCommand called ShooterUp Alternatively: Create a regular Command and set isFinished to true 2) In the constructor adds requires(Robot.m_shooter) 3) In initialize() add our newly created method pitchUp method 4) Repeat steps for ShooterDown command but change pitchUp* to **pitchDown Example Your full ShooterUp.java should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.InstantCommand; import frc.robot.Robot; /** * Add your docs here. */ public class ShooterUp extends InstantCommand { /** * Add your docs here. */ public ShooterUp() { super(); // Use requires() here to declare subsystem dependencies // eg. requires(chassis); requires(Robot.m_shooter); } // Called once when the command executes @Override protected void initialize() { Robot.m_shooter.pitchUp(); } } Your full ShooterDown.java should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.InstantCommand; import frc.robot.Robot; /** * Add your docs here. */ public class ShooterDown extends InstantCommand { /** * Add your docs here. */ public ShooterDown() { super(); // Use requires() here to declare subsystem dependencies // eg. requires(chassis); requires(Robot.m_shooter); } // Called once when the command executes @Override protected void initialize() { Robot.m_shooter.pitchDown(); } }","title":"Creating Deploy/Retract Instant Commands"},{"location":"programming/pneumatics.html#mapping-commands-to-buttons","text":"","title":"Mapping Commands to Buttons"},{"location":"programming/pneumatics.html#creating-joystick-buttons","text":"Now that we have created our ShooterUp and ShooterDown commands we need a way to run them. Lets map them to buttons on our controller! 1) Open OI.java 2) Under our created joystick we will create Button variables and assign them to a button on our joystick 3) Type: Button D1 = new JoystickButton(driverController, 1); This creates a new Button named D1 (D representing driverController and 1 representing the button number) and sets it as a JoystickButton on the controller \u2018driverController\u2019 and button value 1 (this can be found in the Driverstation software). 4) Do this for the rest of the buttons on your controller. Example Your full OI.Java should look like this package frc.robot; import edu.wpi.first.wpilibj.Joystick; import edu.wpi.first.wpilibj.buttons.Button; import edu.wpi.first.wpilibj.buttons.JoystickButton; /** * This class is the glue that binds the controls on the physical operator * interface to the commands and command groups that allow control of the robot. */ public class OI { public Joystick driverController = new Joystick(RobotMap.OI_DRIVER_CONTROLLER); Button D1 = new JoystickButton(driverController, 1); Button D2 = new JoystickButton(driverController, 2); Button D3 = new JoystickButton(driverController, 3); Button D4 = new JoystickButton(driverController, 4); Button D5 = new JoystickButton(driverController, 5); Button D6 = new JoystickButton(driverController, 6); Button D7 = new JoystickButton(driverController, 7); Button D8 = new JoystickButton(driverController, 8); Button D9 = new JoystickButton(driverController, 9); Button D10 = new JoystickButton(driverController, 10); }","title":"Creating Joystick Buttons"},{"location":"programming/pneumatics.html#mapping-joystick-buttons","text":"Now that we have created the buttons in the code we can map certain commands to them. 1) Create a constructor for OI 2) In the constructor type: D1.whenPressed(new ShooterUp()); This means when the button D1 is pressed it runs the ShooterUp command and deploys our pneumatic piston. There are other types of activations for buttons besides whenPressed like: whenRelease, whileHeld, etc . 3) Create a whenPressed button for ShooterDown as well Example The code you typed should be this public OI(){ D1.whenPressed(new ShooterUp()); D2.whenPressed(new ShooterDown()); } Tip You can change your import at the top of the file from: import frc.robot.commands.ShooterUp; to import frc.robot.commands.*; The asterisk (wildcard) makes it so all files in the .command package (folder) are imported. This way you only have to import once.","title":"Mapping Joystick Buttons"},{"location":"programming/robotpreferences.html","text":"[WIP] Using RobotPreferences # Overview # In this section we will be going over Creating and using RobotPreferences in shuffleboard How to convert encoder counts to inches What Are RobotPreferences # On SmartDashboard or ShuffleBoard there is a widget called Robot Preferences that can store variables that can be quickly changed For example you might have a variable that changes PID values which can be changed from Robot Preferences on SmartDashboard/ShuffleBoard For this section of our tutorial we will create a robot preference called driveEncoderCountsPerFoot Creating RobotPreferences # 1) Create a new empty class called RobotPreferences This is where we store all of our RobotPreferences to access anywhere If we want to use a RobotPreference we call RobotPreferences.preferenceName() 2) Inside the constructor type: public static double driveEncoderCountsPerFoot(){ return Preferences.getInstance().getDouble(\u201cdriveEncoderCountsPerFoot\u201d, 1.0); } The format for creating a RobotPreference is public static variableType preferenceName(){ return Preferences.getInstance().getVariableType(\"preferenceName\", value); Example Your full RobotPreferences.java should look like this package frc.robot; import edu.wpi.first.wpilibj.Preferences; /** * Add your docs here. */ public class RobotPreferences { // Drivetrain /** * Default value is 1.0 */ public static double driveEncoderCountsPerFoot() { return Preferences.getInstance().getDouble(\"driveEncoderCountsPerFoot\", 1.0); } } Creating getDriveEncoderDistance Method # We will use this RobotPreference to help us create a method that can keep track of the distance our robot has driven in inches 1) Create a method called getDriveEncoderDistance inside of Drivetrain 2) Inside type: return (getDriveEncoderCount() / RobotPreferences.driveEncoderCountsPerFoot()) * 12; This will divide the current encoder count by however many counts there are in a foot then multiply that number by 12 to give us the encoder distance in inches Note You may need to invert this value if your encoder counts backward when the robot is driving forward Example The code you typed should be this public double getDriveEncoderDistance() { return (getDriveEncoderCount() / RobotPreferences.driveEncoderCountsPerFoot()) * 12; } 3) Add the method to the update method in Telemetry Using RobotPreferences # After deploying the code to your robot find the RobotPreferences widget and add it to your page Click the add button and enter the string of the RobotPreference and its type (doubles and ints are numbers) If you double click on the preference value you will notice that you can change its value If you change a preference value it will update immediately Tip If you want to save your robot preference values that you've changed make sure you hardcode them in RobotPreferences.java later or take a picture if you want to use them again later Measuring Distance Using Encoders # Right now the encoders tell us distance in terms of encoder counts We will use our driveEncoderCountsPerFoot preference to save how many counts there are when the robot drives 1 foot 1) Move the wheel on your robot with the Drivetrain encoder attached 1 foot or drive your robot 1 foot 2) Read how many counts your encoder has in the Drive Encoder Count window If you want to measure again press the Reset Drive Encoder command button to reset the Drivetrain encoder count 3) Change the value of driveEncoderCountsPerFoot in the widget to this number 4) Reset the Drivetrain encoder and move the wheel 1 foot or drive the robot 1 foot again 5) Make sure your Drive Encoder Distance window reads approximately 12 (this is in inches) If not repeat these steps again 6) Save your RobotPreferences widget with this value 7) Hardcode this value in RobotPreferences.java in the driveEncoderCountsPerFoot method incase you cannot recover your RobotPreferences save","title":"[WIP] Using RobotPreferences"},{"location":"programming/robotpreferences.html#wip-using-robotpreferences","text":"","title":"[WIP] Using RobotPreferences"},{"location":"programming/robotpreferences.html#overview","text":"In this section we will be going over Creating and using RobotPreferences in shuffleboard How to convert encoder counts to inches","title":"Overview"},{"location":"programming/robotpreferences.html#what-are-robotpreferences","text":"On SmartDashboard or ShuffleBoard there is a widget called Robot Preferences that can store variables that can be quickly changed For example you might have a variable that changes PID values which can be changed from Robot Preferences on SmartDashboard/ShuffleBoard For this section of our tutorial we will create a robot preference called driveEncoderCountsPerFoot","title":"What Are RobotPreferences"},{"location":"programming/robotpreferences.html#creating-robotpreferences","text":"1) Create a new empty class called RobotPreferences This is where we store all of our RobotPreferences to access anywhere If we want to use a RobotPreference we call RobotPreferences.preferenceName() 2) Inside the constructor type: public static double driveEncoderCountsPerFoot(){ return Preferences.getInstance().getDouble(\u201cdriveEncoderCountsPerFoot\u201d, 1.0); } The format for creating a RobotPreference is public static variableType preferenceName(){ return Preferences.getInstance().getVariableType(\"preferenceName\", value); Example Your full RobotPreferences.java should look like this package frc.robot; import edu.wpi.first.wpilibj.Preferences; /** * Add your docs here. */ public class RobotPreferences { // Drivetrain /** * Default value is 1.0 */ public static double driveEncoderCountsPerFoot() { return Preferences.getInstance().getDouble(\"driveEncoderCountsPerFoot\", 1.0); } }","title":"Creating RobotPreferences"},{"location":"programming/robotpreferences.html#creating-getdriveencoderdistance-method","text":"We will use this RobotPreference to help us create a method that can keep track of the distance our robot has driven in inches 1) Create a method called getDriveEncoderDistance inside of Drivetrain 2) Inside type: return (getDriveEncoderCount() / RobotPreferences.driveEncoderCountsPerFoot()) * 12; This will divide the current encoder count by however many counts there are in a foot then multiply that number by 12 to give us the encoder distance in inches Note You may need to invert this value if your encoder counts backward when the robot is driving forward Example The code you typed should be this public double getDriveEncoderDistance() { return (getDriveEncoderCount() / RobotPreferences.driveEncoderCountsPerFoot()) * 12; } 3) Add the method to the update method in Telemetry","title":"Creating getDriveEncoderDistance Method"},{"location":"programming/robotpreferences.html#using-robotpreferences","text":"After deploying the code to your robot find the RobotPreferences widget and add it to your page Click the add button and enter the string of the RobotPreference and its type (doubles and ints are numbers) If you double click on the preference value you will notice that you can change its value If you change a preference value it will update immediately Tip If you want to save your robot preference values that you've changed make sure you hardcode them in RobotPreferences.java later or take a picture if you want to use them again later","title":"Using RobotPreferences"},{"location":"programming/robotpreferences.html#measuring-distance-using-encoders","text":"Right now the encoders tell us distance in terms of encoder counts We will use our driveEncoderCountsPerFoot preference to save how many counts there are when the robot drives 1 foot 1) Move the wheel on your robot with the Drivetrain encoder attached 1 foot or drive your robot 1 foot 2) Read how many counts your encoder has in the Drive Encoder Count window If you want to measure again press the Reset Drive Encoder command button to reset the Drivetrain encoder count 3) Change the value of driveEncoderCountsPerFoot in the widget to this number 4) Reset the Drivetrain encoder and move the wheel 1 foot or drive the robot 1 foot again 5) Make sure your Drive Encoder Distance window reads approximately 12 (this is in inches) If not repeat these steps again 6) Save your RobotPreferences widget with this value 7) Hardcode this value in RobotPreferences.java in the driveEncoderCountsPerFoot method incase you cannot recover your RobotPreferences save","title":"Measuring Distance Using Encoders"},{"location":"programming/shuffleboard.html","text":"[WIP] Using Shuffleboard # Overview # In this section we will be going over Using and organizing the Shuffleboard Creating the Telemetry subsystem and adding buttons and data to be viewed in Shuffleboard What is Shuffleboard # Shuffleboard is one of the boards the driverstation displays robot data with It can have widgets like graphs, camera streams, and meters Unique to shuffleboard is the ability to have tabs for different boards What is Telemetry # Telemetry is where we add data to be viewed or command buttons on shuffleboard or smartdashboard For this section of our tutorial we will be adding switch and encoder data to shuffleboard Creating the Telemetry Subsystem # 1) Create a new Subsystem called Telemetry 2) Create a constructor for the Telemetry class The constructor is where we will create buttons for shuffleboard 3) Inside type: SmartDashboard.putData(\u201cReset Drive Encoder\u201d, new DriveResetEncoder()); 4) Create a public method called update This method will run periodically in Robot.java to update sensor data on shuffleboard 5) Inside type: SmartDashboard.putNumber(\u201cDrivetrain Encoder Count\u201d, Robot.m_drivetrain.getDriveEncoderCount()); 6) Do the same for the getDriveEncoderDistance method 7) Try adding the Shooter Subsystem commands and sensor methods where they should be Example Your full Telemetry.java should look like this package frc.robot.subsystems; import edu.wpi.first.wpilibj.command.Subsystem; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import frc.robot.Robot; import frc.robot.commands.*; /** * Add your docs here. */ public class Telemetry extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. public Telemetry() { // Drivetrain SmartDashboard.putData(\"Reset Drive Encoder\", new DriveResetEncoder()); // Shooter SmartDashboard.putData(\"Shooter Up\", new ShooterUp()); SmartDashboard.putData(\"Shooter Down\", new ShooterDown()); SmartDashboard.putData(\"Shooter Up Auto\", new ShooterUpAuto()); } public void update() { // Drivetrain SmartDashboard.putNumber(\"Drive Encoder Count\", Robot.m_drivetrain.getDriveEncoderCount()); // Shooter SmartDashboard.putBoolean(\"Shooter Switch\", Robot.m_shooter.isShooterSwitchClosed()); } @Override public void initDefaultCommand() { // Set the default command for a subsystem here. // setDefaultCommand(new MySpecialCommand()); } } Adding The Telemetry Subsystem to Robot.java # 1) When adding Telemetry to Robot.java , in robotInit we must add Telemetry after the other subsystems This is because the Telemetry subsystem relies on methods that are created in other subsystems before it It can be added before or after OI since they don\u2019t use methods from each other 2) It is important that we add the update method to disabledPeriodic, autonomousPeriodic , and teleopPeriodic so that the Shuffleboard is always being updated with information on our sensors. Example The code you typed before robotInit should be this public static Telemetry m_telemetry; The code you typed in robotInit should be this m_telemetry = new Telemetry(); //This must be initialized after all other robot subsystems The code you typed in disabledPeriodic, autonomousPeriodic , and teleopPeriodic should be this Robot.m_telemetry.update(); Testing Shuffleboard # After saving and deploying code, open the driver station Click the gear on the left side and configure your team number and set the dashboard type to \u201cShuffleBoard\u201d If you are still connected to the robot you should see boxes for the buttons and data we added in Telemetry Using Shuffleboard #","title":"[WIP] Using Shuffleboard"},{"location":"programming/shuffleboard.html#wip-using-shuffleboard","text":"","title":"[WIP] Using Shuffleboard"},{"location":"programming/shuffleboard.html#overview","text":"In this section we will be going over Using and organizing the Shuffleboard Creating the Telemetry subsystem and adding buttons and data to be viewed in Shuffleboard","title":"Overview"},{"location":"programming/shuffleboard.html#what-is-shuffleboard","text":"Shuffleboard is one of the boards the driverstation displays robot data with It can have widgets like graphs, camera streams, and meters Unique to shuffleboard is the ability to have tabs for different boards","title":"What is Shuffleboard"},{"location":"programming/shuffleboard.html#what-is-telemetry","text":"Telemetry is where we add data to be viewed or command buttons on shuffleboard or smartdashboard For this section of our tutorial we will be adding switch and encoder data to shuffleboard","title":"What is Telemetry"},{"location":"programming/shuffleboard.html#creating-the-telemetry-subsystem","text":"1) Create a new Subsystem called Telemetry 2) Create a constructor for the Telemetry class The constructor is where we will create buttons for shuffleboard 3) Inside type: SmartDashboard.putData(\u201cReset Drive Encoder\u201d, new DriveResetEncoder()); 4) Create a public method called update This method will run periodically in Robot.java to update sensor data on shuffleboard 5) Inside type: SmartDashboard.putNumber(\u201cDrivetrain Encoder Count\u201d, Robot.m_drivetrain.getDriveEncoderCount()); 6) Do the same for the getDriveEncoderDistance method 7) Try adding the Shooter Subsystem commands and sensor methods where they should be Example Your full Telemetry.java should look like this package frc.robot.subsystems; import edu.wpi.first.wpilibj.command.Subsystem; import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard; import frc.robot.Robot; import frc.robot.commands.*; /** * Add your docs here. */ public class Telemetry extends Subsystem { // Put methods for controlling this subsystem // here. Call these from Commands. public Telemetry() { // Drivetrain SmartDashboard.putData(\"Reset Drive Encoder\", new DriveResetEncoder()); // Shooter SmartDashboard.putData(\"Shooter Up\", new ShooterUp()); SmartDashboard.putData(\"Shooter Down\", new ShooterDown()); SmartDashboard.putData(\"Shooter Up Auto\", new ShooterUpAuto()); } public void update() { // Drivetrain SmartDashboard.putNumber(\"Drive Encoder Count\", Robot.m_drivetrain.getDriveEncoderCount()); // Shooter SmartDashboard.putBoolean(\"Shooter Switch\", Robot.m_shooter.isShooterSwitchClosed()); } @Override public void initDefaultCommand() { // Set the default command for a subsystem here. // setDefaultCommand(new MySpecialCommand()); } }","title":"Creating the Telemetry Subsystem"},{"location":"programming/shuffleboard.html#adding-the-telemetry-subsystem-to-robotjava","text":"1) When adding Telemetry to Robot.java , in robotInit we must add Telemetry after the other subsystems This is because the Telemetry subsystem relies on methods that are created in other subsystems before it It can be added before or after OI since they don\u2019t use methods from each other 2) It is important that we add the update method to disabledPeriodic, autonomousPeriodic , and teleopPeriodic so that the Shuffleboard is always being updated with information on our sensors. Example The code you typed before robotInit should be this public static Telemetry m_telemetry; The code you typed in robotInit should be this m_telemetry = new Telemetry(); //This must be initialized after all other robot subsystems The code you typed in disabledPeriodic, autonomousPeriodic , and teleopPeriodic should be this Robot.m_telemetry.update();","title":"Adding The Telemetry Subsystem to Robot.java"},{"location":"programming/shuffleboard.html#testing-shuffleboard","text":"After saving and deploying code, open the driver station Click the gear on the left side and configure your team number and set the dashboard type to \u201cShuffleBoard\u201d If you are still connected to the robot you should see boxes for the buttons and data we added in Telemetry","title":"Testing Shuffleboard"},{"location":"programming/shuffleboard.html#using-shuffleboard","text":"","title":"Using Shuffleboard"},{"location":"programming/super_core.html","text":"[WIP] Using SuperCORE # This page is currently a work in progress. Check back later","title":"[WIP] Using SuperCORE"},{"location":"programming/super_core.html#wip-using-supercore","text":"This page is currently a work in progress. Check back later","title":"[WIP] Using SuperCORE"},{"location":"programming/using_sensors.html","text":"[WIP] Using Sensors and Switches # Overview # In this section we will be going over Creating and using a switch in a shooter subsystem Create this subsystem now. (See Creating a New Subsystem ) Creating an encoder in the drivetrain subsystem (See creating a driving robot) What Are Sensors # There are different types of sensors that give feedback on different things (i.e. Encoders measure distance, switches detect contact, gyros give orientation). Most of these interface with the roboRIO through either the DIO, analog input, or custom electronics port. Programming Switches (i.e. Limit Switches) # 1) For this tutorial we are going to add a switch to a shooter subsystem to automatically change the pitch of the shooter Inside the shooter subsystem we are going to create a switch called shooterSwitch It will be created as a DigitalInput The DigitalInput constructor only takes 1 parameter - DigitalInput(port) The port refers to the port numbers on the RoboRIO\u2019s DIO Store the port in Constants Example The code you typed should be this DigitalInput shooterSwitch = null; In the constructor shooterSwitch = new DigitalInput(Constants.SHOOTER_SWITCH); In Constants.Java // Digital Inputs public static final int SHOOTER_SWITCH = 0; Creating isShooterSwitchClosed Method # 1) Create a public boolean method called isShooterSwitchClosed This method will tell us when the shooter switch is pressed 2) Inside type: return shooterSwitch.get(); Switches have 2 states: open and closed. Make sure you know which is true or false or you may have to invert the switch by rewiring or using the ! operator Example Your isShooterSwitchClosed() should look like this public boolean isShooterSwitchClosed() { return shooterSwitch.get(); } Full Shooter.java Example package frc.robot.subsystems; import edu.wpi.first.wpilibj.DigitalInput; import edu.wpi.first.wpilibj2.command.SubsystemBase; import frc.robot.Constants; public class Shooter extends SubsystemBase { /** * Creates a new Shooter. */ DigitalInput shooterSwitch = null; public Shooter() { shooterSwitch = new DigitalInput(Constants.SHOOTER_SWITCH); } public boolean isShooterSwitchClosed() { return shooterSwitch.get(); } @Override public void periodic() { // This method will be called once per scheduler run } } Creating ShooterUpAuto Command # We will create a command that gives an example of how a Shooter switch may be used 1) For this tutorial we will use the switch to create a button that automatically pitches the shooter up after the switch is pressed 2) Create a new command called ShooterUpAuto 3) In the constructor add requires(Robot.m_Shooter) 4) In isFinished return our isShooterSwitchClosed method we will not put anything in initialize or execute because we don't want anything to happen until the switch is closed 5) In end add our pitchUp method we will not put end in interrupted either because we only want to change the pitch of the shooter if the switch is closed Example Your full ShooterUpAuto.java should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.Command; import frc.robot.Robot; public class ShooterUpAuto extends Command { public ShooterUpAuto() { // Use requires() here to declare subsystem dependencies // eg. requires(chassis); requires(Robot.m_shooter); } // Called just before this Command runs the first time @Override protected void initialize() { } // Called repeatedly when this Command is scheduled to run @Override protected void execute() { } // Make this return true when this Command no longer needs to run execute() @Override protected boolean isFinished() { return Robot.m_shooter.isShooterSwitchClosed(); } // Called once after isFinished returns true @Override protected void end() { Robot.m_shooter.pitchUp(); } // Called when another command which requires one or more of the same // subsystems is scheduled to run @Override protected void interrupted() { } } Mapping ShooterAutoUpCommand # To be able to test our command right now we can map it to a joystick button like we did our other Shooter commands It would be best to make it a whenPressed or whileHeld button whileHeld will run normally while the button is being held and be interrupted when released Example The code you typed should be this D3.whenPressed(new ShooterUpAuto()); Or this D3.whileHeld(new ShooterUpAuto()); Programming Encoders # 1) For this tutorial we are going to add a encoder to the Drivetrain subsystem to keep track of the distance the robot has driven 2) Inside the Drivetrain subsystem we are going to create an encoder called driveEncoder It will be created as an Encoder The Encoder constructor takes 2 parameters - (new Encoder(port1,port2)) These are DIO ports on the RoboRIO Store ports in Constants as DRIVE_ENCODER_A and B Example The code you typed outside the constructor should be this Encoder driveEncoder = null; Inside the constructor driveEncoder = new Encoder(Constants.DRIVETRAIN_ENCODER_A, Constants.DRIVETRAIN_ENCODER_B); Creating Drive Encoder Methods # 1) Create a public double method called getDriveEncoderCount 2) Inside type: return driveEncoder.get(); Encoders will return counts as an int Depending which direction the encoder shaft rotates the value will increase or decrease 3) Create a public method called resetDriveEncoderCount 4) Inside type: driveEncoder.reset(); This method will reset the drive encoder to zero which is useful for autonomous or when we use the robot as a ruler Example The code you typed should be this public double getDriveEncoderCount() { return driveEncoder.get(); } public void resetDriveEncoder() { driveEncoder.reset(); } Creating ResetDriveEncoder InstantCommand # We need to create a command to use the resetDriveEncoder method since it\u2019s a void method We will create a InstantCommand since we will only use it to reset the drive encoder 1) Create a new InstantCommand called DriveResetEncoder 2) In the constructor add requires(Robot.m_drivetrain) 3) In initialize() add our resetDriveEncoder method Example Your full DriveResetEncoder command should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.InstantCommand; import frc.robot.Robot; /** * Add your docs here. */ public class DriveResetEncoder extends InstantCommand { /** * Add your docs here. */ public DriveResetEncoder() { super(); // Use requires() here to declare subsystem dependencies // eg. requires(chassis); requires(Robot.m_drivetrain); } // Called once when the command executes @Override protected void initialize() { Robot.m_drivetrain.resetDriveEncoder(); } }","title":"[WIP] Using Sensors and Switches"},{"location":"programming/using_sensors.html#wip-using-sensors-and-switches","text":"","title":"[WIP] Using Sensors and Switches"},{"location":"programming/using_sensors.html#overview","text":"In this section we will be going over Creating and using a switch in a shooter subsystem Create this subsystem now. (See Creating a New Subsystem ) Creating an encoder in the drivetrain subsystem (See creating a driving robot)","title":"Overview"},{"location":"programming/using_sensors.html#what-are-sensors","text":"There are different types of sensors that give feedback on different things (i.e. Encoders measure distance, switches detect contact, gyros give orientation). Most of these interface with the roboRIO through either the DIO, analog input, or custom electronics port.","title":"What Are Sensors"},{"location":"programming/using_sensors.html#programming-switches-ie-limit-switches","text":"1) For this tutorial we are going to add a switch to a shooter subsystem to automatically change the pitch of the shooter Inside the shooter subsystem we are going to create a switch called shooterSwitch It will be created as a DigitalInput The DigitalInput constructor only takes 1 parameter - DigitalInput(port) The port refers to the port numbers on the RoboRIO\u2019s DIO Store the port in Constants Example The code you typed should be this DigitalInput shooterSwitch = null; In the constructor shooterSwitch = new DigitalInput(Constants.SHOOTER_SWITCH); In Constants.Java // Digital Inputs public static final int SHOOTER_SWITCH = 0;","title":"Programming Switches (i.e. Limit Switches)"},{"location":"programming/using_sensors.html#creating-isshooterswitchclosed-method","text":"1) Create a public boolean method called isShooterSwitchClosed This method will tell us when the shooter switch is pressed 2) Inside type: return shooterSwitch.get(); Switches have 2 states: open and closed. Make sure you know which is true or false or you may have to invert the switch by rewiring or using the ! operator Example Your isShooterSwitchClosed() should look like this public boolean isShooterSwitchClosed() { return shooterSwitch.get(); } Full Shooter.java Example package frc.robot.subsystems; import edu.wpi.first.wpilibj.DigitalInput; import edu.wpi.first.wpilibj2.command.SubsystemBase; import frc.robot.Constants; public class Shooter extends SubsystemBase { /** * Creates a new Shooter. */ DigitalInput shooterSwitch = null; public Shooter() { shooterSwitch = new DigitalInput(Constants.SHOOTER_SWITCH); } public boolean isShooterSwitchClosed() { return shooterSwitch.get(); } @Override public void periodic() { // This method will be called once per scheduler run } }","title":"Creating isShooterSwitchClosed Method"},{"location":"programming/using_sensors.html#creating-shooterupauto-command","text":"We will create a command that gives an example of how a Shooter switch may be used 1) For this tutorial we will use the switch to create a button that automatically pitches the shooter up after the switch is pressed 2) Create a new command called ShooterUpAuto 3) In the constructor add requires(Robot.m_Shooter) 4) In isFinished return our isShooterSwitchClosed method we will not put anything in initialize or execute because we don't want anything to happen until the switch is closed 5) In end add our pitchUp method we will not put end in interrupted either because we only want to change the pitch of the shooter if the switch is closed Example Your full ShooterUpAuto.java should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.Command; import frc.robot.Robot; public class ShooterUpAuto extends Command { public ShooterUpAuto() { // Use requires() here to declare subsystem dependencies // eg. requires(chassis); requires(Robot.m_shooter); } // Called just before this Command runs the first time @Override protected void initialize() { } // Called repeatedly when this Command is scheduled to run @Override protected void execute() { } // Make this return true when this Command no longer needs to run execute() @Override protected boolean isFinished() { return Robot.m_shooter.isShooterSwitchClosed(); } // Called once after isFinished returns true @Override protected void end() { Robot.m_shooter.pitchUp(); } // Called when another command which requires one or more of the same // subsystems is scheduled to run @Override protected void interrupted() { } }","title":"Creating ShooterUpAuto Command"},{"location":"programming/using_sensors.html#mapping-shooterautoupcommand","text":"To be able to test our command right now we can map it to a joystick button like we did our other Shooter commands It would be best to make it a whenPressed or whileHeld button whileHeld will run normally while the button is being held and be interrupted when released Example The code you typed should be this D3.whenPressed(new ShooterUpAuto()); Or this D3.whileHeld(new ShooterUpAuto());","title":"Mapping ShooterAutoUpCommand"},{"location":"programming/using_sensors.html#programming-encoders","text":"1) For this tutorial we are going to add a encoder to the Drivetrain subsystem to keep track of the distance the robot has driven 2) Inside the Drivetrain subsystem we are going to create an encoder called driveEncoder It will be created as an Encoder The Encoder constructor takes 2 parameters - (new Encoder(port1,port2)) These are DIO ports on the RoboRIO Store ports in Constants as DRIVE_ENCODER_A and B Example The code you typed outside the constructor should be this Encoder driveEncoder = null; Inside the constructor driveEncoder = new Encoder(Constants.DRIVETRAIN_ENCODER_A, Constants.DRIVETRAIN_ENCODER_B);","title":"Programming Encoders"},{"location":"programming/using_sensors.html#creating-drive-encoder-methods","text":"1) Create a public double method called getDriveEncoderCount 2) Inside type: return driveEncoder.get(); Encoders will return counts as an int Depending which direction the encoder shaft rotates the value will increase or decrease 3) Create a public method called resetDriveEncoderCount 4) Inside type: driveEncoder.reset(); This method will reset the drive encoder to zero which is useful for autonomous or when we use the robot as a ruler Example The code you typed should be this public double getDriveEncoderCount() { return driveEncoder.get(); } public void resetDriveEncoder() { driveEncoder.reset(); }","title":"Creating Drive Encoder Methods"},{"location":"programming/using_sensors.html#creating-resetdriveencoder-instantcommand","text":"We need to create a command to use the resetDriveEncoder method since it\u2019s a void method We will create a InstantCommand since we will only use it to reset the drive encoder 1) Create a new InstantCommand called DriveResetEncoder 2) In the constructor add requires(Robot.m_drivetrain) 3) In initialize() add our resetDriveEncoder method Example Your full DriveResetEncoder command should look like this package frc.robot.commands; import edu.wpi.first.wpilibj.command.InstantCommand; import frc.robot.Robot; /** * Add your docs here. */ public class DriveResetEncoder extends InstantCommand { /** * Add your docs here. */ public DriveResetEncoder() { super(); // Use requires() here to declare subsystem dependencies // eg. requires(chassis); requires(Robot.m_drivetrain); } // Called once when the command executes @Override protected void initialize() { Robot.m_drivetrain.resetDriveEncoder(); } }","title":"Creating ResetDriveEncoder InstantCommand"},{"location":"setup/3rd_party_libs.html","text":"","title":"3rd party libs"},{"location":"setup/imaging_roboRIO.html","text":"Imaging the roboRIO # Flashing the firmware Overview # Before we can deploy code to the robot, we must flash a software image on to the roboRIO and possibly update the firmware. This can be accomplished with the roboRIO imaging tool: IMPORTANT NOTE The FRC Game Tools (Windows Only) need to be installed to access the roboRIO imaging tool. Using the roboRIO imaging tool # Following the instructions linked below will explain how to image/update the roboRIO. For Windows ONLY: Official 2020 FRC roboRIO Imaging guide (Windows only)","title":"Imaging the roboRIO"},{"location":"setup/imaging_roboRIO.html#imaging-the-roborio","text":"Flashing the firmware","title":"Imaging the roboRIO"},{"location":"setup/imaging_roboRIO.html#overview","text":"Before we can deploy code to the robot, we must flash a software image on to the roboRIO and possibly update the firmware. This can be accomplished with the roboRIO imaging tool: IMPORTANT NOTE The FRC Game Tools (Windows Only) need to be installed to access the roboRIO imaging tool.","title":"Overview"},{"location":"setup/imaging_roboRIO.html#using-the-roborio-imaging-tool","text":"Following the instructions linked below will explain how to image/update the roboRIO. For Windows ONLY: Official 2020 FRC roboRIO Imaging guide (Windows only)","title":"Using the roboRIO imaging tool"},{"location":"setup/install_other.html","text":"Installing Other Software # Overview # There are other pieces of software necessary for programming a robot depending on your circumstances. If you are using Talon or Spark Max motor controllers, those have their own debugging tools that are necessary. If you plan on motion profiling for your drivetrain in autonomous, (check our [motion profiling tutorial] for more on that) you will need to download a pathing software as well. See table of contents for a breakdown of this section. Installing Pheonix software (for talon motor controllers) # If you are using Talons on your robot, the Pheonix tuner software is a must have. It allows you to deploy software updates, debug/test your talons, and name/organize your talons. Installing the Pheonix suite # For Windows ONLY: Download link for CTRE Framework (next to 'Installer') Installing Spark Max Client # If you are using Spark Max motor controllers on your robot, the Spark Max tuner software is a must have. It allows you to deploy software updates, debug/test your Spark maxes, and name/organize your Spark Maxes. Installing the Spark Max Client # For Windows ONLY: Download Spark Max Client (click 'Download Latest SPARK MAX Client) Installing PathPlanner # In order to utilize motion profiling on a drivetrain, it is extremely helpful to have a tool such as Pathplanner. Pathplanner allows for seamless creation and deployment of motion profiles from your laptop to your robot, and has a nice interface for doing so. There are other available tools we may add in the future if pathplanner does not suit your needs, but we at 3255 found it comprehensive for our motion profiling needs (note, this software is only for driving in autonomous, it is likely not useful for other mechanisms). Installing Pathplanner # For Windows ONLY: Pathplanner Github (to install, click on the latest version under releases on the right)","title":"Installing Other Software"},{"location":"setup/install_other.html#installing-other-software","text":"","title":"Installing Other Software"},{"location":"setup/install_other.html#overview","text":"There are other pieces of software necessary for programming a robot depending on your circumstances. If you are using Talon or Spark Max motor controllers, those have their own debugging tools that are necessary. If you plan on motion profiling for your drivetrain in autonomous, (check our [motion profiling tutorial] for more on that) you will need to download a pathing software as well. See table of contents for a breakdown of this section.","title":"Overview"},{"location":"setup/install_other.html#installing-pheonix-software-for-talon-motor-controllers","text":"If you are using Talons on your robot, the Pheonix tuner software is a must have. It allows you to deploy software updates, debug/test your talons, and name/organize your talons.","title":"Installing Pheonix software (for talon motor controllers)"},{"location":"setup/install_other.html#installing-the-pheonix-suite","text":"For Windows ONLY: Download link for CTRE Framework (next to 'Installer')","title":"Installing the Pheonix suite"},{"location":"setup/install_other.html#installing-spark-max-client","text":"If you are using Spark Max motor controllers on your robot, the Spark Max tuner software is a must have. It allows you to deploy software updates, debug/test your Spark maxes, and name/organize your Spark Maxes.","title":"Installing Spark Max Client"},{"location":"setup/install_other.html#installing-the-spark-max-client","text":"For Windows ONLY: Download Spark Max Client (click 'Download Latest SPARK MAX Client)","title":"Installing the Spark Max Client"},{"location":"setup/install_other.html#installing-pathplanner","text":"In order to utilize motion profiling on a drivetrain, it is extremely helpful to have a tool such as Pathplanner. Pathplanner allows for seamless creation and deployment of motion profiles from your laptop to your robot, and has a nice interface for doing so. There are other available tools we may add in the future if pathplanner does not suit your needs, but we at 3255 found it comprehensive for our motion profiling needs (note, this software is only for driving in autonomous, it is likely not useful for other mechanisms).","title":"Installing PathPlanner"},{"location":"setup/install_other.html#installing-pathplanner_1","text":"For Windows ONLY: Pathplanner Github (to install, click on the latest version under releases on the right)","title":"Installing Pathplanner"},{"location":"setup/install_software.html","text":"Installing Necessary Software # Lets get started Overview # Before we can start programing a robot we must install the necessary software for programming and driving the robot. See table of contents for a breakdown of this section. Tip You can install both the Development Tools and the FRC Game Tools on the same computer or separate computers. However many teams (3255 included) have a development laptop (with both) and a dedicated driverstation laptop (with only the FRC Game Tools) that often stays disconnected from the internet. Installing Java Development Tools # If all you are doing is writing and deploying code to a robot, all you need are the development tools. Following the instructions linked below will get you set up with a development environment and get you setup with all the tools necessary to program a robot. Installing Java and Visual Studio Code (VSCode) # For Windows, macOS, or Linux: Official FRC installation guide (Windows, macOS, or Linux) IMPORTANT NOTE These tools only allow you to program and deploy code to an already imaged roboRIO. They do not allow you to drive the robot or image/update the roboRIO. To accomplish those tasks you must install the FRC Game Tools . Installing the FRC Game Tools # If all you are doing is driving an already programmed robot or imaging/updating the roboRIO all you need is the FRC Game Tools. Following the instructions linked below will get you set up with the tools to drive the robot and image/update the roboRIO. Installing the Driverstation software and roboRIO imaging tool # For Windows ONLY: Official FRC installation guide (Windows only) IMPORTANT NOTE These tools only allow you to drive the robot and image/update a roboRIO. They do not allow you to program the robot. To accomplish those tasks you must install the Java Development Tools . Installing the FRC Radio Configuration Utility # In order to enable wireless connectivity to the robot outside of FRC events or to allow connectivity to other network attached devices (i.e.Limelight Vision Camera), you must configure the robot's radio. Following the instructions linked below will get you set up with the Radio Configuration Utility and how to program the radio. Installing the Radio Configuration Utility and Programming the Radio # For Windows ONLY: Official FRC Radio Configuration Utility and Use guide (Windows only)","title":"Installing Necessary Software"},{"location":"setup/install_software.html#installing-necessary-software","text":"Lets get started","title":"Installing Necessary Software"},{"location":"setup/install_software.html#overview","text":"Before we can start programing a robot we must install the necessary software for programming and driving the robot. See table of contents for a breakdown of this section. Tip You can install both the Development Tools and the FRC Game Tools on the same computer or separate computers. However many teams (3255 included) have a development laptop (with both) and a dedicated driverstation laptop (with only the FRC Game Tools) that often stays disconnected from the internet.","title":"Overview"},{"location":"setup/install_software.html#installing-java-development-tools","text":"If all you are doing is writing and deploying code to a robot, all you need are the development tools. Following the instructions linked below will get you set up with a development environment and get you setup with all the tools necessary to program a robot.","title":"Installing Java Development Tools"},{"location":"setup/install_software.html#installing-java-and-visual-studio-code-vscode","text":"For Windows, macOS, or Linux: Official FRC installation guide (Windows, macOS, or Linux) IMPORTANT NOTE These tools only allow you to program and deploy code to an already imaged roboRIO. They do not allow you to drive the robot or image/update the roboRIO. To accomplish those tasks you must install the FRC Game Tools .","title":"Installing Java and Visual Studio Code (VSCode)"},{"location":"setup/install_software.html#installing-the-frc-game-tools","text":"If all you are doing is driving an already programmed robot or imaging/updating the roboRIO all you need is the FRC Game Tools. Following the instructions linked below will get you set up with the tools to drive the robot and image/update the roboRIO.","title":"Installing the FRC Game Tools"},{"location":"setup/install_software.html#installing-the-driverstation-software-and-roborio-imaging-tool","text":"For Windows ONLY: Official FRC installation guide (Windows only) IMPORTANT NOTE These tools only allow you to drive the robot and image/update a roboRIO. They do not allow you to program the robot. To accomplish those tasks you must install the Java Development Tools .","title":"Installing the Driverstation software and roboRIO imaging tool"},{"location":"setup/install_software.html#installing-the-frc-radio-configuration-utility","text":"In order to enable wireless connectivity to the robot outside of FRC events or to allow connectivity to other network attached devices (i.e.Limelight Vision Camera), you must configure the robot's radio. Following the instructions linked below will get you set up with the Radio Configuration Utility and how to program the radio.","title":"Installing the FRC Radio Configuration Utility"},{"location":"setup/install_software.html#installing-the-radio-configuration-utility-and-programming-the-radio","text":"For Windows ONLY: Official FRC Radio Configuration Utility and Use guide (Windows only)","title":"Installing the Radio Configuration Utility and Programming the Radio"},{"location":"version_control/github.html","text":"[WIP] Using GitHub # This page is currently a work in progress. Check back later","title":"[WIP] Using GitHub"},{"location":"version_control/github.html#wip-using-github","text":"This page is currently a work in progress. Check back later","title":"[WIP] Using GitHub"}]} \ No newline at end of file diff --git a/search/worker.js b/search/worker.js new file mode 100644 index 0000000..8628dbc --- /dev/null +++ b/search/worker.js @@ -0,0 +1,133 @@ +var base_path = 'function' === typeof importScripts ? '.' : '/search/'; +var allowSearch = false; +var index; +var documents = {}; +var lang = ['en']; +var data; + +function getScript(script, callback) { + console.log('Loading script: ' + script); + $.getScript(base_path + script).done(function () { + callback(); + }).fail(function (jqxhr, settings, exception) { + console.log('Error: ' + exception); + }); +} + +function getScriptsInOrder(scripts, callback) { + if (scripts.length === 0) { + callback(); + return; + } + getScript(scripts[0], function() { + getScriptsInOrder(scripts.slice(1), callback); + }); +} + +function loadScripts(urls, callback) { + if( 'function' === typeof importScripts ) { + importScripts.apply(null, urls); + callback(); + } else { + getScriptsInOrder(urls, callback); + } +} + +function onJSONLoaded () { + data = JSON.parse(this.responseText); + var scriptsToLoad = ['lunr.js']; + if (data.config && data.config.lang && data.config.lang.length) { + lang = data.config.lang; + } + if (lang.length > 1 || lang[0] !== "en") { + scriptsToLoad.push('lunr.stemmer.support.js'); + if (lang.length > 1) { + scriptsToLoad.push('lunr.multi.js'); + } + if (lang.includes("ja") || lang.includes("jp")) { + scriptsToLoad.push('tinyseg.js'); + } + for (var i=0; i < lang.length; i++) { + if (lang[i] != 'en') { + scriptsToLoad.push(['lunr', lang[i], 'js'].join('.')); + } + } + } + loadScripts(scriptsToLoad, onScriptsLoaded); +} + +function onScriptsLoaded () { + console.log('All search scripts loaded, building Lunr index...'); + if (data.config && data.config.separator && data.config.separator.length) { + lunr.tokenizer.separator = new RegExp(data.config.separator); + } + + if (data.index) { + index = lunr.Index.load(data.index); + data.docs.forEach(function (doc) { + documents[doc.location] = doc; + }); + console.log('Lunr pre-built index loaded, search ready'); + } else { + index = lunr(function () { + if (lang.length === 1 && lang[0] !== "en" && lunr[lang[0]]) { + this.use(lunr[lang[0]]); + } else if (lang.length > 1) { + this.use(lunr.multiLanguage.apply(null, lang)); // spread operator not supported in all browsers: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator#Browser_compatibility + } + this.field('title'); + this.field('text'); + this.ref('location'); + + for (var i=0; i < data.docs.length; i++) { + var doc = data.docs[i]; + this.add(doc); + documents[doc.location] = doc; + } + }); + console.log('Lunr index built, search ready'); + } + allowSearch = true; + postMessage({config: data.config}); + postMessage({allowSearch: allowSearch}); +} + +function init () { + var oReq = new XMLHttpRequest(); + oReq.addEventListener("load", onJSONLoaded); + var index_path = base_path + '/search_index.json'; + if( 'function' === typeof importScripts ){ + index_path = 'search_index.json'; + } + oReq.open("GET", index_path); + oReq.send(); +} + +function search (query) { + if (!allowSearch) { + console.error('Assets for search still loading'); + return; + } + + var resultDocuments = []; + var results = index.search(query); + for (var i=0; i < results.length; i++){ + var result = results[i]; + doc = documents[result.ref]; + doc.summary = doc.text.substring(0, 200); + resultDocuments.push(doc); + } + return resultDocuments; +} + +if( 'function' === typeof importScripts ) { + onmessage = function (e) { + if (e.data.init) { + init(); + } else if (e.data.query) { + postMessage({ results: search(e.data.query) }); + } else { + console.error("Worker - Unrecognized message: " + e); + } + }; +} diff --git a/setup/3rd_party_libs.html b/setup/3rd_party_libs.html new file mode 100644 index 0000000..1dcfb06 --- /dev/null +++ b/setup/3rd_party_libs.html @@ -0,0 +1,20 @@ + 3rd party libs - FRC Java Programming
\ No newline at end of file diff --git a/setup/imaging_roboRIO.html b/setup/imaging_roboRIO.html new file mode 100644 index 0000000..2d24ef5 --- /dev/null +++ b/setup/imaging_roboRIO.html @@ -0,0 +1,20 @@ + Imaging the roboRIO - FRC Java Programming

Imaging the roboRIO#

Flashing the firmware

roboRIO

Overview#

Before we can deploy code to the robot, we must flash a software image on to the roboRIO and possibly update the firmware. This can be accomplished with the roboRIO imaging tool:

IMPORTANT NOTE

The FRC Game Tools (Windows Only) need to be installed to access the roboRIO imaging tool.


Using the roboRIO imaging tool#

Following the instructions linked below will explain how to image/update the roboRIO.

For Windows ONLY:

Official 2020 FRC roboRIO Imaging guide (Windows only)

\ No newline at end of file diff --git a/setup/install_other.html b/setup/install_other.html new file mode 100644 index 0000000..29d7247 --- /dev/null +++ b/setup/install_other.html @@ -0,0 +1,20 @@ + Installing Other Software - FRC Java Programming

Installing Other Software#

Overview#

There are other pieces of software necessary for programming a robot depending on your circumstances. If you are using Talon or Spark Max motor controllers, those have their own debugging tools that are necessary. If you plan on motion profiling for your drivetrain in autonomous, (check our [motion profiling tutorial] for more on that) you will need to download a pathing software as well.

See table of contents for a breakdown of this section.

Installing Pheonix software (for talon motor controllers)#

If you are using Talons on your robot, the Pheonix tuner software is a must have. It allows you to deploy software updates, debug/test your talons, and name/organize your talons.

Installing the Pheonix suite#

For Windows ONLY:

Download link for CTRE Framework (next to 'Installer')


Installing Spark Max Client#

If you are using Spark Max motor controllers on your robot, the Spark Max tuner software is a must have. It allows you to deploy software updates, debug/test your Spark maxes, and name/organize your Spark Maxes.

Installing the Spark Max Client#

For Windows ONLY:

Download Spark Max Client (click 'Download Latest SPARK MAX Client)


Installing PathPlanner#

In order to utilize motion profiling on a drivetrain, it is extremely helpful to have a tool such as Pathplanner. Pathplanner allows for seamless creation and deployment of motion profiles from your laptop to your robot, and has a nice interface for doing so. There are other available tools we may add in the future if pathplanner does not suit your needs, but we at 3255 found it comprehensive for our motion profiling needs (note, this software is only for driving in autonomous, it is likely not useful for other mechanisms).

Installing Pathplanner#

For Windows ONLY:

Pathplanner Github (to install, click on the latest version under releases on the right)

\ No newline at end of file diff --git a/setup/install_software.html b/setup/install_software.html new file mode 100644 index 0000000..c11c208 --- /dev/null +++ b/setup/install_software.html @@ -0,0 +1,20 @@ + Installing Necessary Software - FRC Java Programming

Installing Necessary Software#

Lets get started

NI VSCode

Overview#

Before we can start programing a robot we must install the necessary software for programming and driving the robot.

See table of contents for a breakdown of this section.

Tip

You can install both the Development Tools and the FRC Game Tools on the same computer or separate computers. However many teams (3255 included) have a development laptop (with both) and a dedicated driverstation laptop (with only the FRC Game Tools) that often stays disconnected from the internet.


Installing Java Development Tools#

If all you are doing is writing and deploying code to a robot, all you need are the development tools. Following the instructions linked below will get you set up with a development environment and get you setup with all the tools necessary to program a robot.

Installing Java and Visual Studio Code (VSCode)#

For Windows, macOS, or Linux:

Official FRC installation guide (Windows, macOS, or Linux)

IMPORTANT NOTE

These tools only allow you to program and deploy code to an already imaged roboRIO. They do not allow you to drive the robot or image/update the roboRIO. To accomplish those tasks you must install the FRC Game Tools.


Installing the FRC Game Tools#

If all you are doing is driving an already programmed robot or imaging/updating the roboRIO all you need is the FRC Game Tools. Following the instructions linked below will get you set up with the tools to drive the robot and image/update the roboRIO.

Installing the Driverstation software and roboRIO imaging tool#

For Windows ONLY:

Official FRC installation guide (Windows only)

IMPORTANT NOTE

These tools only allow you to drive the robot and image/update a roboRIO. They do not allow you to program the robot. To accomplish those tasks you must install the Java Development Tools.


Installing the FRC Radio Configuration Utility#

In order to enable wireless connectivity to the robot outside of FRC events or to allow connectivity to other network attached devices (i.e.Limelight Vision Camera), you must configure the robot's radio. Following the instructions linked below will get you set up with the Radio Configuration Utility and how to program the radio.

Installing the Radio Configuration Utility and Programming the Radio#

For Windows ONLY:

Official FRC Radio Configuration Utility and Use guide (Windows only)

\ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..0f8724e --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 0000000..5b5a76b Binary files /dev/null and b/sitemap.xml.gz differ diff --git a/version_control/github.html b/version_control/github.html new file mode 100644 index 0000000..73a3aa6 --- /dev/null +++ b/version_control/github.html @@ -0,0 +1,33 @@ + [WIP] Using GitHub - FRC Java Programming \ No newline at end of file diff --git a/why_software.html b/why_software.html new file mode 100644 index 0000000..e43ece2 --- /dev/null +++ b/why_software.html @@ -0,0 +1,20 @@ + Why software - FRC Java Programming

What is software#

What is software? Why should you care?

software

Where is software#

  • Everywhere!
  • Games, apps, websites
  • All smart devices
  • It’s the stuff on your phone and computer that you see and interact with
  • It is also handling complex parts behind what you see and interact with
  • Even things that didn't before have software now
    • Cars, Planes, Ovens, Light Bulbs, Garbage Trucks, Children's Toys, etc.

smart-light


But what is it#

  • It’s the language of computers!
  • Logical building blocks for controlling the devices around us
  • Software makes devices smart
  • Software allows us to automate things

automation

Why should you do software#

  • Learn Creative Problem Solving
  • Peek be hind the curtain of what makes technology tick
    • Better understanding and appreciation of technology
  • Programming is the FUTURE
  • Your imagination is your only limitation
  • Low barrier to entry
    • Only need a computing device (can be a cheap $35 computer or even your phone)
  • Lucrative job opportunities
    • Many job opportunities from Game Development to App Development to Robotics


\ No newline at end of file