diff --git a/src/main/java/lib2202/nt/NtBind.java b/src/main/java/lib2202/nt/NtBind.java new file mode 100644 index 00000000..ad7920ae --- /dev/null +++ b/src/main/java/lib2202/nt/NtBind.java @@ -0,0 +1,180 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package lib2202.nt; + +import java.lang.reflect.Field; +import java.util.LinkedList; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import edu.wpi.first.networktables.NetworkTable; +import edu.wpi.first.networktables.NetworkTableInstance; +import edu.wpi.first.wpilibj2.command.SubsystemBase; + +public class NtBind extends SubsystemBase { + NetworkTable table = NetworkTableInstance.getDefault().getTable("root"); + int count; + + interface apply { + Boolean apply(); + } + + public apply buildDoubleInput(NtBind ntBind, NetworkTable table, String name, Field f, Object state) { + var a = new doubleApply(); + var nt = table.getEntry(name); + + a.current = () -> { + try { + return f.getDouble(state); + } catch (Exception e) { + ntBind.ReportError(table.getPath(), name, e); + return 0.0; + } + }; + + a.set = (Double val) -> { + try { + f.setDouble(state, val); + } catch (Exception e) { + ntBind.ReportError(table.getPath(), name, e); + } + }; + + a.next = () -> nt.getDouble(a.current.get()); + + return a; + } + + + public apply buildDoubleOutput(NtBind ntBind, NetworkTable table, String name, Field f, Object state) { + var a = new doubleApply(); + var nt = table.getEntry(name); + + a.current = () -> nt.getDouble(0.0); + a.set = (Double val) -> nt.setDouble(val); + a.next = () -> { + try { + return f.getDouble(state); + } catch (Exception e) { + ntBind.ReportError(table.getPath(), name, e); + return 0.0; + } + }; + + return a; + } + + class doubleApply implements apply { + Supplier current; + Consumer set; + Supplier next; + + public Boolean apply() { + var next = this.next.get(); + var current = this.current.get(); + + if (next != current) { + set.accept(next); + return true; + } + + return false; + } + } + + class boundData implements apply { + LinkedList applys = new LinkedList(); + Runnable onChange; + + public Boolean apply() { + Boolean isChanged = false; + + for (var a : applys) { + isChanged |= a.apply(); + } + + if (isChanged && onChange != null) { + onChange.run(); + } + + return isChanged; + } + } + + LinkedList boundObjects = new LinkedList(); + + public TStateObject bind(TStateObject state, String name, Runnable onChange) { + var classFields = state.getClass().getFields(); + var data = new boundData(); + var sub = table.getSubTable(name); + + for (var field : classFields) { + for (var annotation : field.getAnnotations()) { + + if (annotation instanceof NtInput) { + var ntInput = (NtInput) annotation; + + // figure out we should call it + var entryName = ntInput.name(); + if (entryName == "") { + entryName = field.getName(); + } + + if (field.getType().equals(double.class)) { + data.applys.add(buildDoubleInput(this, sub, entryName, field, state)); + } + } + + if (annotation instanceof NtOutput) { + var ntOutput = (NtOutput) annotation; + + // figure out we should call it + var entryName = ntOutput.name(); + if (entryName == "") { + entryName = field.getName(); + } + + if (field.getType().equals(double.class)) { + data.applys.add(buildDoubleOutput(this, sub, entryName, field, state)); + } + } + } + } + + boundObjects.add(data); + return state; + } + + public void ReportError(String table, String name, Exception e) { + // todo: real comments and reporting and something + } + + private static NtBind instance; + + public static NtBind getInstance() { + if (instance == null) { + instance = new NtBind(); + } + + return instance; + } + + /** Creates a new NtBind. */ + public NtBind() { + } + + @Override + public void periodic() { + count++; + if (count > 10) { + count = 0; + + for (var data : boundObjects) { + data.apply(); + } + } + // This method will be called once per scheduler run + } +} diff --git a/src/main/java/lib2202/nt/NtInput.java b/src/main/java/lib2202/nt/NtInput.java new file mode 100644 index 00000000..460a06ef --- /dev/null +++ b/src/main/java/lib2202/nt/NtInput.java @@ -0,0 +1,17 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package lib2202.nt; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; + +/** Add your docs here. */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface NtInput { + String name() default ""; +} diff --git a/src/main/java/lib2202/nt/NtOutput.java b/src/main/java/lib2202/nt/NtOutput.java new file mode 100644 index 00000000..8e096a43 --- /dev/null +++ b/src/main/java/lib2202/nt/NtOutput.java @@ -0,0 +1,17 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +package lib2202.nt; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; + +/** Add your docs here. */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface NtOutput { + String name() default ""; +} diff --git a/src/main/java/lib2202/nt/examples/DoubleInput.java b/src/main/java/lib2202/nt/examples/DoubleInput.java new file mode 100644 index 00000000..291ad915 --- /dev/null +++ b/src/main/java/lib2202/nt/examples/DoubleInput.java @@ -0,0 +1,28 @@ +package lib2202.nt.examples; + + +import lib2202.nt.NtInput; +import lib2202.nt.NtOutput; +import lib2202.nt.NtBind; + +public class DoubleInput { + class State { + + @NtInput(name = "set") + public double SetPoint = 0; + + @NtOutput(name = "err") + public double Error = 0; + + // @NtInput() + } + + State left = NtBind.getInstance().bind(new State(), "left", this::onChanged); + State right = NtBind.getInstance().bind(new State(), "right", this::onChanged); + + void onChanged() { + System.out.println("SetPoint: " + left.SetPoint + ", err: " + left.Error); + System.out.println("SetPoint: " + right.SetPoint + ", err: " + right.Error); + } + +}