package jaci.openrio.toast.lib.module; import edu.wpi.first.wpilibj.DriverStation; import edu.wpi.first.wpilibj.IterativeRobot; import edu.wpi.first.wpilibj.RobotBase; import edu.wpi.first.wpilibj.SampleRobot; import jaci.openrio.toast.core.StateTracker; import jaci.openrio.toast.core.Toast; import jaci.openrio.toast.core.loader.annotation.NoLoad; import jaci.openrio.toast.core.loader.module.ModuleCandidate; import sun.misc.Unsafe; import sun.reflect.ReflectionFactory; import java.io.File; import java.lang.reflect.*; import java.util.HashMap; /** * The 'ModuleWrapper' is a Wrapper for the ToastModule class around classes implementing RobotBase. The Goal of this * class is to support loading of compiled FRC Program Jars that aren't designed for Toast (e.g. FRCUserProgram.jar). * This allows importing of code that isn't designed for module use. * * Keep in mind that only RobotBase and IterativeRobot are supported, with SampleRobot being half-supported (no ticking * support). StartCompetition is started in a new thread for code that substitutes its own periodic ticker, although * this usage is discouraged. * * @author Jaci */ @NoLoad public class ModuleWrapper extends ToastModule { File parentFile; RobotBase base; Class<? extends RobotBase> baseClass; ModuleCandidate candidate; boolean isIterative; boolean isSample; public static Unsafe theUnsafe; public ModuleWrapper(File parentFile, Class<? extends RobotBase> robotBaseClass, ModuleCandidate candidate) { this.parentFile = parentFile; this.baseClass = robotBaseClass; this.candidate = candidate; } @Override public void onConstruct() { try { // Thanks, NetworkTables if (theUnsafe == null) { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); theUnsafe = (Unsafe) f.get(null); } this.base = baseClass.cast(theUnsafe.allocateInstance(baseClass)); } catch (InstantiationException | IllegalAccessException | NoSuchFieldException e) { Toast.log().error("Could not instantiate Wrapped Module: " + e); } this.isIterative = base instanceof IterativeRobot; this.isSample = base instanceof SampleRobot; } @Override public String getModuleName() { return parentFile.getName() + "!wrapper"; } @Override public String getModuleVersion() { return "0.0.0"; } @Override public void prestart() { try { Field m_ds = RobotBase.class.getDeclaredField("m_ds"); m_ds.setAccessible(true); m_ds.set(base, DriverStation.getInstance()); } catch (NoSuchFieldException | IllegalAccessException e) { Toast.log().error("Error while setting DriverStation for wrapped module: " + e); } reflectMethod("prestart", base); } @Override public void start() { Thread thread = new Thread(() -> { if (isSample || isIterative) reflectMethod("robotInit", base); else { base.startCompetition(); } stateSetup(); }); thread.start(); } public HashMap<String, String> getCustomData() { HashMap<String, String> hash = new HashMap<>(); hash.put("Iterative", Boolean.toString(isIterative)); hash.put("Sample", Boolean.toString(isSample)); hash.put("Wrapped Class", base.getClass().getName()); return hash; } public void stateSetup() { if (isIterative) { IterativeRobot robot = (IterativeRobot) base; StateTracker.addTicker((state) -> { switch (state) { case DISABLED: robot.disabledPeriodic(); break; case AUTONOMOUS: robot.autonomousPeriodic(); break; case TELEOP: robot.teleopPeriodic(); break; case TEST: robot.testPeriodic(); break; } }); StateTracker.addTransition((state, oldstate) -> { switch (state) { case DISABLED: robot.disabledInit(); break; case AUTONOMOUS: robot.autonomousInit(); break; case TELEOP: robot.teleopInit(); break; case TEST: robot.testInit(); break; } }); } } public void reflectMethod(String method, Object base) { // Why WPILib decided to leave these methods as 'protected' I will never know // Kids, don't do this. Method m = getMethod(method, base); if (m == null) Toast.log().error("Could not instantiate Wrapped Module reflection on " + method + " as it does not exist!"); else { try { m.setAccessible(true); m.invoke(base); } catch (InvocationTargetException | IllegalAccessException e) { Toast.log().error("Could not instantiate Wrapped Module reflection on " + method + ": " + e); Toast.log().exception(e); } } } public Method getMethod(String theMethod, Object base) { Class<?> clazz = base.getClass(); while (clazz != null) { Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { // Test any other things about it beyond the name... if (method.getName().equals(theMethod)) { return method; } } clazz = clazz.getSuperclass(); } return null; } }