{
    "componentChunkName": "component---src-components-page-template-jsx",
    "path": "/guides/main/nuclear-tutorial",
    "result": {"data":{"mdx":{"id":"7b6b366b-b57f-564a-8de2-26a3650f5337","body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"section\": \"Guides\",\n  \"chapter\": \"Main Codebase\",\n  \"title\": \"NUClear\",\n  \"description\": \"Creating a NUClear module.\",\n  \"slug\": \"/guides/main/nuclear-tutorial\",\n  \"authors\": [\"Ysobel Sims (@ysims)\"]\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, _excluded);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", {\n    \"id\": \"generating-a-module\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h2\",\n    \"href\": \"#generating-a-module\",\n    \"aria-label\": \"generating a module permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Generating a Module\"), mdx(\"p\", null, \"The first step on your NUClear journey is generating a module. This will give you a nice playground to try out NUClear functionality. Before you can generate a module, you will need to set up the codebase.\"), mdx(\"h3\", {\n    \"id\": \"setting-up-the-codebase\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#setting-up-the-codebase\",\n    \"aria-label\": \"setting up the codebase permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Setting up the Codebase\"), mdx(\"p\", null, \"Setting up the codebase is vital! Most new members will have done this during the recruitment tasks, but if you did not do this or you have lost your setup head over to \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"/guides/main/getting-started\"\n  }, \"the Getting Started page\"), \".\"), mdx(\"p\", null, \"Make sure you have\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Git (command line, GitHub Desktop or GitKraken)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Docker\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"A text editor\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Cloned the code\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"./b target generic\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"./b configure\"))), mdx(\"p\", null, \"If the last two commands ran successfully, you are ready to go!\"), mdx(\"h3\", {\n    \"id\": \"the-folder-structure\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#the-folder-structure\",\n    \"aria-label\": \"the folder structure permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"The Folder Structure\"), mdx(\"p\", null, \"Before getting into it, let's look at what we are working with.\"), mdx(\"p\", null, \"If you open the NUbots repository in your text editor, you will see a whole lot of files and folders on the first level of the repository. Most of these you can ignore for now. Here are some of them which will be touched on in this tutorial.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"module\"), \": Contains all the NUClear modules, which are the main pieces of code that run on the robots. This is where our new module will live, and where you will likely spend most of your time programming if you're working on robot functionality.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"roles\"), \": Contains all the role files, which define a complete program. The files contain a list of modules that will be in the compiled program.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"shared/message\"), \": You'll need to look inside the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"shared\"), \" folder for this one. The message folder contains all the Protobuf messages that may be used in the system. NUClear is a message passing architecture, and these Protobuf files define those messages!\")), mdx(\"p\", null, \"Other folders that are important but won't be visited in this tutorial are the following.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"nusight2\"), \": Contains all the code for NUsight, our web-based visual debugging tool.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"shared/utility\"), \": This folder contains many C++ files with functions and classes that are used in NUClear modules.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"shared/tests\"), \": This folder contains automated tests, which use \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://github.com/catchorg/Catch2\"\n  }, \"the Catch framework\"), \".\")), mdx(\"h3\", {\n    \"id\": \"generate-the-module\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#generate-the-module\",\n    \"aria-label\": \"generate the module permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Generate the Module\"), mdx(\"p\", null, \"The generate tool will create the boilerplate code for a module. Here's the command\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"./b module generate TestModule\\n\")), mdx(\"p\", null, \"Run it if you haven't already done so. If you look in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"module\"), \" folder, you will see a new folder called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"TestModule\"), \".\"), mdx(\"p\", null, \"This module wasn't created in a folder, but if you are making a new module in a real scenario you should identify the folder your module best fits in and put it in there.\"), mdx(\"p\", null, \"Lets see what we have in this folder.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"data/config\"), \": Contains a \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"TestModule.yaml\"), \" file, which holds configuration values. Rather than hardcoding values in your implementation, put the values in this file so they can be easily seen and updated. This file is generated with one value, \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"log_level\"), \". We will look into how this works later.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"src\"), \": This folder contains the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \".hpp\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \".cpp\"), \" files for the module. If you look in the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \".hpp\"), \", you'll see a TestModule class is declared that extends \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"NUClear::Reactor\"), \". The \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \".cpp\"), \" file implements the constructor with one lambda statement that reads configuration values.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"tests\"), \": This contains a file that can be used to test the reactors in the module. We will not look at this, but if you want an example go to \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"module/extension/FileWatcher/tests/\"), \".\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"CMakeLists.txt\"), \": This allows CMake to configure the module - we won't change this.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"README.md\"), \": Documentation for the module. We will be filling this out - it is very important to document your code.\")), mdx(\"h2\", {\n    \"id\": \"creating-a-program\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h2\",\n    \"href\": \"#creating-a-program\",\n    \"aria-label\": \"creating a program permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Creating a Program\"), mdx(\"p\", null, \"Ok, so we have a module. It doesn't do much yet, but it should compile. But, it will only compile if it is included in a role file. Let's make a role file.\"), mdx(\"p\", null, \"Go to the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"role\"), \" folder in the repository. Create a new file called \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"testprogram.role\"), \". Put the following in the file\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"nuclear_role(\\n  # FileWatcher, Signal Catcher and ConsoleLogHandler Must Go First\\n  extension::FileWatcher\\n  support::SignalCatcher\\n  support::logging::ConsoleLogHandler\\n  TestModule\\n)\\n\")), mdx(\"p\", null, \"Most of this is copied from other role files. The first three modules are necessary for many programs.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"FileWatcher watches the configuration files (the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \".yaml\"), \" files) for any changes.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"SignalCatcher allows the user to \", mdx(\"kbd\", null, \"CTRL\"), \" + \", mdx(\"kbd\", null, \"C\"), \" to terminate the program cleanly.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"ConsoleLogHandler allows log statements to be output.\")), mdx(\"p\", null, \"The last module added is our new module, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"TestModule\"), \".\"), mdx(\"p\", null, \"This should compile. First we'll need to turn the role we just made \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ON\"), \". Run\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"./b configure\\n./b configure -i\\n\")), mdx(\"p\", null, \"You'll see an interface appear. There will be a long list of entries starting with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ROLE\"), \" set to either \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ON\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"OFF\"), \". Arrow key down to those lines and press \", mdx(\"kbd\", null, \"Enter\"), \" to toggle the role on or off. Turn every role off except for \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ROLE_testprogram\"), \".\"), mdx(\"p\", null, \"Now run\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"./b build\\n\")), mdx(\"p\", null, \"This should have compiled. Let's run it.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"./b run testprogram\\n\")), mdx(\"p\", null, \"You should see the NUbots logo, and the name of the role, and statements telling you what modules have been installed. If you see that, great! But, this isn't very exciting. It doesn't really do anything yet! Let's implement something in our module.\"), mdx(\"p\", null, \"Hit \", mdx(\"kbd\", null, \"Ctrl\"), \" + \", mdx(\"kbd\", null, \"C\"), \" to stop the program.\"), mdx(\"h3\", {\n    \"id\": \"configuration\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#configuration\",\n    \"aria-label\": \"configuration permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Configuration\"), mdx(\"p\", null, \"Let's take a look at the configuration file in our module. Open \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"module/TestModule/data/config/TestModule.yaml\"), \".\"), mdx(\"p\", null, \"This file will store any of values for the program that might change. It is bad practice to hardcode values, and configuration files provide a way of avoiding that. Another thing about configuration files is that they can be changed while a program is running, and that change will be reflected in the program.\"), mdx(\"p\", null, \"Open \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"module/TestModule/src/TestModule.cpp\"), \". There is one \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"on\"), \" statement here, the Configuration \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"on\"), \" statement. The code inside this function will run on startup and whenever the configuration file \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"TestModule.yaml\"), \" is changed.\"), mdx(\"p\", null, \"Inside this statement, after the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"log_level\"), \" is set, add the following code\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-cpp\"\n  }, \"log<TRACE>(\\\"This is a TRACE log.\\\");\\nlog<DEBUG>(\\\"This is a DEBUG log.\\\");\\nlog<INFO>(\\\"This is a INFO log.\\\");\\nlog<WARN>(\\\"This is a WARN log.\\\");\\nlog<ERROR>(\\\"This is a ERROR log.\\\");\\nlog<FATAL>(\\\"This is a FATAL log.\\\");\\n\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"log\"), \" will print to the terminal. The log level set from the configuration file determines what level logs are printed. The log level is currently on \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"INFO\"), \". Let's compile and run the program again.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-cpp\"\n  }, \"./b build\\n./b run testprogram\\n\")), mdx(\"p\", null, \"You should see all of the logs print except for \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"TRACE\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DEBUG\"), \". This is because all logs with level \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"INFO\"), \" and higher will run. Lets change the configuration file without stopping the program! Open a new terminal and run \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"./b edit config/TestModule.yaml\"), \". This will open the TestModule configuration file in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"nano\"), \". Change \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"INFO\"), \" to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"TRACE\"), \". Save with \", mdx(\"kbd\", null, \"CTRL\"), \" + \", mdx(\"kbd\", null, \"O\"), \" and then \", mdx(\"kbd\", null, \"Enter\"), \". If you look at the program, you will see the logs print out again, but with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"TRACE\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"DEBUG\"), \" included. Go back and change the config file to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"WARN\"), \". You will now see the logs print again, but with only \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"WARN\"), \", \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ERROR\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"FATAL\"), \".\"), mdx(\"p\", null, \"The log level system allows you to keep statements for debugging or tuning without the logs spamming the terminal when someone is running the module for other purposes.\"), mdx(\"p\", null, \"Remove all of the log statements. We will be making a 'ping-pong' program that sends an incrementing count between reactions. The increment size will be a configuration value. Let's add that configuration value now.\"), mdx(\"p\", null, \"In \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"TestModule.yaml\"), \" (in your text editor, not with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"./b edit\"), \" since this is not persistent), add in the following lines\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-yaml\"\n  }, \"# Each time the Ping Pong messages are sent, a count is incremented and the increment is of the following size\\nincrement: 2\\n\")), mdx(\"p\", null, \"Next we need to read it in the module. Go to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"TestModule.hpp\"), \". We will make a variable to store this in. Inside the config struct, add in\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-cpp\"\n  }, \"/// @brief How much to increment the count by each time a new Ping or Pong message is emitted\\nint increment = 0;\\n\")), mdx(\"p\", null, \"Always initialise your variables and add \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"/guides/general/code-conventions#documentation\"\n  }, \"documentation directives\"), \". The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"@brief\"), \" is a documentation directive that indicates that the comment is a brief description of the following variable. These are used to \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://codedocs.nubots.net/\"\n  }, \"generate documentation\"), \".\"), mdx(\"p\", null, \"Save and go to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"TestModule.cpp\"), \". Add in the following lines to read the configuration value and print a message to let the user know the increment size.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-cpp\"\n  }, \"cfg.increment = config[\\\"increment\\\"].as<int>();\\nlog<INFO>(\\\"Increment is of size\\\", cfg.increment);\\n\")), mdx(\"p\", null, \"Save, recompile and check that the program prints the message.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"./b build\\n./b run testprogram\\n\")), mdx(\"p\", null, \"Make sure you see your \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"INFO\"), \" log and then lets move onto making the messages that we need. If you can't see the log in the terminal, make sure that \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"log_level\"), \" in \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"TestModule.yaml\"), \" is set to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"INFO\"), \".\"), mdx(\"h3\", {\n    \"id\": \"protobuf-messages\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#protobuf-messages\",\n    \"aria-label\": \"protobuf messages permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Protobuf Messages\"), mdx(\"p\", null, \"The idea of the program is that one reactor will send a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Ping\"), \" message when it receives a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Pong\"), \" message, and the other reactor will send a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Pong\"), \" message when it receives a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Ping\"), \" message.\"), mdx(\"p\", null, \"We will need to create these messages. In \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"shared/message\"), \" create a new file \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"PingPong.proto\"), \". Put the follow code into the file.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-proto\"\n  }, \"syntax = \\\"proto3\\\";\\n\\npackage message;\\n\\nmessage Ping {\\n    uint32 count = 1;\\n}\\n\\nmessage Pong {\\n    uint32 count = 1;\\n}\\n\")), mdx(\"p\", null, \"There is an unsigned 32-bit integer in each of these messages. This is the count that will be incremented each time a new \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Ping\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Pong\"), \" message is sent. The number it is assigned, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"1\"), \", is not the value but its position in the Protobuf message. Save this file and head back over to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"TestModule.cpp\"), \".\"), mdx(\"p\", null, \"Up at the top of the file, include the protobuf message we just created. Note that these messages are transcompile into C++ code, so include the generated header.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-cpp\"\n  }, \"#include \\\"message/PingPong.hpp\\\"\\n\")), mdx(\"p\", null, \"Make sure it compiles and still runs successfully. Because we added files, we will need to configure again.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"./b configure\\n./b build\\n./b run testprogram\\n\")), mdx(\"p\", null, \"If this works, you can move onto the next section where we make our reactions.\"), mdx(\"h3\", {\n    \"id\": \"reactions\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#reactions\",\n    \"aria-label\": \"reactions permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Reactions\"), mdx(\"p\", null, \"Reactions are chunks of code that will run given certain conditions are true. We will make two reactions, one that runs tasks whenever it gets a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Pong\"), \" message, and the other when it gets a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Ping\"), \" message.\"), mdx(\"p\", null, \"Head back to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"TestModule.cpp\"), \". Add using statements after the namepace declaration\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-cpp\"\n  }, \"using message::Ping;\\nusing message::Pong;\\n\")), mdx(\"p\", null, \"Underneath the Configuration reactor, add in the following reaction\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-cpp\"\n  }, \"on<Trigger<Ping>>().then([this](const Ping& ping) {\\n    // Some code!\\n});\\n\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Trigger\"), \" means that it will run when it a \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Ping\"), \" message is available. We get access to this message via the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ping\"), \" parameter. Let's add in the other reaction.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-cpp\"\n  }, \"on<Trigger<Pong>>().then([this](const Pong& pong) {\\n    // Some code!\\n});\\n\")), mdx(\"p\", null, \"Very similar, but it gets the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Pong\"), \" message instead.\"), mdx(\"p\", null, \"Next, we want to get the count from the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Ping\"), \" or \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Pong\"), \" message, increment it, and send a new message with the incremented count. Here's the code for the first reaction.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-cpp\"\n  }, \"// Print the Ping message!\\nlog<INFO>(\\\"Ping count\\\", ping.count);\\n\\n// Make a Pong message to send\\nauto pong = std::make_unique<Pong>();\\npong->count = ping.count + cfg.increment;\\n\\n// Send the message\\nemit(pong);\\n\")), mdx(\"p\", null, \"And for the second reaction\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-cpp\"\n  }, \"// Print the Pong message!\\nlog<INFO>(\\\"Pong count\\\", pong.count);\\n\\n// Make a Ping message to send\\nauto ping = std::make_unique<Ping>();\\nping->count = pong.count + cfg.increment;\\n\\n// Send the message\\nemit(ping);\\n\")), mdx(\"p\", null, \"Have a look through the comments and make sure you understand the code. \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"auto\"), \" is a keyword that automatically determines the type of the variable.\"), mdx(\"p\", null, \"Is everything ready now to see the messages ping-ponging between each other, printing out increasing count values? Compile the program and run it, and see what happens.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-cpp\"\n  }, \"./b build\\n./b run testprogram\\n\")), mdx(\"p\", null, \"Well... Nothing changed. If you're not sure why, have a think about it before moving onto the next section.\"), mdx(\"h3\", {\n    \"id\": \"starting-the-chain\",\n    \"style\": {\n      \"position\": \"relative\"\n    }\n  }, mdx(\"a\", {\n    parentName: \"h3\",\n    \"href\": \"#starting-the-chain\",\n    \"aria-label\": \"starting the chain permalink\",\n    \"className\": \"anchor before\"\n  }, mdx(\"svg\", {\n    parentName: \"a\",\n    \"aria-hidden\": \"true\",\n    \"focusable\": \"false\",\n    \"height\": \"16\",\n    \"version\": \"1.1\",\n    \"viewBox\": \"0 0 16 16\",\n    \"width\": \"16\"\n  }, mdx(\"path\", {\n    parentName: \"svg\",\n    \"fillRule\": \"evenodd\",\n    \"d\": \"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"\n  }))), \"Starting the Chain\"), mdx(\"p\", null, \"The problem is, that there is no initial Ping or Pong message. No one ever sends the first message - the first reactor is forever waiting for a Ping message, and the second reactor is forever waiting for a Pong message!\"), mdx(\"p\", null, \"Ok, so how do we start the reaction tasks? There are a few ways it could be done, but we will use the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Startup\"), \" NUClear domain specific language (DSL) word. This is another reaction that will run only when the program starts up.\"), mdx(\"p\", null, \"Here is the code for the reaction.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-cpp\"\n  }, \"on<Startup>().then([this] {\\n    // Make an initial Ping message to send\\n    auto ping = std::make_unique<Ping>();\\n    ping->count = 0;\\n\\n    // Send the message\\n    emit(ping);\\n});\\n\")), mdx(\"p\", null, \"Let's compile and run it.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"./b build\\n./b run testprogram\\n\")), mdx(\"p\", null, \"That was a bit too fast! Stop the program if you haven't already. Let's review our reactions. There's nothing stopping them from running as fast as they can, they will keep sending messages to each other as fast as possible!\"), mdx(\"p\", null, \"Here's where the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Every\"), \" DSL word comes in. Let's replace the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Trigger<Ping>\"), \" with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Every<2, std::chrono::seconds>, With<Ping>\"), \". This will prevent the reaction from running tasks faster than once every two seconds. Compile and run again.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"./b build\\n./b run testprogram\\n\")), mdx(\"p\", null, \"You should see the Ping and Pong logs running at a reasonable speed.\"), mdx(\"p\", null, \"Now that you understand the module, you can fill in the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"README.md\"), \" file. This is a short description of the module. The README is generated with headings to guide the content. Here are some hints for each section.\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Description:\"), \" Write in your own description of what the module does.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Usage:\"), \" Since it is not dependent on other modules, the user only needs to include the module in their role to use it.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Consumes:\"), \" The the \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"Ping\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"Pong\"), \" messages are consumed by the reactors.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Emits:\"), \" The \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"Ping\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"Pong\"), \" messages are emitted by the reactors.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Dependencies:\"), \" This module doesn't have any dependencies of note.\")), mdx(\"p\", null, \"This is the end of this tutorial. If you want to learn more, play around with some of the other NUClear DSL words and see how they work.\"), mdx(\"p\", null, \"If you had any problems setting up the program, you can find all \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://drive.google.com/file/d/1AzfWoKbMe-XAzKYRuVjOO5veYLpLMUil/\"\n  }, \"the code files here\"), \".\"));\n}\n;\nMDXContent.isMDXComponent = true;","tableOfContents":{"items":[{"url":"#generating-a-module","title":"Generating a Module","items":[{"url":"#setting-up-the-codebase","title":"Setting up the Codebase"},{"url":"#the-folder-structure","title":"The Folder Structure"},{"url":"#generate-the-module","title":"Generate the Module"}]},{"url":"#creating-a-program","title":"Creating a Program","items":[{"url":"#configuration","title":"Configuration"},{"url":"#protobuf-messages","title":"Protobuf Messages"},{"url":"#reactions","title":"Reactions"},{"url":"#starting-the-chain","title":"Starting the Chain"}]}]},"frontmatter":{"section":"Guides","chapter":"Main Codebase","title":"NUClear","description":"Creating a NUClear module.","keywords":null,"slug":"/guides/main/nuclear-tutorial","hidden":null},"childNUbookContributions":{"authors":[{"name":"Ysobel Sims","username":"ysims"}],"lastCommit":{"date":"2025-04-25T05:50:58.000Z","hash":"a2d0c2a851883b05ca89a2b49d94af5a76dd87cc"}}}},"pageContext":{"mdxPath":"src/book/03-guides/01-main-codebase/02-nuclear-tutorial.mdx","id":"7b6b366b-b57f-564a-8de2-26a3650f5337","next":{"chapter":"Main Codebase","title":"Running and Tuning Scripts","description":"How to tune and run scripts.","slug":"/guides/main/tuning-and-running-scripts","hidden":null},"previous":{"chapter":"Main Codebase","title":"Getting Started","description":"Setting up your environment and getting started with NUbots development.","slug":"/guides/main/getting-started","hidden":null},"menu":[{"title":"Team","slug":"/team/introduction","chapters":[{"title":"Introduction","slug":"/team/introduction","pages":[{"title":"Introduction to NUbots","slug":"/team/introduction","hidden":null},{"title":"Areas of Research","slug":"/team/areas-of-research","hidden":null},{"title":"Current Team","slug":"/team/current-members","hidden":null}],"hidden":false},{"title":"Joining the Team","slug":"/team/how-we-work","pages":[{"title":"How We Work","slug":"/team/how-we-work","hidden":null},{"title":"How to Join NUbots","slug":"/team/how-to-join","hidden":null},{"title":"Lab Induction","slug":"/team/induction","hidden":null}],"hidden":false},{"title":"Community","slug":"/team/sponsors","pages":[{"title":"Sponsors","slug":"/team/sponsors","hidden":null},{"title":"Connect","slug":"/team/connect","hidden":null}],"hidden":false},{"title":"History","slug":"/team/history","pages":[{"title":"Team History","slug":"/team/history","hidden":null},{"title":"Past Members","slug":"/team/past-members","hidden":null},{"title":"Publications","slug":"/team/publications","hidden":null}],"hidden":false},{"title":"RoboCup","slug":"/team/robocup","pages":[{"title":"RoboCup","slug":"/team/robocup","hidden":null},{"title":"Resources","slug":"/team/robocup/resources","hidden":null},{"title":"Debriefs","slug":"/team/robocup/debriefs","hidden":null}],"hidden":false}],"hidden":false},{"title":"System","slug":"/system/foundations/overview","chapters":[{"title":"Foundations","slug":"/system/foundations/overview","pages":[{"title":"Overview","slug":"/system/foundations/overview","hidden":null},{"title":"Build System","slug":"/system/foundations/build-system","hidden":null},{"title":"Continuous Integration","slug":"/system/foundations/ci-system","hidden":null},{"title":"NUClear","slug":"/system/foundations/nuclear","hidden":null},{"title":"Mathematics","slug":"/system/foundations/mathematics","hidden":null},{"title":"Configuration and Script System","slug":"/system/foundations/config-script","hidden":null},{"title":"Director","slug":"/system/foundations/director","hidden":null}],"hidden":false},{"title":"Subsystems","slug":"/system/subsystems/input","pages":[{"title":"Input","slug":"/system/subsystems/input","hidden":null},{"title":"Odometry","slug":"/system/subsystems/odometry","hidden":null},{"title":"Localisation","slug":"/system/subsystems/localisation","hidden":null},{"title":"Motion","slug":"/system/subsystems/motion","hidden":null},{"title":"Vision","slug":"/system/subsystems/vision","hidden":null},{"title":"Behaviour","slug":"/system/subsystems/behaviour","hidden":null},{"title":"Logging","slug":"/system/subsystems/logging","hidden":null}],"hidden":false},{"title":"Tools","slug":"/system/tools/nusight","pages":[{"title":"NUsight","slug":"/system/tools/nusight","hidden":null},{"title":"NUbook","slug":"/system/tools/nubook","hidden":null},{"title":"NUpbr","slug":"/system/tools/nupbr","hidden":null},{"title":"NUgan","slug":"/system/tools/nugan","hidden":null},{"title":"System Configuration","slug":"/system/tools/system_configuration","hidden":null},{"title":"NatNet SDK","slug":"/system/tools/natnet_sdk","hidden":null}],"hidden":false},{"title":"Hardware","slug":"/system/hardware/overview","pages":[{"title":"Overview and Specifications","slug":"/system/hardware/overview","hidden":null}],"hidden":false},{"title":"Modules","slug":"/system/modules/actuation/","pages":[{"title":"Actuation","slug":"/system/modules/actuation/","hidden":null},{"title":"Extension","slug":"/system/modules/extension/","hidden":null},{"title":"Input","slug":"/system/modules/input/","hidden":null},{"title":"Localisation","slug":"/system/modules/localisation/","hidden":null},{"title":"Nbs","slug":"/system/modules/nbs/","hidden":null},{"title":"Network","slug":"/system/modules/network/","hidden":null},{"title":"Output","slug":"/system/modules/output/","hidden":null},{"title":"Planning","slug":"/system/modules/planning/","hidden":null},{"title":"Platform","slug":"/system/modules/platform/","hidden":null},{"title":"Purpose","slug":"/system/modules/purpose/","hidden":null},{"title":"Skill","slug":"/system/modules/skill/","hidden":null},{"title":"Strategy","slug":"/system/modules/strategy/","hidden":null},{"title":"Support","slug":"/system/modules/support/","hidden":null},{"title":"Tools","slug":"/system/modules/tools/","hidden":null},{"title":"Vision","slug":"/system/modules/vision/","hidden":null}],"hidden":false}],"hidden":false},{"title":"Guides","slug":"/guides/main/getting-started","chapters":[{"title":"Main Codebase","slug":"/guides/main/getting-started","pages":[{"title":"Getting Started","slug":"/guides/main/getting-started","hidden":null},{"title":"NUClear","slug":"/guides/main/nuclear-tutorial","hidden":null},{"title":"Running and Tuning Scripts","slug":"/guides/main/tuning-and-running-scripts","hidden":null},{"title":"Maintaining Subsystems","slug":"/guides/main/maintaining-subsystems","hidden":null},{"title":"Data Recording and Playback","slug":"/guides/main/data-recording-playback","hidden":null},{"title":"Camera Calibration","slug":"/guides/main/camera-calibration","hidden":null},{"title":"Onboarding Workshop","slug":"/guides/main/onboarding","hidden":null}],"hidden":false},{"title":"Tools","slug":"/guides/tools/nusight-contribution","pages":[{"title":"Contributing to NUsight","slug":"/guides/tools/nusight-contribution","hidden":null},{"title":"Setting Up Webots","slug":"/guides/tools/webots-setup","hidden":null},{"title":"Visual Mesh Getting Started","slug":"/guides/tools/visualmesh","hidden":null},{"title":"Using NUpbr","slug":"/guides/tools/nupbr-guide","hidden":null},{"title":"GameController Setup","slug":"/guides/tools/gamecontroller","hidden":null}],"hidden":false},{"title":"Hardware","slug":"/guides/hardware/working-with-robots","pages":[{"title":"Working with Robots","slug":"/guides/hardware/working-with-robots","hidden":null},{"title":"Flashing a Robot","slug":"/guides/hardware/flashing","hidden":null},{"title":"Servo Setup and Calibration","slug":"/guides/hardware/servo-calibration","hidden":null},{"title":"Batteries","slug":"/guides/hardware/batteries","hidden":null},{"title":"DARwIn Op2 Robot Restoration and Calibration Guide","slug":"/guides/hardware/darwin-op2-guide","hidden":null}],"hidden":false},{"title":"General","slug":"/guides/general/learning-resources","pages":[{"title":"Learning Resources","slug":"/guides/general/learning-resources","hidden":null},{"title":"Contribution Workflow","slug":"/guides/general/contribute","hidden":null},{"title":"RoboCup Setup","slug":"/guides/general/robocup-setup","hidden":null},{"title":"Troubleshooting","slug":"/guides/general/troubleshooting","hidden":null},{"title":"Code Conventions","slug":"/guides/general/code-conventions","hidden":null},{"title":"Glossary","slug":"/guides/general/glossary","hidden":null}],"hidden":false}],"hidden":false},{"title":"Kitchen Sink","slug":"/kitchen-sink/headers","chapters":[{"title":"Markdown","slug":"/kitchen-sink/headers","pages":[{"title":"Headers","slug":"/kitchen-sink/headers","hidden":true},{"title":"Formatting and Paragraphs","slug":"/kitchen-sink/formatting-and-paragraphs","hidden":true},{"title":"Blockquotes","slug":"/kitchen-sink/blockquotes","hidden":true},{"title":"Alerts","slug":"/kitchen-sink/alerts","hidden":true},{"title":"Images","slug":"/kitchen-sink/images","hidden":true},{"title":"Lists","slug":"/kitchen-sink/lists","hidden":true},{"title":"Code","slug":"/kitchen-sink/code","hidden":true},{"title":"Math","slug":"/kitchen-sink/math","hidden":true},{"title":"Table of Contents","slug":"/kitchen-sink/table-of-contents","hidden":true},{"title":"Tables","slug":"/kitchen-sink/tables","hidden":true},{"title":"Collapsible Content","slug":"/kitchen-sink/collapsible","hidden":true},{"title":"GraphViz Diagrams","slug":"/kitchen-sink/graphviz-diagrams","hidden":true},{"title":"Referencing","slug":"/kitchen-sink/referencing","hidden":true}],"hidden":true}],"hidden":true}],"references":null,"hidden":null}},
    "staticQueryHashes": ["3715292327","445096115","466146812"]}