diff --git a/TeamCode/src/main/java/org/cyberarm/NeXT/DataStruct.java b/TeamCode/src/main/java/org/cyberarm/NeXT/DataStruct.java new file mode 100644 index 0000000..e5e215b --- /dev/null +++ b/TeamCode/src/main/java/org/cyberarm/NeXT/DataStruct.java @@ -0,0 +1,141 @@ +package org.cyberarm.NeXT; + + +import java.util.HashMap; + +public class DataStruct { + public String name; + public boolean enabled; + + /* + Hash of Strings mapped to Strings, those strings are various types mapped to stings: + Boolean -> "Bxfalse" + Integer -> "Ix100" + Integer -> "Lx123456789" + Double -> "Dx0.1" + Float -> "Fx10.1" + String -> "SxWords can go here." + + DataStruct.valueOf(Hash.get("distanceToDrive")) -> 100 + */ + private HashMap variables; + + public DataStruct() { + this.variables = new HashMap<>(); + } + public DataStruct(String name, boolean enabled, HashMap variables) { + this.name = name; + this.enabled = enabled; + this.variables = variables; + } + + + public String name() { + return name; + } + public void setName(String name) { + this.name = name; + } + + public boolean enabled() { + return enabled; + } + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public HashMap variables(String power) { + return variables; + } + + public T variable(String key) throws NullPointerException { + if (variables.get(key.trim()) == null) { + throw(new NullPointerException("Action \""+ name +"\" has no Variable called \""+ key.trim() +"\"")); + } + + return valueOf(variables.get(key.trim())); + } + + @SuppressWarnings("unchecked") + public static T valueOf(String value) { + String[] split = value.split("x"); + + switch (split[0]) { + case "B": { + return (T) Boolean.valueOf(split[(split.length-1)]); + } + case "D": { + return (T) Double.valueOf(split[(split.length-1)]); + } + case "F": { + return (T) Float.valueOf(split[(split.length-1)]); + } + case "I": { + return (T) Integer.valueOf(split[(split.length-1)]); + } + case "L": { + return (T) Long.valueOf(split[(split.length-1)]); + } + case "S": { + return (T) String.valueOf(split[(split.length-1)]); + } + default: { + return null; + } + } + } + + public static String typeOf(String value) { + String[] split = value.split("x"); + + switch (split[0]) { + case "B": { + return "Boolean"; + } + case "D": { + return "Double"; + } + case "F": { + return "Float"; + } + case "I": { + return "Integer"; + } + case "L": { + return "Long"; + } + case "S": { + return "String"; + } + default: { + return "=!UNKNOWN!="; + } + } + } + + public static String encodeValue(String type, String value) { + switch (type) { + case "Boolean": { + return "Bx"+value; + } + case "Double": { + return "Dx"+value; + } + case "Float": { + return "Fx"+value; + } + case "Integer": { + return "Ix"+value; + } + case "Long": { + return "Lx"+value; + } + case "String": { + return "Sx"+value; + } + default: { + return "=!UNKNOWN!="; + } + } + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/cyberarm/NeXT/StateConfiguration.java b/TeamCode/src/main/java/org/cyberarm/NeXT/StateConfiguration.java new file mode 100644 index 0000000..7e828a4 --- /dev/null +++ b/TeamCode/src/main/java/org/cyberarm/NeXT/StateConfiguration.java @@ -0,0 +1,92 @@ +package org.cyberarm.NeXT; + +import android.os.Environment; +import android.util.Log; + +import com.google.gson.Gson; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +public class StateConfiguration { + private boolean loadSuccessful = false; + private ArrayList dataStructs; + private HashMap actions; + + public StateConfiguration() { + dataStructs = new ArrayList<>(); + actions = new HashMap<>(); + + // Load configuration file. + if (loadJSON()) { + Log.i("TC_CONFIG", "Successfully loaded configuration file."); + + for (DataStruct dataStruct : dataStructs) { + actions.put(dataStruct.name.trim(), dataStruct); + } + + } else { + Log.e("TC_CONFIG", "FAILED TO LOAD CONFIGURATION FILE!"); + } + } + + public DataStruct get(String key) throws NullPointerException { + if (actions.get(key.trim()) == null) { + throw(new NullPointerException("Config has no Action called \""+ key.trim() +"\"")); + } + + return actions.get(key.trim()); + } + + public boolean allow(String key) throws NullPointerException { + if (get(key.trim()) == null) { + throw(new NullPointerException("Config has no Action called \""+ key.trim() +"\"")); + } + + return get(key.trim()).enabled; + } + + private boolean loadJSON() { + boolean loadSuccessful = false; + + File file = new File(getDirectory() + File.separator + "config.json"); + StringBuilder text = new StringBuilder(); + + if (file.exists()) { + try { + BufferedReader br = new BufferedReader(new FileReader(file)); + String line; + + while ((line = br.readLine()) != null) { + text.append(line); + text.append('\n'); + } + br.close(); + + Gson gson = new Gson(); + DataStruct[] array = gson.fromJson(text.toString(), DataStruct[].class); + this.dataStructs = new ArrayList<>(Arrays.asList(array)); + + loadSuccessful = true; + + } catch (IOException e) { + System.out.println(e); + // TODO: handle this + } + } + + this.loadSuccessful = loadSuccessful; + return loadSuccessful; + } + + private String getDirectory() { + return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "FIRST_TC_CONFIG"; + } + + public boolean wasLoadSuccessful() { return loadSuccessful; } +} diff --git a/TeamCode/src/main/java/org/cyberarm/container/InputChecker.java b/TeamCode/src/main/java/org/cyberarm/container/InputChecker.java new file mode 100644 index 0000000..438e476 --- /dev/null +++ b/TeamCode/src/main/java/org/cyberarm/container/InputChecker.java @@ -0,0 +1,58 @@ +package org.cyberarm.container; + +import android.util.Log; + +import com.qualcomm.robotcore.hardware.Gamepad; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; + +public class InputChecker { + private Gamepad gamepad; + private HashMap buttons; + private ArrayList buttonList; + private byte NULL = 0, + PRESSED = 1, + RELEASED= 2; + public InputChecker(Gamepad gamepad) { + this.gamepad = gamepad; + buttons = new HashMap<>(); + buttonList = new ArrayList<>(); + + buttonList.add("a"); buttonList.add("b"); buttonList.add("x"); buttonList.add("y"); + buttonList.add("start"); buttonList.add("guide"); buttonList.add("back"); + buttonList.add("left_bumper"); buttonList.add("right_bumper"); + buttonList.add("left_stick_button"); buttonList.add("right_stick_button"); + buttonList.add("dpad_left"); buttonList.add("dpad_right"); + buttonList.add("dpad_up"); buttonList.add("dpad_down"); + } + + public void update() { + for (int i = 0; i < buttonList.size(); i++) { + try { + Field field = gamepad.getClass().getDeclaredField(buttonList.get(i)); + + if (field.getBoolean(gamepad)) { + buttons.put(buttonList.get(i), PRESSED); + } else { + if (buttons.get(buttonList.get(i)) != null && buttons.get(buttonList.get(i)) == PRESSED) { + buttons.put(buttonList.get(i), RELEASED); + } + } + } catch (NoSuchFieldException|IllegalAccessException e) { + e.printStackTrace(); + } + } + } + + public boolean check(String button) { + boolean state = false; + if (buttons.containsKey(button) && buttons.get(button) == RELEASED) { + Log.d("InputChecker","button \""+button+"\" has been released on \"gamepad"+gamepad.getGamepadId()+"\""); + state = true; + buttons.put(button, NULL); + } + return state; + } +} diff --git a/TeamCode/src/main/java/org/cyberarm/engine/V2/CyberarmEngine.java b/TeamCode/src/main/java/org/cyberarm/engine/V2/CyberarmEngine.java new file mode 100644 index 0000000..98f1784 --- /dev/null +++ b/TeamCode/src/main/java/org/cyberarm/engine/V2/CyberarmEngine.java @@ -0,0 +1,220 @@ +package org.cyberarm.engine.V2; + +import android.util.Log; + +import com.qualcomm.robotcore.eventloop.opmode.OpMode; + +import java.util.ArrayList; + +/** + * CyberarmEngine Version 2.0 | October 26th 2018 + * AN Experimental reimplementation of GoldfishPi's original Engine system. + * Designed to be easily maintainable, extensible, and easy to understand. + */ +public abstract class CyberarmEngine extends OpMode { + + public static CyberarmEngine instance; + //Array To Hold States + private ArrayList cyberarmStates = new ArrayList<>(); + private int activeStateIndex = 0; + private boolean isRunning; + + private static String TAG = "PROGRAM.ENGINE: "; + + /** + * Called when INIT button on Driver Station is pushed + * ENSURE to call super.init() if you override this method + */ + public void init() { + CyberarmEngine.instance = this; + isRunning = false; + + setup(); + + isRunning = true; + + for (CyberarmState state: cyberarmStates) { + initState(state); + } + } + + /** + * Setup states for engine to use + * For example: + *
+   * @
+   *   public void setup() {
+   *     addState(new TestState());
+   *     addState(new AnotherState(100, 500));
+   *   }
+   * 
+   * 
+ */ + public abstract void setup(); + + /** + * Called when START button on Driver Station is pushed + * ENSURE to call super.start() if you override this method + */ + public void start() { + if (cyberarmStates.size() > 0) { + runState(cyberarmStates.get(0)); + } + } + + /** + * Engine main loop + * ENSURE to call super.loop() if you override this method + */ + public void loop() { + CyberarmState state; + + // Try to set state to the current state, if it fails assume that there are no states to run + try { + state = cyberarmStates.get(activeStateIndex); + } catch(IndexOutOfBoundsException e) { + // The engine is now out of states. + stop(); + + telemetry.addLine("" + this.getClass().getSimpleName() + " is out of states to run!"); + telemetry.addLine(); + return; + } + + // Add telemetry to show currently running state + telemetry.addLine("Running state: " +state.getClass().getSimpleName() + ". State: " + activeStateIndex + " of " + (cyberarmStates.size()-1)); + telemetry.addLine(); + + if (state.getHasFinished() && state.childrenHaveFinished()) { + activeStateIndex++; + + try { + state = cyberarmStates.get(activeStateIndex); + runState(state); + } catch(IndexOutOfBoundsException e) { /* loop will handle this in a few milliseconds */ } + + } else { + stateTelemetry(state); + } + } + + /** + * Stops every known state + */ + @Override + public void stop() { + for (CyberarmState state: cyberarmStates) { + stopState(state); + } + } + + /** + * Recursively calls telemetry() on states + * @param state State to get telemetry + */ + private void stateTelemetry(CyberarmState state) { + if (!state.getHasFinished()) { + state.telemetry(); + } + + for(CyberarmState childState : state.children) { + if (!childState.getHasFinished()) { + stateTelemetry(childState); + } + } + } + + /** + * Called when INIT button on Driver Station is pressed + * Recursively initiates states + * @param state State to initiate + */ + private void initState(CyberarmState state) { + state.init(); + + for(CyberarmState childState : state.children) { + initState(childState); + } + } + + /** + * Called when programs ends or STOP button on Driver Station is pressed + * Recursively stop states + * @param state State to stop + */ + private void stopState(CyberarmState state) { + state.setHasFinished(true); + state.stop(); + + for(CyberarmState childState : state.children) { + stopState(childState); + } + } + + /** + * Recursively start up states + * @param state State to run + */ + protected void runState(CyberarmState state) { + final CyberarmState finalState = state; +// if (state.isRunning()) { return; } // Assume that we have already started running this state + + new Thread(new Runnable() { + @Override + public void run() { + finalState.start(); + finalState.startTime = System.currentTimeMillis(); + finalState.run(); + } + }).start(); + + for (CyberarmState kid : state.children) { + runState(kid); + } + } + + /** + * Add state to queue, will call init() on state if engine is running + * @param state State to add to queue + */ + public CyberarmState addState(CyberarmState state) { + Log.i(TAG, "Adding cyberarmState "+ state.getClass()); + cyberarmStates.add(state); + + if (isRunning()) { initState(state); } + + return state; + } + + /** + * Inserts state after the query state plus an offset to ensure logical insertion + * @param query State to add state after + * @param state State to be inserted + * @return + */ + public CyberarmState insertState(CyberarmState query, CyberarmState state) { + int index = cyberarmStates.indexOf(query) + query.insertOffset; + cyberarmStates.add(index, state); + query.insertOffset++; + + if (isRunning()) { initState(state); } + + return state; + } + + /** + * This will return false while Engine.setup() is executing, and be true after. + * @return Whether the engine main loop is running + */ + public boolean isRunning() { + return isRunning; + } + + /** + * + * @return The index used to lookup the current state from cyberarmStates + */ + public int getActiveStateIndex() { + return activeStateIndex; + } +} diff --git a/TeamCode/src/main/java/org/cyberarm/engine/V2/CyberarmState.java b/TeamCode/src/main/java/org/cyberarm/engine/V2/CyberarmState.java new file mode 100644 index 0000000..bb8c8e9 --- /dev/null +++ b/TeamCode/src/main/java/org/cyberarm/engine/V2/CyberarmState.java @@ -0,0 +1,206 @@ +package org.cyberarm.engine.V2; + +import android.util.Log; + +import java.util.ArrayList; + +/** + * A State for use with CyberarmEngineV2 + */ +public abstract class CyberarmState implements Runnable { + + private volatile boolean isRunning, hasFinished; + public static String TAG = "PROGRAM.STATE"; + public CyberarmEngine engine = CyberarmEngine.instance; + public ArrayList children = new ArrayList<>(); + public long startTime = 0; + public int insertOffset = 1; + + /** + * Called when INIT button on Driver Station is pushed + */ + public void init() { + } + + /** + * Called when state has begin to run + */ + public void start() { + } + + /** + * Called while State is running + */ + public abstract void exec(); + + /** + * State's main loop, calls exec() until hasFinished is true + * DO NO OVERRIDE + */ + @Override + public void run() { + isRunning = true; + while (!hasFinished) { + exec(); + } + isRunning = false; + } + + /** + * Place telemetry calls in here instead of inside exec() to have them displayed correctly on the Driver Station + * (States update thousands of times per second, resulting in missing or weirdly formatted telemetry if telemetry is added in exec()) + */ + public void telemetry() { + } + + /** + * Called when Engine is finished + */ + public void stop() { + } + + /** + * Add a state which runs in parallel with this one + */ + public CyberarmState addParallelState(CyberarmState state) { + Log.i(TAG, "Adding " + state.getClass() + " to " + this.getClass()); + children.add(state); + + if (isRunning()) { state.init(); engine.runState(state); } + + return state; + } + + /** + * Add a state to engine which will run after this one finishes + */ + public CyberarmState addState(CyberarmState state) { + engine.insertState(this, state); + + return state; + } + + /** + * Returns whether or not state has children + * @return True if state has children, false otherwise + */ + public boolean hasChildren() { + return (children.size() > 0); + } + + /** + * Have all of the states children finished running themselves? + * @return Wether or not all children have finished running + */ + public boolean childrenHaveFinished() { + return childrenHaveFinished(children); + } + + /** + * Have all of the states children finished running themselves? + * @param kids ArrayList of children to check for hasFinished() + * @return Whether or not all children have finished running + */ + public boolean childrenHaveFinished(ArrayList kids) { + boolean allDone = true; + + for (CyberarmState state : kids) { + if (!state.hasFinished) { + allDone = false; + break; + } else { + if (!state.childrenHaveFinished()) { + allDone = false; + break; + } + } + } + + return allDone; + } + + /** + * + * @return The number of milliseconds this state has been running for + */ + public double runTime() { + return (System.currentTimeMillis() - startTime); + } + + /** + * Set whether state has finished or not + * @param value + */ + public void setHasFinished(boolean value) { + hasFinished = value; + } + + /** + * + * @return Get value of hasFinished + */ + public boolean getHasFinished() { + return hasFinished; + } + + /** + * + * @return Get value of isRunning + */ + public boolean isRunning() { + return isRunning; + } + + /** + * + * @param timems How long to sleep in milliseconds + */ + public void sleep(long timems) { + try { + Thread.sleep(timems); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + /** + * + * @param width How many characters wide to be + * @param percentCompleted Number between 0.0 and 100.0 + * @param bar What character to draw the completion bar with + * @param padding What character to draw non-completed bar with + * @return A string + */ + public String progressBar(int width, double percentCompleted, String bar, String padding) { + String percentCompletedString = "" + Math.round(percentCompleted) + "%"; + double activeWidth = (width - 2) - percentCompletedString.length(); + + String string = "["; + double completed = (percentCompleted / 100.0) * activeWidth; + + for (int i = 0; i <= ((int) activeWidth); i++) { + if (i == ((int) activeWidth) / 2) { + string += percentCompletedString; + } else { + if (i <= (int) completed && (int) completed > 0) { + string += bar; + } else { + string += padding; + } + } + } + + string += "]"; + return string; + } + + /** + * + * @param width How many characters wide to be + * @param percentCompleted Number between 0.0 and 100.0 + * @return A string + */ + public String progressBar(int width, double percentCompleted) { + return progressBar(width, percentCompleted, "=", " "); + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/cyberarm/engine/V2/README.txt b/TeamCode/src/main/java/org/cyberarm/engine/V2/README.txt new file mode 100644 index 0000000..7632dc5 --- /dev/null +++ b/TeamCode/src/main/java/org/cyberarm/engine/V2/README.txt @@ -0,0 +1,28 @@ +CyberarmEngine V2 Architecture + +Engine + -> [States] + -> [ParallelStates] + +Start with an Engine and override setup(): + +public class Engine extends CyberarmEngineV2 { + public void setup() { + addState(new State(arguments)); + } +} + +NOTE: states do not need to be passed the instance of Engine as they have a field 'cyberarmEngine' + which is set to CyberarmEngineV2.instance when they are created. + +States can have 'children' which are also States, which run in parallel with their parent. There is +no fixed limit to how many grandchildren can exist (Children themselves can have children.): + +public class State extends CyberarmEngineStateV2 { + public init() { + addParallelState(new ParallelState(arguments)); + } + + public exec() { + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/readme.md b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/readme.md deleted file mode 100644 index 2f7e3a4..0000000 --- a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/readme.md +++ /dev/null @@ -1,121 +0,0 @@ -## TeamCode Module - -Welcome! - -This module, TeamCode, is the place where you will write/paste the code for your team's -robot controller App. This module is currently empty (a clean slate) but the -process for adding OpModes is straightforward. - -## Creating your own OpModes - -The easiest way to create your own OpMode is to copy a Sample OpMode and make it your own. - -Sample opmodes exist in the FtcRobotController module. -To locate these samples, find the FtcRobotController module in the "Project/Android" tab. - -Expand the following tree elements: - FtcRobotController / java / org.firstinspires.ftc.robotcontroller / external / samples - -A range of different samples classes can be seen in this folder. -The class names follow a naming convention which indicates the purpose of each class. -The full description of this convention is found in the samples/sample_convention.md file. - -A brief synopsis of the naming convention is given here: -The prefix of the name will be one of the following: - -* Basic: This is a minimally functional OpMode used to illustrate the skeleton/structure - of a particular style of OpMode. These are bare bones examples. -* Sensor: This is a Sample OpMode that shows how to use a specific sensor. - It is not intended as a functioning robot, it is simply showing the minimal code - required to read and display the sensor values. -* Hardware: This is not an actual OpMode, but a helper class that is used to describe - one particular robot's hardware devices: eg: for a Pushbot. Look at any - Pushbot sample to see how this can be used in an OpMode. - Teams can copy one of these to create their own robot definition. -* Pushbot: This is a Sample OpMode that uses the Pushbot robot structure as a base. -* Concept: This is a sample OpMode that illustrates performing a specific function or concept. - These may be complex, but their operation should be explained clearly in the comments, - or the header should reference an external doc, guide or tutorial. -* Library: This is a class, or set of classes used to implement some strategy. - These will typically NOT implement a full OpMode. Instead they will be included - by an OpMode to provide some stand-alone capability. - -Once you are familiar with the range of samples available, you can choose one to be the -basis for your own robot. In all cases, the desired sample(s) needs to be copied into -your TeamCode module to be used. - -This is done inside Android Studio directly, using the following steps: - - 1) Locate the desired sample class in the Project/Android tree. - - 2) Right click on the sample class and select "Copy" - - 3) Expand the TeamCode / java folder - - 4) Right click on the org.firstinspires.ftc.teamcode folder and select "Paste" - - 5) You will be prompted for a class name for the copy. - Choose something meaningful based on the purpose of this class. - Start with a capital letter, and remember that there may be more similar classes later. - -Once your copy has been created, you should prepare it for use on your robot. -This is done by adjusting the OpMode's name, and enabling it to be displayed on the -Driver Station's OpMode list. - -Each OpMode sample class begins with several lines of code like the ones shown below: - -``` - @TeleOp(name="Template: Linear OpMode", group="Linear Opmode") - @Disabled -``` - -The name that will appear on the driver station's "opmode list" is defined by the code: - ``name="Template: Linear OpMode"`` -You can change what appears between the quotes to better describe your opmode. -The "group=" portion of the code can be used to help organize your list of OpModes. - -As shown, the current OpMode will NOT appear on the driver station's OpMode list because of the - ``@Disabled`` annotation which has been included. -This line can simply be deleted , or commented out, to make the OpMode visible. - - - -## ADVANCED Multi-Team App management: Cloning the TeamCode Module - -In some situations, you have multiple teams in your club and you want them to all share -a common code organization, with each being able to *see* the others code but each having -their own team module with their own code that they maintain themselves. - -In this situation, you might wish to clone the TeamCode module, once for each of these teams. -Each of the clones would then appear along side each other in the Android Studio module list, -together with the FtcRobotController module (and the original TeamCode module). - -Selective Team phones can then be programmed by selecting the desired Module from the pulldown list -prior to clicking to the green Run arrow. - -Warning: This is not for the inexperienced Software developer. -You will need to be comfortable with File manipulations and managing Android Studio Modules. -These changes are performed OUTSIDE of Android Studios, so close Android Studios before you do this. - -Also.. Make a full project backup before you start this :) - -To clone TeamCode, do the following: - -Note: Some names start with "Team" and others start with "team". This is intentional. - -1) Using your operating system file management tools, copy the whole "TeamCode" - folder to a sibling folder with a corresponding new name, eg: "Team0417". - -2) In the new Team0417 folder, delete the TeamCode.iml file. - -3) the new Team0417 folder, rename the "src/main/java/org/firstinspires/ftc/teamcode" folder - to a matching name with a lowercase 'team' eg: "team0417". - -4) In the new Team0417/src/main folder, edit the "AndroidManifest.xml" file, change the line that contains - package="org.firstinspires.ftc.teamcode" - to be - package="org.firstinspires.ftc.team0417" - -5) Add: include ':Team0417' to the "/settings.gradle" file. - -6) Open up Android Studios and clean out any old files by using the menu to "Build/Clean Project"" \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/TimeCraftersConfiguration.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/TimeCraftersConfiguration.java new file mode 100644 index 0000000..ce41fd5 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/TimeCraftersConfiguration.java @@ -0,0 +1,150 @@ +package org.timecrafters.TimeCraftersConfigurationTool; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.Config; +import org.timecrafters.TimeCraftersConfigurationTool.backend.Settings; +import org.timecrafters.TimeCraftersConfigurationTool.backend.TAC; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Presets; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Variable; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.ActionDeserializer; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.ActionSerializer; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.ConfigDeserializer; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.ConfigSerializer; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.ConfigurationDeserializer; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.ConfigurationSerializer; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.GroupDeserializer; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.GroupSerializer; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.PresetsDeserializer; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.PresetsSerializer; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.SettingsDeserializer; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.SettingsSerializer; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.VariableDeserializer; +import org.timecrafters.TimeCraftersConfigurationTool.serializers.VariableSerializer; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; + +public class TimeCraftersConfiguration { + private static final String TAG = "TCT|TCConfig"; + private Config config; + + public TimeCraftersConfiguration() { + Settings settings = loadSettings(); + this.config = loadConfig(settings.config); + } + + public TimeCraftersConfiguration(String configName) { + this.config = loadConfig(configName); + } + + public Config getConfig() { + return config; + } + + public Group group(String groupName) { + for (final Group group : config.getGroups()) { + if (group.name.trim().equals(groupName.trim())) { + return group; + } + } + + throw(new RuntimeException("Failed to find a group named:\"" + groupName.trim() + "\" in config \"" + config.getName() + "\"")); + } + + public Action action(String groupName, String actionName) { + final Group group = group(groupName); + + for (Action action : group.getActions()) { + if (action.name.trim().equals(actionName.trim())) { + return action; + } + } + + throw(new RuntimeException("Failed to find an action named:\"" + actionName.trim() + "\" in group \"" + groupName.trim() + "\" in config \"" + config.getName() + "\"")); + } + + public Variable variable(String groupName, String actionName, String variableName) { + final Action action = action(groupName, groupName); + + for (Variable variable : action.getVariables()) { + if (variable.name.trim().equals(variableName.trim())) { + return variable; + } + } + + throw(new RuntimeException("Failed to find a variable named \"" + variableName.trim() + "\" in action:\"" + actionName.trim() + + "\" in group \"" + groupName.trim() + "\" in config \"" + config.getName() + "\"")); + } + + private Settings loadSettings() { + File settingsFile = new File(TAC.SETTINGS_PATH); + + if (!settingsFile.exists()) { + throw( new RuntimeException("Unable to load settings.json, file does not exist!") ); + } + + try { + return gsonForSettings().fromJson(new FileReader(settingsFile), Settings.class); + } catch (FileNotFoundException e) { + throw( new RuntimeException("Unable to load settings.json") ); + } + } + + private Config loadConfig(String name) { + if (name.equals("")) { + throw(new RuntimeException("Cannot load a config with an empty name!")); + } + + String path = TAC.CONFIGS_PATH + File.separator + name + ".json"; + File configFile = new File(path); + + if (configFile.exists() && configFile.isFile()) { + try { + Config config = gsonForConfig().fromJson(new FileReader(configFile), Config.class); + config.setName(name); + + return config; + } catch (FileNotFoundException e) { + e.printStackTrace(); + throw(new RuntimeException("Unable to find a config file named \"" + name + "\"")); + } + } else { + throw(new RuntimeException("Unable to find a config file named \"" + name + "\"")); + } + } + + private Gson gsonForSettings() { + return new GsonBuilder() + .registerTypeAdapter(Settings.class, new SettingsSerializer()) + .registerTypeAdapter(Settings.class, new SettingsDeserializer()) + .create(); + } + + public Gson gsonForConfig() { + return new GsonBuilder() + .registerTypeAdapter(Config.class, new ConfigSerializer()) + .registerTypeAdapter(Config.class, new ConfigDeserializer()) + + .registerTypeAdapter(Configuration.class, new ConfigurationSerializer()) + .registerTypeAdapter(Configuration.class, new ConfigurationDeserializer()) + + .registerTypeAdapter(Group.class, new GroupSerializer()) + .registerTypeAdapter(Group.class, new GroupDeserializer()) + + .registerTypeAdapter(Action.class, new ActionSerializer()) + .registerTypeAdapter(Action.class, new ActionDeserializer()) + + .registerTypeAdapter(Variable.class, new VariableSerializer()) + .registerTypeAdapter(Variable.class, new VariableDeserializer()) + + .registerTypeAdapter(Presets.class, new PresetsSerializer()) + .registerTypeAdapter(Presets.class, new PresetsDeserializer()) + .create(); + } +} diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Config.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Config.java new file mode 100644 index 0000000..894bb52 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Config.java @@ -0,0 +1,44 @@ +package org.timecrafters.TimeCraftersConfigurationTool.backend; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Presets; + +import java.util.ArrayList; +import java.util.Date; + +public class Config { + private String name; + private Configuration configuration; + private ArrayList groups; + private Presets presets; + + public Config(String name) { + this.name = name; + this.configuration = new Configuration(new Date(), new Date(), TAC.CONFIG_SPEC_VERSION, 0); + groups = new ArrayList<>(); + presets = new Presets(new ArrayList(), new ArrayList()); + } + + public Config(Configuration configuration, ArrayList groups, Presets presets) { + this.configuration = configuration; + this.groups = groups; + this.presets = presets; + } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public Configuration getConfiguration() { + return configuration; + } + + public Presets getPresets() { + return presets; + } + + public ArrayList getGroups() { + return groups; + } +} diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Settings.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Settings.java new file mode 100644 index 0000000..b093e73 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/Settings.java @@ -0,0 +1,12 @@ +package org.timecrafters.TimeCraftersConfigurationTool.backend; + +public class Settings { + public String hostname, config; + public int port; + + public Settings(String hostname, int port, String config) { + this.hostname = hostname; + this.port = port; + this.config = config; + } +} diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/TAC.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/TAC.java new file mode 100644 index 0000000..ed0bb14 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/TAC.java @@ -0,0 +1,14 @@ +package org.timecrafters.TimeCraftersConfigurationTool.backend; + +import android.os.Environment; + +import java.io.File; + +public class TAC { + // TODO: Update filesystem handling + public static final String ROOT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "TimeCrafters_Configuration_Tool", + CONFIGS_PATH = ROOT_PATH + File.separator + "/configs", + SETTINGS_PATH = ROOT_PATH + File.separator + "settings.json"; + + public static final int CONFIG_SPEC_VERSION = 2; +} diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Action.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Action.java new file mode 100644 index 0000000..c55bbea --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Action.java @@ -0,0 +1,18 @@ +package org.timecrafters.TimeCraftersConfigurationTool.backend.config; + +import java.util.ArrayList; + +public class Action { + public String name, comment; + public boolean enabled; + private ArrayList variables; + + public Action(String name, String comment, boolean enabled, ArrayList variables) { + this.name = name; + this.comment = comment; + this.enabled = enabled; + this.variables = variables; + } + + public ArrayList getVariables() { return variables; } +} diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Configuration.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Configuration.java new file mode 100644 index 0000000..1ad0387 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Configuration.java @@ -0,0 +1,20 @@ +package org.timecrafters.TimeCraftersConfigurationTool.backend.config; + +import java.util.Date; + +public class Configuration { + public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss Z"; + + public Date createdAt, updatedAt; + private int specVersion; + public int revision; + + public Configuration(Date createdAt, Date updatedAt, int specVersion, int revision) { + this.createdAt = createdAt; + this.updatedAt = updatedAt; + this.specVersion = specVersion; + this.revision = revision; + } + + public int getSpecVersion() { return specVersion; } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Group.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Group.java new file mode 100644 index 0000000..40702ba --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Group.java @@ -0,0 +1,27 @@ +package org.timecrafters.TimeCraftersConfigurationTool.backend.config; + +import java.util.ArrayList; + +public class Group { + public String name; + private ArrayList actions; + + public Group(String name, ArrayList actions) { + this.name = name; + this.actions = actions; + } + + public static boolean nameIsUnique(ArrayList groups, String name) { + for (Group group: groups) { + if (group.name.equals(name)) { + return false; + } + } + + return true; + } + + public ArrayList getActions() { + return actions; + } +} diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Presets.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Presets.java new file mode 100644 index 0000000..a1612c5 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Presets.java @@ -0,0 +1,22 @@ +package org.timecrafters.TimeCraftersConfigurationTool.backend.config; + + +import java.util.ArrayList; + +public class Presets { + private ArrayList groups; + private ArrayList actions; + + public Presets(ArrayList groups, ArrayList actions) { + this.groups = groups; + this.actions = actions; + } + + public ArrayList getGroups() { + return groups; + } + + public ArrayList getActions() { + return actions; + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Variable.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Variable.java new file mode 100644 index 0000000..cca427c --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/backend/config/Variable.java @@ -0,0 +1,88 @@ +package org.timecrafters.TimeCraftersConfigurationTool.backend.config; + +public class Variable { + public String name; + private String value; + + public Variable(String name, String value) { + this.name = name; + this.value = value; + } + + public String rawValue() { + return value; + } + + public T value() { + return valueOf(value); + } + + public void setValue(String value) { + this.value = value; + } + + @SuppressWarnings("unchecked") + static public T valueOf(String value) { + String[] split = value.split("x", 2); +// Log.d("Variable", "valueOf split: " + Arrays.toString(split)); + + switch (split[0]) { + case "B": { + return (T) Boolean.valueOf(split[(split.length-1)]); + } + case "D": { + return (T) Double.valueOf(split[(split.length-1)]); + } + case "F": { + return (T) Float.valueOf(split[(split.length-1)]); + } + case "I": { + return (T) Integer.valueOf(split[(split.length-1)]); + } + case "L": { + return (T) Long.valueOf(split[(split.length-1)]); + } + case "S": { + String string = ""; + int i = 0; + for(String str : split) { + if (i == 0) { i++; continue; } + + string += str; + } + return (T) string; + } + default: { + return null; + } + } + } + + static public String typeOf(String value) { + String[] split = value.split("x"); + + switch (split[0]) { + case "B": { + return "Boolean"; + } + case "D": { + return "Double"; + } + case "F": { + return "Float"; + } + case "I": { + return "Integer"; + } + case "L": { + return "Long"; + } + case "S": { + return "String"; + } + default: { + return "=!UNKNOWN!="; + } + } + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ActionDeserializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ActionDeserializer.java new file mode 100644 index 0000000..d44ef8f --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ActionDeserializer.java @@ -0,0 +1,32 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Variable; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ActionDeserializer implements JsonDeserializer { + @Override + public Action deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + + final String name = jsonObject.get("name").getAsString(); + final String comment = jsonObject.get("comment").getAsString(); + final boolean enabled = jsonObject.get("enabled").getAsBoolean(); + Variable[] variablesArray = context.deserialize(jsonObject.get("variables"), Variable[].class); + + List variablesList = Arrays.asList(variablesArray); + ArrayList variables = new ArrayList<>(variablesList); + + return new Action(name, comment, enabled, variables); + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ActionSerializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ActionSerializer.java new file mode 100644 index 0000000..e369e56 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ActionSerializer.java @@ -0,0 +1,26 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Variable; + +import java.lang.reflect.Type; + +public class ActionSerializer implements JsonSerializer { + @Override + public JsonElement serialize(Action action, Type type, JsonSerializationContext context) { + JsonObject container = new JsonObject(); + + container.add("name", new JsonPrimitive(action.name)); + container.add("comment", new JsonPrimitive(action.comment)); + container.add("enabled", new JsonPrimitive(action.enabled)); + container.add("variables", context.serialize(action.getVariables().toArray(), Variable[].class)); + + return container; + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ConfigDeserializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ConfigDeserializer.java new file mode 100644 index 0000000..55b928e --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ConfigDeserializer.java @@ -0,0 +1,34 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.Config; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Presets; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ConfigDeserializer implements JsonDeserializer { + @Override + public Config deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + JsonObject data = jsonObject.get("data").getAsJsonObject(); + + Configuration configuration = context.deserialize(jsonObject.get("config"), Configuration.class); + Group[] groupsArray = context.deserialize(data.get("groups"), Group[].class); + List groupsList = Arrays.asList(groupsArray); + ArrayList groups = new ArrayList<>(groupsList); + + Presets presets = context.deserialize(data.get("presets"), Presets.class); + + return new Config(configuration, groups, presets); + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ConfigSerializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ConfigSerializer.java new file mode 100644 index 0000000..5682f05 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ConfigSerializer.java @@ -0,0 +1,32 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.Config; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group; + +import java.lang.reflect.Type; + +public class ConfigSerializer implements JsonSerializer { + @Override + public JsonElement serialize(Config config, Type type, JsonSerializationContext context) { + JsonObject container = new JsonObject(); + JsonObject result = new JsonObject(); + JsonObject presets = new JsonObject(); + container.add("config", context.serialize(config.getConfiguration(), Configuration.class)); + result.add("groups", context.serialize(config.getGroups().toArray(), Group[].class)); + + presets.add("groups", context.serialize(config.getPresets().getGroups().toArray(), Group[].class)); + presets.add("actions", context.serialize(config.getPresets().getActions().toArray(), Action[].class)); + + result.add("presets", presets); + container.add("data", result); + + return container; + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ConfigurationDeserializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ConfigurationDeserializer.java new file mode 100644 index 0000000..799da84 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ConfigurationDeserializer.java @@ -0,0 +1,36 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration; + +import java.lang.reflect.Type; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class ConfigurationDeserializer implements JsonDeserializer { + @Override + public Configuration deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { + JsonObject config = json.getAsJsonObject(); + + SimpleDateFormat dateFormat = new SimpleDateFormat(Configuration.DATE_FORMAT); + Date createdAt = new Date(); + Date updatedAt = new Date(); + try { + createdAt = dateFormat.parse(config.get("created_at").getAsString()); + updatedAt = dateFormat.parse(config.get("updated_at").getAsString()); + } catch (ParseException e) { + e.printStackTrace(); + } + + final int spec_version = config.get("spec_version").getAsInt(); + final int revision = config.get("revision").getAsInt(); + + return new Configuration(createdAt, updatedAt, spec_version, revision); + } +} diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ConfigurationSerializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ConfigurationSerializer.java new file mode 100644 index 0000000..73c1484 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/ConfigurationSerializer.java @@ -0,0 +1,28 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Configuration; + +import java.lang.reflect.Type; +import java.text.SimpleDateFormat; + +public class ConfigurationSerializer implements JsonSerializer { + @Override + public JsonElement serialize(Configuration configuration, Type type, JsonSerializationContext context) { + JsonObject container = new JsonObject(); + + SimpleDateFormat dateFormat = new SimpleDateFormat(Configuration.DATE_FORMAT); + + container.add("created_at", new JsonPrimitive(dateFormat.format(configuration.createdAt))); + container.add("updated_at", new JsonPrimitive(dateFormat.format(configuration.updatedAt))); + container.add("spec_version", new JsonPrimitive(configuration.getSpecVersion())); + container.add("revision", new JsonPrimitive(configuration.revision)); + + return container; + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/GroupDeserializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/GroupDeserializer.java new file mode 100644 index 0000000..8398a7c --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/GroupDeserializer.java @@ -0,0 +1,30 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class GroupDeserializer implements JsonDeserializer { + @Override + public Group deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + + final String name = jsonObject.get("name").getAsString(); + Action[] actionsArray = context.deserialize(jsonObject.get("actions"), Action[].class); + + List actionsList = Arrays.asList(actionsArray); + ArrayList actions = new ArrayList<>(actionsList); + + return new Group(name, actions); + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/GroupSerializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/GroupSerializer.java new file mode 100644 index 0000000..f555cc0 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/GroupSerializer.java @@ -0,0 +1,24 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group; + +import java.lang.reflect.Type; + +public class GroupSerializer implements JsonSerializer { + @Override + public JsonElement serialize(Group group, Type type, JsonSerializationContext context) { + JsonObject container = new JsonObject(); + + container.add("name", new JsonPrimitive(group.name)); + container.add("actions", context.serialize(group.getActions().toArray(), Action[].class)); + + return container; + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/PresetsDeserializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/PresetsDeserializer.java new file mode 100644 index 0000000..50d7263 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/PresetsDeserializer.java @@ -0,0 +1,33 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Presets; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PresetsDeserializer implements JsonDeserializer { + @Override + public Presets deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + + Group[] GroupsArray = context.deserialize(jsonObject.get("groups"), Group[].class); + Action[] actionsArray = context.deserialize(jsonObject.get("actions"), Action[].class); + + List groupsList = Arrays.asList(GroupsArray); + ArrayList groups = new ArrayList<>(groupsList); + List actionsList = Arrays.asList(actionsArray); + ArrayList actions = new ArrayList<>(actionsList); + + return new Presets(groups, actions); + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/PresetsSerializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/PresetsSerializer.java new file mode 100644 index 0000000..21276a2 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/PresetsSerializer.java @@ -0,0 +1,24 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Action; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Group; +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Presets; + +import java.lang.reflect.Type; + +public class PresetsSerializer implements JsonSerializer { + @Override + public JsonElement serialize(Presets presets, Type type, JsonSerializationContext context) { + JsonObject container = new JsonObject(); + + container.add("groups", context.serialize(presets.getGroups().toArray(), Group[].class)); + container.add("actions", context.serialize(presets.getActions().toArray(), Action[].class)); + + return container; + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/SettingsDeserializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/SettingsDeserializer.java new file mode 100644 index 0000000..9406c46 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/SettingsDeserializer.java @@ -0,0 +1,25 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.Settings; + +import java.lang.reflect.Type; + +public class SettingsDeserializer implements JsonDeserializer { + @Override + public Settings deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + JsonObject data = jsonObject.get("data").getAsJsonObject(); + + return new Settings( + data.get("hostname").getAsString(), + data.get("port").getAsInt(), + data.get("config").getAsString() + ); + } +} diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/SettingsSerializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/SettingsSerializer.java new file mode 100644 index 0000000..fd101a5 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/SettingsSerializer.java @@ -0,0 +1,27 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.Settings; + +import java.lang.reflect.Type; + +public class SettingsSerializer implements JsonSerializer { + @Override + public JsonElement serialize(Settings settings, Type type, JsonSerializationContext context) { + JsonObject container = new JsonObject(); + JsonObject result = new JsonObject(); + result.add("hostname", new JsonPrimitive(settings.hostname)); + result.add("port", new JsonPrimitive(settings.port)); + result.add("config", new JsonPrimitive(settings.config)); + + container.add("data", result); + + return container; + } +} + diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/VariableDeserializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/VariableDeserializer.java new file mode 100644 index 0000000..698dc5d --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/VariableDeserializer.java @@ -0,0 +1,23 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Variable; + +import java.lang.reflect.Type; + +public class VariableDeserializer implements JsonDeserializer { + @Override + public Variable deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + + final String name = jsonObject.get("name").getAsString(); + final String value = jsonObject.get("value").getAsString(); + + return new Variable(name, value); + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/VariableSerializer.java b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/VariableSerializer.java new file mode 100644 index 0000000..35ac60a --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/TimeCraftersConfigurationTool/serializers/VariableSerializer.java @@ -0,0 +1,23 @@ +package org.timecrafters.TimeCraftersConfigurationTool.serializers; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +import org.timecrafters.TimeCraftersConfigurationTool.backend.config.Variable; + +import java.lang.reflect.Type; + +public class VariableSerializer implements JsonSerializer { + @Override + public JsonElement serialize(Variable variable, Type type, JsonSerializationContext context) { + JsonObject container = new JsonObject(); + + container.add("name", new JsonPrimitive(variable.name)); + container.add("value", new JsonPrimitive(variable.rawValue())); + + return container; + } +} \ No newline at end of file diff --git a/TeamCode/src/main/java/org/timecrafters/UltimateGoal/HardwareTesting/ControlHubTest.java b/TeamCode/src/main/java/org/timecrafters/UltimateGoal/HardwareTesting/ControlHubTest.java new file mode 100644 index 0000000..442a8b5 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/UltimateGoal/HardwareTesting/ControlHubTest.java @@ -0,0 +1,27 @@ +package org.timecrafters.UltimateGoal.HardwareTesting; + +import org.cyberarm.engine.V2.CyberarmState; +import org.timecrafters.UltimateGoal.Robot; + + +public class ControlHubTest extends CyberarmState { + + private Robot robot; + private float angle = 0; + + public ControlHubTest(Robot robot) { + this.robot = robot; + } + + @Override + public void exec() { + angle = robot.imu.getAngularOrientation().firstAngle; + } + + @Override + public void telemetry() { + engine.telemetry.addLine("Greetings"); + engine.telemetry.addData("Angle", angle); + + } +} diff --git a/TeamCode/src/main/java/org/timecrafters/UltimateGoal/HardwareTesting/EncoderTest.java b/TeamCode/src/main/java/org/timecrafters/UltimateGoal/HardwareTesting/EncoderTest.java new file mode 100644 index 0000000..9c08417 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/UltimateGoal/HardwareTesting/EncoderTest.java @@ -0,0 +1,66 @@ +package org.timecrafters.UltimateGoal.HardwareTesting; + +import org.cyberarm.engine.V2.CyberarmState; +import org.timecrafters.UltimateGoal.Robot; + + +public class EncoderTest extends CyberarmState { + + private Robot robot; + private int ticksLeft; + private int ticksRight; + private double biasLeft = 0; + private double biasRight = 0; + + public EncoderTest(Robot robot) { + this.robot = robot; + } + + @Override + public void exec() { + robot.updateLocation(); + + if (runTime() < 3000) { + robot.setDrivePower(0.5, 0.5); + + ticksLeft=robot.encoderLeft.getCurrentPosition(); + ticksRight=robot.encoderRight.getCurrentPosition(); + + + + } else { + robot.encoderLeft.setPower(0.0); + robot.encoderRight.setPower(0.0); + + double ticksExtreme; + if (Math.abs(ticksLeft) < Math.abs(ticksRight)) { + ticksExtreme = ticksLeft; + } else { + ticksExtreme = ticksRight; + } + biasLeft = ticksExtreme/ticksLeft; + biasRight = ticksExtreme/ticksRight; + } + + } + + @Override + public void telemetry() { + engine.telemetry.addLine("Biases"); + engine.telemetry.addData("Left", biasLeft); + engine.telemetry.addData("Right", biasRight); + engine.telemetry.addLine(); + engine.telemetry.addLine("Latency Values"); + engine.telemetry.addData("Y", robot.getLocationY()); + engine.telemetry.addData("X", robot.getLocationX()); + engine.telemetry.addLine(); + engine.telemetry.addData("Rotation", robot.getRotation()); + engine.telemetry.addLine(); + engine.telemetry.addLine("Actual Values"); + engine.telemetry.addData("Left", ticksLeft); + engine.telemetry.addData("Right", ticksRight); +// engine.telemetry.addLine(""); +// engine.telemetry.addData("Front", robot.encoderFront); +// engine.telemetry.addData("Back", robot.encoderBack); + } +} diff --git a/TeamCode/src/main/java/org/timecrafters/UltimateGoal/HardwareTesting/TestingEngine.java b/TeamCode/src/main/java/org/timecrafters/UltimateGoal/HardwareTesting/TestingEngine.java new file mode 100644 index 0000000..8e8f22e --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/UltimateGoal/HardwareTesting/TestingEngine.java @@ -0,0 +1,28 @@ +package org.timecrafters.UltimateGoal.HardwareTesting; + +import com.qualcomm.robotcore.eventloop.opmode.TeleOp; + +import org.cyberarm.engine.V2.CyberarmEngine; +import org.timecrafters.UltimateGoal.Robot; + +@TeleOp (name = "Encoder test", group = "test") +public class TestingEngine extends CyberarmEngine { + + private Robot robot; + + @Override + public void init() { + robot = new Robot(hardwareMap); + robot.initHardware(); + super.init(); + } + + + + @Override + public void setup() { + addState(new EncoderTest(robot)); + } + + +} diff --git a/TeamCode/src/main/java/org/timecrafters/UltimateGoal/Robot.java b/TeamCode/src/main/java/org/timecrafters/UltimateGoal/Robot.java new file mode 100644 index 0000000..7b50866 --- /dev/null +++ b/TeamCode/src/main/java/org/timecrafters/UltimateGoal/Robot.java @@ -0,0 +1,103 @@ +package org.timecrafters.UltimateGoal; + +import com.qualcomm.hardware.bosch.BNO055IMU; +import com.qualcomm.robotcore.hardware.DcMotor; +import com.qualcomm.robotcore.hardware.DcMotorSimple; +import com.qualcomm.robotcore.hardware.HardwareMap; + +import org.cyberarm.NeXT.StateConfiguration; + +public class Robot { + + private HardwareMap hardwareMap; + + public Robot(HardwareMap hardwareMap) { + this.hardwareMap = hardwareMap; + } + + public StateConfiguration stateConfiguration = new StateConfiguration(); + public BNO055IMU imu; + + //drive system + + public DcMotor encoderFront; + public DcMotor encoderLeft; + public DcMotor encoderBack; + public DcMotor encoderRight; + + double BIAS_LEFT = 1.0; + double BIAS_RIGHT = 0.6815; + + //Robot Localizatoin + private double locationX; + private double locationY; + private float rotation; + + private int encoderFrontPrevious = 0; + private int encoderLeftPrevious = 0; + private int encoderBackPrevious = 0; + private int encoderRightPrevious = 0; + private float rotationPrevious = 0; + + public void initHardware() { + imu = hardwareMap.get(BNO055IMU.class, "imu"); +// encoderFront = hardwareMap.dcMotor.get("encoderFront"); + encoderLeft = hardwareMap.dcMotor.get("encoderLeft"); +// encoderBack = hardwareMap.dcMotor.get("encoderBack"); + encoderRight = hardwareMap.dcMotor.get("encoderRight"); + + encoderLeft.setDirection(DcMotorSimple.Direction.REVERSE); + + encoderLeft.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE); + encoderRight.setZeroPowerBehavior(DcMotor.ZeroPowerBehavior.BRAKE); + + BNO055IMU.Parameters parameters = new BNO055IMU.Parameters(); + + parameters.mode = BNO055IMU.SensorMode.IMU; + parameters.angleUnit = BNO055IMU.AngleUnit.DEGREES; + parameters.accelUnit = BNO055IMU.AccelUnit.METERS_PERSEC_PERSEC; + parameters.loggingEnabled = false; + + imu.initialize(parameters); + } + + public void setDrivePower(double powerLeft, double powerRight){ + encoderLeft.setPower(powerLeft * BIAS_LEFT); + encoderRight.setPower(powerRight * BIAS_RIGHT); + + } + + public void updateLocation(){ + rotation = imu.getAngularOrientation().firstAngle; + float rotationChange = rotation - rotationPrevious; + int encoderLeftCurrent = encoderLeft.getCurrentPosition(); + int encoderRightCurrent = encoderRight.getCurrentPosition(); + double encoderLeftChange = encoderLeftCurrent - encoderLeftPrevious; + double encoderRightChange = encoderRightCurrent - encoderRightPrevious; + + encoderLeftPrevious = encoderLeftCurrent; + encoderRightPrevious = encoderRightCurrent; + rotationPrevious = rotation; + + double average = (encoderLeftChange+encoderRightChange)/2; + + double xChange = average * (Math.sin(Math.toRadians(rotationChange))); + double yChange = average * (Math.cos(Math.toRadians(rotationChange))); + + locationX += xChange; + locationY += yChange; + + } + + public double getRotation() { + return rotation; + } + + public double getLocationX() { + return locationX; + } + + public double getLocationY() { + return locationY; + } +}