From 37eac06021ed46be84657097f50a9c4315402c5b Mon Sep 17 00:00:00 2001 From: Dean Brettle Date: Tue, 21 May 2024 22:18:54 -0700 Subject: [PATCH 1/8] Add support for CAN devices. --- asyncapi-template/template/$$schema$$.java | 20 +- asyncapi-template/template/SimDeviceSim.java | 2 +- build.gradle | 5 +- .../wpiws/connection/MessageProcessor.java | 41 +- .../interfaces/DeviceMessageProcessor.java | 3 +- wpilib-ws.yaml | 584 ++++++++++++++++++ 6 files changed, 643 insertions(+), 12 deletions(-) create mode 100644 wpilib-ws.yaml diff --git a/asyncapi-template/template/$$schema$$.java b/asyncapi-template/template/$$schema$$.java index 24c154d..e248cce 100644 --- a/asyncapi-template/template/$$schema$$.java +++ b/asyncapi-template/template/$$schema$$.java @@ -49,6 +49,7 @@ public class {{ name }}Sim { {% endif -%} {% if hasId -%} private static final Map STATE_MAP = new ConcurrentHashMap<>(); + private final String type; {% else -%} private static final {{ name }}Sim.State STATE = new State(); {% endif -%} @@ -58,9 +59,15 @@ public class {{ name }}Sim { /** * Creates a new {{ name }}Sim * @param id the device identifier of this {{ name }}Sim + * @param type the type of this {{ name }}Sim */ - public {{ name }}Sim(String id) { + public {{ name }}Sim(String id, String type) { super(id, STATE_MAP); + this.type = type; + } + + public {{ name }}Sim(String id) { + this(id, "{{ type }}"); } {%- endif -%} @@ -153,7 +160,7 @@ private void setInitialized(boolean initialized, boolean notifyRobot) { INITIALIZED_DEVICES.remove(id); } if(notifyRobot) { - ConnectionProcessor.broadcastMessage(id, "{{ type }}", new WSValue(" data) { + public static void processMessage(String device, String type, List data) { // Process all of the values, but save the " c * @param device the device identifier of the device sending the message * @param data the data associated with the message */ - public static void processMessage(String device, List data) { + public static void processMessage(String device, String type, List data) { SimDeviceSim simDevice = new SimDeviceSim(device); data.stream().filter(Objects::nonNull).forEach(value -> { String key = value.getKey(); diff --git a/build.gradle b/build.gradle index 5c90b0a..5b23b4f 100644 --- a/build.gradle +++ b/build.gradle @@ -41,18 +41,19 @@ task archiveTemplate(type: Tar) { // the asyncapi generator. task generateDeviceFiles(type: NpxTask) { def outputDir = "${buildDir}/generated/sources/asyncapi" + def specYaml = "${projectDir}/wpilib-ws.yaml" // Define the command line that npx should use workingDir = buildDir // Because templateDir can't be under it command = '@asyncapi/generator@1.17.25' args = ['--force-write', '-o', "${outputDir}/org/team199/wpiws/devices", - "https://raw.githubusercontent.com/wpilibsuite/allwpilib/master/simulation/halsim_ws_core/doc/wpilib-ws.yaml", + specYaml, file(archiveTemplate.archiveFile).toURI()] // Define the inputs and outputs of this task so that gradle only runs it // when necessary. - inputs.files(archiveTemplate.outputs) + inputs.files(archiveTemplate.outputs, file(specYaml)) outputs.dir(outputDir).withPropertyName("outputDir") } diff --git a/src/main/java/org/team199/wpiws/connection/MessageProcessor.java b/src/main/java/org/team199/wpiws/connection/MessageProcessor.java index dc3541f..1ede4bb 100644 --- a/src/main/java/org/team199/wpiws/connection/MessageProcessor.java +++ b/src/main/java/org/team199/wpiws/connection/MessageProcessor.java @@ -8,6 +8,8 @@ import org.team199.wpiws.devices.AccelerometerSim; import org.team199.wpiws.devices.AnalogInputSim; +import org.team199.wpiws.devices.CANEncoderSim; +import org.team199.wpiws.devices.CANMotorSim; import org.team199.wpiws.devices.DIOSim; import org.team199.wpiws.devices.DriverStationSim; import org.team199.wpiws.devices.DutyCycleSim; @@ -45,6 +47,14 @@ public final class MessageProcessor { registerProcessor("RoboRIO", RoboRIOSim::processMessage); registerProcessor("Solenoid", SolenoidSim::processMessage); registerProcessor("SimDevice", SimDeviceSim::processMessage); + registerProcessor("CANMotor", CANMotorSim::processMessage); + registerProcessor("CANEncoder", CANEncoderSim::processMessage); + registerProcessor("CANDutyCycle", DutyCycleSim::processMessage); + registerProcessor("CANAccel", AccelerometerSim::processMessage); + registerProcessor("CANAIn", AnalogInputSim::processMessage); + registerProcessor("CANDIO", DIOSim::processMessage); + registerProcessor("CANGyro", GyroSim::processMessage); + } /** @@ -70,14 +80,37 @@ public static void registerProcessor(String type, DeviceMessageProcessor process * @param data the values of that device which have been modified */ public static void process(String device, String type, List data) { - DeviceMessageProcessor processor = processors.get(type); + // Per the spec, some message with a type of SimDevice have a data + // format that is identical to hardware devices. In particular a + // SimDevice message with device=DutyCyle:Name has the same data format + // as a DutyCycle message, and a SimDevice message with + // device=CAN{Gyro,AI,Accel,DIO,DutyCycle}:Name has the same data format + // as a {Gyro,AI,Accel,DIO,DutyCycle} message. The + // CAN{Gyro,AI,Accel,DIO,DutyCycle} processors are registered as aliases + // for the the their non CAN counterparts. CANMotor and CANEncoder have + // their own processors because there is no Motor processor and the + // CANEncoder data format is different from the Encoder data format. + String dataType = type; + if (type.equals("SimDevice")) { + String[] deviceParts = device.split(":"); + if (deviceParts.length == 2) { + dataType = deviceParts[0]; + } + } + + // Use the data type to find the appropriate processor. + DeviceMessageProcessor processor = processors.get(dataType); if(processor == null) { - if(unknownTypes.add(type)) { - System.err.println("No processor found for device of type: \"" + type + "\" messages for devices of this type will be ignored"); + if(unknownTypes.add(dataType)) { + System.err.println("No processor found for device with data type: \"" + dataType + "\". Messages with this data type will be ignored."); } return; } - processor.processMessage(device, data); + + // Pass the actual device type to the processor which will pass it on to + // the *Sim constructor so that the *Sim object creates messages with + // the correct type. + processor.processMessage(device, type, data); } private MessageProcessor() {} diff --git a/src/main/java/org/team199/wpiws/interfaces/DeviceMessageProcessor.java b/src/main/java/org/team199/wpiws/interfaces/DeviceMessageProcessor.java index 3ba1287..3cc3258 100644 --- a/src/main/java/org/team199/wpiws/interfaces/DeviceMessageProcessor.java +++ b/src/main/java/org/team199/wpiws/interfaces/DeviceMessageProcessor.java @@ -14,8 +14,9 @@ public interface DeviceMessageProcessor { /** * Processes a WPI HALSim message * @param device the device identifier of the device sending the message + * @param type the type of the device sending the message * @param data the data associated with the message */ - public void processMessage(String device, List data); + public void processMessage(String device, String type, List data); } diff --git a/wpilib-ws.yaml b/wpilib-ws.yaml new file mode 100644 index 0000000..348c5bd --- /dev/null +++ b/wpilib-ws.yaml @@ -0,0 +1,584 @@ +asyncapi: 2.0.0 +info: + title: WPILib WebSocket Remote Endpoint API (with FRC 199 additions) + version: "1.0.0" + description: | + API to route WPILib HAL calls over WebSockets. + license: + name: WPILib BSD + +channels: + wpilibws: + description: General channel for WPILib WebSocket messages + publish: + operationId: wpilibwsPublish + message: + $ref: "#/components/messages/wpilibwsMsg" + subscribe: + operationId: wpilibwsSubscribe + message: + $ref: "#/components/messages/wpilibwsMsg" + +components: + messages: + wpilibwsMsg: + title: WPILib WebSocket Message + summary: Message envelope. Note that the "data" field contains a diff of the current state of a particular device. E.g. If only the "value" changes for a DIO device, then only the "<>value" field will be sent. + contentType: application/json + examples: + - payload: + type: PWM + device: "1" + data: + "x": + type: number + description: "Acceleration in G’s " + ">y": + type: number + description: "Acceleration in G’s " + ">z": + type: number + description: "Acceleration in G’s " + + aiData: + type: object + required: + - type + - device + properties: + type: + type: string + description: Device Type (e.g. DIO/AI/PWM/Encoder etc) + const: AI + device: + type: string + description: Device Identifier (usually channel) + data: + type: object + description: "Analog Input Data (type: AI, device: channel number)" + properties: + voltage": + type: number + description: "Input voltage, in volts" + + dioData: + type: object + required: + - type + - device + properties: + type: + type: string + description: Device Type (e.g. DIO/AI/PWM/Encoder etc) + const: DIO + device: + type: string + description: Device Identifier (usually channel) + data: + type: object + description: "Digital Input/Output Data (type: DIO, device: channel number)" + properties: + value: + type: boolean + description: "Input or output state" + + dpwmData: + type: object + required: + - type + - device + properties: + type: + type: string + description: Device Type (e.g. DIO/AI/PWM/Encoder etc) + const: dPWM + device: + type: string + description: Device Identifier (usually channel) + data: + type: object + description: "Duty Cycle Output Data (type: dPWM, device: channel number)" + properties: + new_data": + type: boolean + description: "One shot. If set to true in a message, notifies the robot program that new DS and Joystick data is available." + ">enabled": + type: boolean + description: "True to enable the robot program" + ">autonomous": + type: boolean + description: "True for autonomous mode; false for teleoperated mode" + ">test": + type: boolean + description: "True for test mode; false for other modes" + ">estop": + type: boolean + description: "True to emergency stop (no motor outputs)" + ">fms": + type: boolean + description: "True if the DS is connected to a Field Management System (FMS)" + ">ds": + type: boolean + description: "True if a DS application is connected" + ">station": + type: string + description: "Station color and number; supported values are 'red1', 'red2', 'red3', 'blue1', 'blue2', 'blue3'." + ">match_time": + type: number + description: "Match time countdown, in seconds, for each match period (e.g. for 15 second period, starts at 15 and counts down to 0). If not in a match, -1." + ">game_data": + type: string + description: "Game-specific data; arbitrary string contents" + + dutycycleData: + type: object + required: + - type + - device + properties: + type: + type: string + description: Device Type (e.g. DIO/AI/PWM/Encoder etc) + const: DutyCycle + device: + type: string + description: Arbitrary device name + data: + type: object + description: "Duty Cycle Input Data (type: DutyCycle, device: channel number)" + properties: + ">connected": + type: boolean + description: "True if the encoder is connected" + ">position": + type: number + description: "The position in rotations" + + encoderData: + type: object + required: + - type + - device + properties: + type: + type: string + description: Device Type (e.g. DIO/AI/PWM/Encoder etc) + const: Encoder + device: + type: string + description: Device Identifier (usually channel) + data: + type: object + description: "Quadrature Encoder Data (type: Encoder, device: channel number)" + properties: + count": + type: integer + description: "Accumulated count (pulses)" + ">period": + type: number + description: "Period between pulses in seconds" + + gyroData: + type: object + required: + - type + - device + properties: + type: + type: string + description: Device Type (e.g. DIO/AI/PWM/Encoder etc) + const: Gyro + device: + type: string + description: Arbitrary device name + data: + type: object + description: "Gyro Data (type: Gyro, device: channel number)" + properties: + connected": + type: boolean + description: "True if the gyro is connected" + ">angle_x": + type: number + description: "The gyro angle in degrees" + ">angle_y": + type: number + description: "The gyro angle in degrees" + ">angle_z": + type: number + description: "The gyro angle in degrees" + ">rate_x": + type: number + description: "The current gyro angular rate of change in degrees/second" + ">rate_y": + type: number + description: "The current gyro angular rate of change in degrees/second" + ">rate_z": + type: number + description: "The current gyro angular rate of change in degrees/second" + + joystickData: + type: object + required: + - type + - device + properties: + type: + type: string + description: Device Type (e.g. DIO/AI/PWM/Encoder etc) + const: Joystick + device: + type: string + description: Device Identifier (usually channel) + data: + type: object + description: "Joystick Data (type: Joystick, device: channel number)" + properties: + ">axes": + type: array + items: + type: number + description: "Value of an individual axis on this joystick" + minimum: -1.0 + maximum: 1.0 + ">povs": + type: array + description: "One array element per POV; value is a" + items: + type: integer + description: "State of all POV switches on this joystick; an angle in degrees of the POV (e.g. 0, 90, 315) if pressed, or -1 if the POV is not pressed" + ">buttons": + type: array + description: State of all buttons on this joystick + items: + type: boolean + description: Pressed state of an individual button + fpga_button": + type: boolean + description: "FPGA button state" + ">vin_voltage": + type: number + description: "Vin rail voltage" + ">vin_current": + type: number + description: "Vin rail current" + ">6v_voltage": + type: number + description: "6V rail voltage" + ">6v_current": + type: number + description: "6V rail current" + ">6v_active": + type: boolean + description: "True if 6V rail active, false if inactive" + ">6v_faults": + type: integer + description: "Number of faults on 6V rail" + ">5v_voltage": + type: number + description: "5V rail voltage" + ">5v_current": + type: number + description: "5V rail current" + ">5v_active": + type: boolean + description: "True if 5V rail active, false if inactive" + ">5v_faults": + type: integer + description: "Number of faults on 5V rail" + ">3v3_voltage": + type: number + description: "3.3V rail voltage" + ">3v3_current": + type: number + description: "3.3V rail current" + ">3v3_active": + type: boolean + description: "True if 3.3V rail active, false if inactive" + ">3v3_faults": + type: integer + description: "Number of faults on 3.3V rail" + + canmotorData: + type: object + required: + - type + - device + properties: + type: + type: string + description: "Dummy type used for SimDevice type with device=CANMotor:*" + const: CANMotor + device: + type: string + description: "Should have the form CANMotor:ControllerClassName[id]" + data: + type: object + description: "CANMotor Data" + properties: + supplyCurrent": + type: number + description: "Supply current in Amps" + minimum: 0.0 + ">motorCurrent": + type: number + description: "Motor current in Amps" + minimum: 0.0 + ">busVoltage": + type: number + description: "Bus voltage in Volts" + minimum: 0.0 + + canencoderData: + type: object + required: + - type + - device + properties: + type: + type: string + description: "Dummy type used for SimDevice type with device=CANEncoder:*" + const: CANEncoder + device: + type: string + description: "Should have the form CANEncoder:ControllerClassName[id]" + data: + type: object + description: "CANEncoder Data" + properties: + position": + type: number + description: "Position in rotations" + ">velocity": + type: number + description: "Velocity in rotations per second" + From fc2613d57f88a89b9faf806a42befe8122cfbf10 Mon Sep 17 00:00:00 2001 From: Dean Brettle Date: Wed, 22 May 2024 10:20:24 -0700 Subject: [PATCH 2/8] Document use of an extended version of the spec. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f2cbf89..e18cf03 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # WPIWebSockets This API provides a Java interface to the [WPILib HALSim WebSockets API](https://github.com/wpilibsuite/allwpilib/blob/master/simulation/halsim_ws_core/doc/hardware_ws_api.md) which mirrors the standard simulation API. # Building the API -To build the API, navigate to the root directory of this repository and run `./gradlew build`. This will use Gradle to build the API with Java 8. Note that the simulated device files (org.team199.wpiws.devices.*) are auto-generated using the [AsyncAPI Generator](https://github.com/asyncapi/generator) and [WPILib's machine readable WebSocket specification](https://github.com/wpilibsuite/allwpilib/blob/master/simulation/halsim_ws_core/doc/wpilib-ws.yaml). The AsyncAPI template used to generate these files is located at [./asyncapi-template](https://github.com/DeepBlueRobotics/WPIWebSockets/tree/master/asyncapi-template). These files are auto-generated during the `WPIWebSockets:build` task, however, they can be manually regenerated by running `./gradlew generateDeviceFiles`. +To build the API, navigate to the root directory of this repository and run `./gradlew build`. This will use Gradle to build the API with Java 8. Note that the simulated device files (org.team199.wpiws.devices.*) are auto-generated using the [AsyncAPI Generator](https://github.com/asyncapi/generator) and [an extended version](https://github.com/DeepBlueRobotics/WPIWebSockets/tree/master/wpilib-ws.yaml) of [WPILib's machine readable WebSocket specification](https://github.com/wpilibsuite/allwpilib/blob/master/simulation/halsim_ws_core/doc/wpilib-ws.yaml). The AsyncAPI template used to generate these files is located at [./asyncapi-template](https://github.com/DeepBlueRobotics/WPIWebSockets/tree/master/asyncapi-template). These files are auto-generated during the `WPIWebSockets:build` task, however, they can be manually regenerated by running `./gradlew generateDeviceFiles`. # Using the API Connecting to the running HALSim client and server instances can be accomplished by using the `connectHALSim` and `startHALSimServer` methods of [`org.team199.wpiws.connection.WSConnection`](https://github.com/DeepBlueRobotics/WPIWebSockets/blob/master/src/main/java/org/team199/wpiws/connection/WSConnection.java). Code for connecting to simulated devices can be found in the `org.team199.wpiws.devices` package. These classes can be initialized by providing the constructor with the device id with which it should associate itself. The instance will then provide getter, setter, and callback methods for all of the values exposed by the WebSocket API. (ex. `new PWMSim("5").setSpeed(5)`, `new DIOSim("0").getValue()`). Note: `static` methods apply globaly to the device type. From 640c453bc07dd0f4e10569b34a552a173ee3a2de Mon Sep 17 00:00:00 2001 From: Dean Brettle Date: Wed, 22 May 2024 10:29:26 -0700 Subject: [PATCH 3/8] Use relative links in README.md. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e18cf03..7d97e12 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # WPIWebSockets This API provides a Java interface to the [WPILib HALSim WebSockets API](https://github.com/wpilibsuite/allwpilib/blob/master/simulation/halsim_ws_core/doc/hardware_ws_api.md) which mirrors the standard simulation API. # Building the API -To build the API, navigate to the root directory of this repository and run `./gradlew build`. This will use Gradle to build the API with Java 8. Note that the simulated device files (org.team199.wpiws.devices.*) are auto-generated using the [AsyncAPI Generator](https://github.com/asyncapi/generator) and [an extended version](https://github.com/DeepBlueRobotics/WPIWebSockets/tree/master/wpilib-ws.yaml) of [WPILib's machine readable WebSocket specification](https://github.com/wpilibsuite/allwpilib/blob/master/simulation/halsim_ws_core/doc/wpilib-ws.yaml). The AsyncAPI template used to generate these files is located at [./asyncapi-template](https://github.com/DeepBlueRobotics/WPIWebSockets/tree/master/asyncapi-template). These files are auto-generated during the `WPIWebSockets:build` task, however, they can be manually regenerated by running `./gradlew generateDeviceFiles`. +To build the API, navigate to the root directory of this repository and run `./gradlew build`. This will use Gradle to build the API with Java 8. Note that the simulated device files (org.team199.wpiws.devices.*) are auto-generated using the [AsyncAPI Generator](https://github.com/asyncapi/generator) and [an extended version](./wpilib-ws.yaml) of [WPILib's machine readable WebSocket specification](https://github.com/wpilibsuite/allwpilib/blob/master/simulation/halsim_ws_core/doc/wpilib-ws.yaml). The AsyncAPI template used to generate these files is located at [./asyncapi-template](./asyncapi-template). These files are auto-generated during the `WPIWebSockets:build` task, however, they can be manually regenerated by running `./gradlew generateDeviceFiles`. # Using the API -Connecting to the running HALSim client and server instances can be accomplished by using the `connectHALSim` and `startHALSimServer` methods of [`org.team199.wpiws.connection.WSConnection`](https://github.com/DeepBlueRobotics/WPIWebSockets/blob/master/src/main/java/org/team199/wpiws/connection/WSConnection.java). Code for connecting to simulated devices can be found in the `org.team199.wpiws.devices` package. These classes can be initialized by providing the constructor with the device id with which it should associate itself. The instance will then provide getter, setter, and callback methods for all of the values exposed by the WebSocket API. (ex. `new PWMSim("5").setSpeed(5)`, `new DIOSim("0").getValue()`). Note: `static` methods apply globaly to the device type. +Connecting to the running HALSim client and server instances can be accomplished by using the `connectHALSim` and `startHALSimServer` methods of [`org.team199.wpiws.connection.WSConnection`](./src/main/java/org/team199/wpiws/connection/WSConnection.java). Code for connecting to simulated devices can be found in the `org.team199.wpiws.devices` package. These classes can be initialized by providing the constructor with the device id with which it should associate itself. The instance will then provide getter, setter, and callback methods for all of the values exposed by the WebSocket API. (ex. `new PWMSim("5").setSpeed(5)`, `new DIOSim("0").getValue()`). Note: `static` methods apply globaly to the device type. For a more complete example, see [DeepBlueRobotics/DeepBlueSim](https://github.com/DeepBlueRobotics/DeepBlueSim) which uses this API to provide an interface to a [Webots](https://cyberbotics.com/) robot simulator. From cf13d3635d879874218deb9df48ff198fd057540 Mon Sep 17 00:00:00 2001 From: Dean Brettle Date: Wed, 22 May 2024 13:00:41 -0700 Subject: [PATCH 4/8] Add neutralDeadband and brakeMode to CANMotor data format. --- wpilib-ws.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/wpilib-ws.yaml b/wpilib-ws.yaml index 348c5bd..317e11c 100644 --- a/wpilib-ws.yaml +++ b/wpilib-ws.yaml @@ -537,7 +537,7 @@ components: Date: Thu, 23 May 2024 08:23:01 -0700 Subject: [PATCH 5/8] Support can device names containing ":" Co-authored-by: CoolSpy3 --- .../java/org/team199/wpiws/connection/MessageProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/team199/wpiws/connection/MessageProcessor.java b/src/main/java/org/team199/wpiws/connection/MessageProcessor.java index 1ede4bb..bdea380 100644 --- a/src/main/java/org/team199/wpiws/connection/MessageProcessor.java +++ b/src/main/java/org/team199/wpiws/connection/MessageProcessor.java @@ -93,7 +93,7 @@ public static void process(String device, String type, List data) { String dataType = type; if (type.equals("SimDevice")) { String[] deviceParts = device.split(":"); - if (deviceParts.length == 2) { + if (deviceParts.length > 1) { dataType = deviceParts[0]; } } From 30d808dc9191f54b298198fcfb131d5c06a716cd Mon Sep 17 00:00:00 2001 From: Dean Brettle Date: Thu, 23 May 2024 08:31:30 -0700 Subject: [PATCH 6/8] Fallback on SimDevice if data type is invalid. Co-authored-by: CoolSpy3 --- .../java/org/team199/wpiws/connection/MessageProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/team199/wpiws/connection/MessageProcessor.java b/src/main/java/org/team199/wpiws/connection/MessageProcessor.java index bdea380..2b6f211 100644 --- a/src/main/java/org/team199/wpiws/connection/MessageProcessor.java +++ b/src/main/java/org/team199/wpiws/connection/MessageProcessor.java @@ -98,8 +98,8 @@ public static void process(String device, String type, List data) { } } - // Use the data type to find the appropriate processor. - DeviceMessageProcessor processor = processors.get(dataType); + // Use the data type to find the appropriate processor. Fallback on SimDevice if the parsing above returned an invalid type. + DeviceMessageProcessor processor = processors.get(dataType, type.equals("SimDevice") ? processors.get("SimDevice") : null); if(processor == null) { if(unknownTypes.add(dataType)) { System.err.println("No processor found for device with data type: \"" + dataType + "\". Messages with this data type will be ignored."); From 9d015a4959d6f61e07027209d1a8226db648d6e6 Mon Sep 17 00:00:00 2001 From: Dean Brettle Date: Thu, 23 May 2024 10:23:52 -0700 Subject: [PATCH 7/8] Fix build failure. Use getOrDefault() instead of get(). --- .../java/org/team199/wpiws/connection/MessageProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/team199/wpiws/connection/MessageProcessor.java b/src/main/java/org/team199/wpiws/connection/MessageProcessor.java index 2b6f211..0fc3857 100644 --- a/src/main/java/org/team199/wpiws/connection/MessageProcessor.java +++ b/src/main/java/org/team199/wpiws/connection/MessageProcessor.java @@ -99,7 +99,7 @@ public static void process(String device, String type, List data) { } // Use the data type to find the appropriate processor. Fallback on SimDevice if the parsing above returned an invalid type. - DeviceMessageProcessor processor = processors.get(dataType, type.equals("SimDevice") ? processors.get("SimDevice") : null); + DeviceMessageProcessor processor = processors.getOrDefault(dataType, type.equals("SimDevice") ? processors.get("SimDevice") : null); if(processor == null) { if(unknownTypes.add(dataType)) { System.err.println("No processor found for device with data type: \"" + dataType + "\". Messages with this data type will be ignored."); From 227d12951024fff902a8ea16d634a6f2aba67aa7 Mon Sep 17 00:00:00 2001 From: Dean Brettle Date: Thu, 23 May 2024 11:52:20 -0700 Subject: [PATCH 8/8] Update wpilib-ws.yaml to accurately represent the schema and update the template files to deal with that. --- asyncapi-template/hooks/UpdateSchemaNames.js | 3 + .../{template => partials}/SimDeviceSim.java | 0 asyncapi-template/template/$$schema$$.java | 4 + wpilib-ws.yaml | 93 ++++++++++++++++++- 4 files changed, 98 insertions(+), 2 deletions(-) rename asyncapi-template/{template => partials}/SimDeviceSim.java (100%) diff --git a/asyncapi-template/hooks/UpdateSchemaNames.js b/asyncapi-template/hooks/UpdateSchemaNames.js index 93674fd..eca8643 100644 --- a/asyncapi-template/hooks/UpdateSchemaNames.js +++ b/asyncapi-template/hooks/UpdateSchemaNames.js @@ -3,6 +3,9 @@ const deviceFilter = require('../filters/DeviceFilter'); module.exports = { 'setFileTemplateName': (generator, originalFilename) => { + if(originalFilename["originalFilename"] === "simdeviceData") { + return "SimDeviceSim"; + } for(const [name, schema] of generator.asyncapi.allSchemas()) { if(name === originalFilename["originalFilename"] && schema.property("type") !== null) { return deviceFilter.formatName(schema.property("type").const()) + "Sim"; diff --git a/asyncapi-template/template/SimDeviceSim.java b/asyncapi-template/partials/SimDeviceSim.java similarity index 100% rename from asyncapi-template/template/SimDeviceSim.java rename to asyncapi-template/partials/SimDeviceSim.java diff --git a/asyncapi-template/template/$$schema$$.java b/asyncapi-template/template/$$schema$$.java index e248cce..5bf31b2 100644 --- a/asyncapi-template/template/$$schema$$.java +++ b/asyncapi-template/template/$$schema$$.java @@ -1,3 +1,6 @@ +{%- if schemaName === "simdeviceData" -%} +{% include "../partials/SimDeviceSim.java" -%} +{%- else -%} {% set type = schema.property("type").const() -%} {% set name = type | formatName -%} {% set hasId = type | hasId -%} @@ -350,3 +353,4 @@ public static class State { } } +{% endif -%} diff --git a/wpilib-ws.yaml b/wpilib-ws.yaml index 317e11c..6b279ef 100644 --- a/wpilib-ws.yaml +++ b/wpilib-ws.yaml @@ -52,8 +52,7 @@ components: - $ref: "#/components/schemas/relayData" - $ref: "#/components/schemas/solenoidData" - $ref: "#/components/schemas/roborioData" - - $ref: "#/components/schemas/canmotorData" - - $ref: "#/components/schemas/canencoderData" + - $ref: "#/components/schemas/simdeviceData" schemas: accelData: @@ -590,3 +589,93 @@ components: type: number description: "Velocity in rotations per second" + simdeviceData: + type: object + required: + - type + - device + oneOf: + - properties: + type: + type: string + const: SimDevice + device: + type: string + format: "[^:]+" + data: + type: object + additionalProperties: + type: string + - properties: + type: + type: string + const: SimDevice + device: + type: string + format: "DutyCycle:.+" + data: + $ref: "#/components/schemas/dutycycleData/properties/data" + - properties: + type: + type: string + const: SimDevice + device: + type: string + format: "CANMotor:.+" + data: + $ref: "#/components/schemas/canmotorData/properties/data" + - properties: + type: + type: string + const: SimDevice + device: + type: string + format: "CANEncoder:.+" + data: + $ref: "#/components/schemas/canencoderData/properties/data" + - properties: + type: + type: string + const: SimDevice + device: + type: string + format: "CANGyro:.+" + data: + $ref: "#/components/schemas/gyroData/properties/data" + - properties: + type: + type: string + const: SimDevice + device: + type: string + format: "CANAccel:.+" + data: + $ref: "#/components/schemas/accelData/properties/data" + - properties: + type: + type: string + const: SimDevice + device: + type: string + format: "CANAIn:.+" + data: + $ref: "#/components/schemas/aiData/properties/data" + - properties: + type: + type: string + const: SimDevice + device: + type: string + format: "CANDIO:.+" + data: + ref: "#/components/schemas/dioData/properties/data" + - properties: + type: + type: string + const: SimDevice + device: + type: string + format: "CANDutyCycle:.+" + data: + $ref: "#/components/schemas/dutycycleData/properties/data" +