From 8533eb7d10c59cdcd6023e2c1a9edb19f16af756 Mon Sep 17 00:00:00 2001 From: Brad Miller Date: Wed, 1 Oct 2014 06:32:22 -0400 Subject: Initial commit of files from FIRSTForge project --- FakeRobot/README.txt | 1 + FakeRobot/build.xml | 74 ++ ...beans-modules-java-j2seproject-copylibstask.jar | Bin 0 -> 22335 bytes FakeRobot/lib/NetworkTables.jar | Bin 0 -> 43275 bytes FakeRobot/lib/nblibraries.properties | 3 + FakeRobot/lib/networktables-desktop-r156.jar | Bin 0 -> 80957 bytes FakeRobot/lib/networktables-desktop-r157.jar | Bin 0 -> 80997 bytes FakeRobot/lib/networktables-desktop-r158.jar | Bin 0 -> 80997 bytes FakeRobot/manifest.mf | 3 + FakeRobot/nbproject/build-impl.xml | 1078 +++++++++++++++ FakeRobot/nbproject/genfiles.properties | 8 + FakeRobot/nbproject/project.properties | 75 ++ FakeRobot/nbproject/project.xml | 18 + .../livewindowfakerobot/LiveWindowFakeRobot.java | 82 ++ WPIJavaCV/build.xml | 74 ++ WPIJavaCV/hs_err_pid4572.log | 264 ++++ WPIJavaCV/hs_err_pid5372.log | 261 ++++ WPIJavaCV/hs_err_pid5412.log | 261 ++++ WPIJavaCV/hs_err_pid5892.log | 30 + WPIJavaCV/lib/javacpp.jar | Bin 0 -> 67389 bytes WPIJavaCV/lib/javacv-windows-x86.jar | Bin 0 -> 1219843 bytes WPIJavaCV/lib/javacv.jar | Bin 0 -> 643854 bytes WPIJavaCV/manifest.mf | 3 + WPIJavaCV/nbproject/build-impl.xml | 1400 ++++++++++++++++++++ WPIJavaCV/nbproject/genfiles.properties | 8 + WPIJavaCV/nbproject/private/config.properties | 0 WPIJavaCV/nbproject/private/private.properties | 6 + WPIJavaCV/nbproject/project.properties | 87 ++ WPIJavaCV/nbproject/project.xml | 16 + .../edu/wpi/first/wpijavacv/WPIBinaryImage.java | 277 ++++ .../src/edu/wpi/first/wpijavacv/WPICamera.java | 26 + .../src/edu/wpi/first/wpijavacv/WPIColor.java | 64 + .../src/edu/wpi/first/wpijavacv/WPIColorImage.java | 158 +++ .../src/edu/wpi/first/wpijavacv/WPIContour.java | 100 ++ .../src/edu/wpi/first/wpijavacv/WPIDisposable.java | 119 ++ .../edu/wpi/first/wpijavacv/WPIFFmpegVideo.java | 151 +++ .../edu/wpi/first/wpijavacv/WPIGrayscaleImage.java | 53 + .../src/edu/wpi/first/wpijavacv/WPIImage.java | 76 ++ .../edu/wpi/first/wpijavacv/WPIJavaCVUtils.java | 41 + .../edu/wpi/first/wpijavacv/WPILaptopCamera.java | 28 + .../src/edu/wpi/first/wpijavacv/WPIMemoryPool.java | 34 + .../src/edu/wpi/first/wpijavacv/WPIPoint.java | 56 + .../src/edu/wpi/first/wpijavacv/WPIPolygon.java | 126 ++ .../src/edu/wpi/first/wpijavacv/WPIWindow.java | 52 + extensions/camera/SquareTracker/Square.pdf | Bin 0 -> 79845 bytes extensions/camera/SquareTracker/build.xml | 74 ++ extensions/camera/SquareTracker/manifest.mf | 3 + .../camera/SquareTracker/nbproject/build-impl.xml | 950 +++++++++++++ .../SquareTracker/nbproject/genfiles.properties | 8 + .../nbproject/private/config.properties | 0 .../nbproject/private/private.properties | 6 + .../SquareTracker/nbproject/private/private.xml | 4 + .../SquareTracker/nbproject/project.properties | 91 ++ .../camera/SquareTracker/nbproject/project.xml | 49 + .../wpilibj/examples/SquareTrackerExtension.java | 118 ++ extensions/camera/WPICameraExtension/build.xml | 74 ++ extensions/camera/WPICameraExtension/manifest.mf | 3 + .../WPICameraExtension/nbproject/build-impl.xml | 1400 ++++++++++++++++++++ .../nbproject/genfiles.properties | 8 + .../nbproject/private/config.properties | 0 .../nbproject/private/private.properties | 6 + .../nbproject/private/private.xml | 4 + .../nbproject/project.properties | 96 ++ .../WPICameraExtension/nbproject/project.xml | 16 + .../camera/VideoStreamExtension.java | 166 +++ .../camera/VideoStreamViewerExtension.java | 129 ++ .../smartdashboard/camera/WPICameraExtension.java | 169 +++ .../camera/WPIFFmpegVideoViewer.java | 133 ++ .../camera/WPILaptopCameraExtension.java | 148 +++ smartdashboard/.classpath | 10 + smartdashboard/.project | 17 + smartdashboard/build.xml | 91 ++ smartdashboard/customSave | 36 + smartdashboard/lib/jcommon-1.0.16.jar | Bin 0 -> 309293 bytes smartdashboard/lib/jfreechart-1.0.13.jar | Bin 0 -> 1425744 bytes smartdashboard/lib/junit-4.8.2.jar | Bin 0 -> 237344 bytes smartdashboard/lib/nblibraries.properties | 12 + smartdashboard/manifest.mf | 3 + smartdashboard/nbbuild.xml | 85 ++ smartdashboard/nbproject/build-impl.xml | 1400 ++++++++++++++++++++ smartdashboard/nbproject/genfiles.properties | 8 + smartdashboard/nbproject/project.properties | 94 ++ smartdashboard/nbproject/project.xml | 16 + smartdashboard/smartdashboard.install4j | 460 +++++++ .../edu/wpi/first/smartdashboard/ArgParser.java | 53 + .../src/edu/wpi/first/smartdashboard/LogToCSV.java | 81 ++ .../smartdashboard/extensions/FileSniffer.java | 138 ++ .../first/smartdashboard/gui/DashboardFrame.java | 380 ++++++ .../first/smartdashboard/gui/DashboardMenu.java | 238 ++++ .../first/smartdashboard/gui/DashboardPanel.java | 596 +++++++++ .../first/smartdashboard/gui/DashboardPrefs.java | 117 ++ .../first/smartdashboard/gui/DisplayElement.java | 166 +++ .../wpi/first/smartdashboard/gui/GlassPane.java | 552 ++++++++ .../wpi/first/smartdashboard/gui/MainPanel.java | 50 + .../first/smartdashboard/gui/PropertyEditor.java | 118 ++ .../wpi/first/smartdashboard/gui/StaticWidget.java | 10 + .../edu/wpi/first/smartdashboard/gui/Widget.java | 591 +++++++++ .../smartdashboard/gui/elements/BooleanBox.java | 47 + .../first/smartdashboard/gui/elements/Button.java | 50 + .../smartdashboard/gui/elements/CheckBox.java | 37 + .../first/smartdashboard/gui/elements/Chooser.java | 308 +++++ .../first/smartdashboard/gui/elements/Command.java | 91 ++ .../smartdashboard/gui/elements/CommandButton.java | 46 + .../first/smartdashboard/gui/elements/Compass.java | 82 ++ .../gui/elements/ConnectionIndicator.java | 113 ++ .../elements/DefaultDisplayElementRegistrar.java | 35 + .../gui/elements/FormattedField.java | 63 + .../first/smartdashboard/gui/elements/Image.java | 73 + .../first/smartdashboard/gui/elements/Label.java | 56 + .../smartdashboard/gui/elements/LinePlot.java | 76 ++ .../smartdashboard/gui/elements/PIDEditor.java | 114 ++ .../smartdashboard/gui/elements/ProgressBar.java | 59 + .../gui/elements/RobotPreferences.java | 302 +++++ .../smartdashboard/gui/elements/Scheduler.java | 157 +++ .../smartdashboard/gui/elements/SimpleDial.java | 67 + .../smartdashboard/gui/elements/Subsystem.java | 74 ++ .../first/smartdashboard/gui/elements/TextBox.java | 59 + .../gui/elements/VideoStreamViewerExtension.java | 175 +++ .../bindings/AbstractNumberDatasetWidget.java | 17 + .../gui/elements/bindings/AbstractTableWidget.java | 160 +++ .../gui/elements/bindings/AbstractValueWidget.java | 81 ++ .../gui/elements/bindings/BooleanBindable.java | 10 + .../gui/elements/bindings/NumberBindable.java | 11 + .../gui/elements/bindings/StringBindable.java | 10 + .../livewindow/elements/Controller.java | 20 + .../livewindow/elements/DigitalInputDisplay.java | 41 + .../elements/DigitalOutputController.java | 67 + .../livewindow/elements/EncoderDisplay.java | 100 ++ .../livewindow/elements/GyroDisplay.java | 90 ++ .../livewindow/elements/LWSubsystem.java | 251 ++++ .../elements/LiveWindowWidgetRegistrar.java | 26 + .../livewindow/elements/NameTag.java | 26 + .../livewindow/elements/RelayController.java | 60 + .../livewindow/elements/ServoController.java | 100 ++ .../livewindow/elements/SingleNumberDisplay.java | 53 + .../livewindow/elements/SpeedController.java | 94 ++ .../src/edu/wpi/first/smartdashboard/main.java | 149 +++ .../first/smartdashboard/net/TCPImageFetcher.java | 137 ++ .../smartdashboard/properties/BooleanProperty.java | 61 + .../smartdashboard/properties/ColorProperty.java | 152 +++ .../smartdashboard/properties/DoubleProperty.java | 32 + .../smartdashboard/properties/FileProperty.java | 84 ++ .../smartdashboard/properties/GenericProperty.java | 37 + .../properties/IPAddressProperty.java | 37 + .../properties/IntegerListProperty.java | 78 ++ .../smartdashboard/properties/IntegerProperty.java | 32 + .../smartdashboard/properties/MultiProperty.java | 73 + .../smartdashboard/properties/NumberProperty.java | 33 + .../first/smartdashboard/properties/Property.java | 110 ++ .../smartdashboard/properties/PropertyHolder.java | 56 + .../smartdashboard/properties/StringProperty.java | 21 + .../properties/TextInputProperty.java | 23 + .../edu/wpi/first/smartdashboard/robot/Robot.java | 73 + .../wpi/first/smartdashboard/types/DataType.java | 112 ++ .../types/DisplayElementRegistry.java | 210 +++ .../first/smartdashboard/types/NamedDataType.java | 49 + .../types/named/AccelerometerType.java | 30 + .../types/named/AnalogInputType.java | 30 + .../smartdashboard/types/named/ButtonType.java | 25 + .../smartdashboard/types/named/CommandType.java | 25 + .../smartdashboard/types/named/CompassType.java | 30 + .../smartdashboard/types/named/CompressorType.java | 30 + .../smartdashboard/types/named/CounterType.java | 30 + .../types/named/DigitalInputType.java | 30 + .../types/named/DigitalOutputType.java | 30 + .../types/named/DoubleSolenoidType.java | 31 + .../smartdashboard/types/named/EncoderType.java | 30 + .../types/named/GearToothSensorType.java | 30 + .../first/smartdashboard/types/named/GyroType.java | 25 + .../smartdashboard/types/named/LWGyroType.java | 30 + .../types/named/LWSubsystemType.java | 30 + .../smartdashboard/types/named/PIDCommandType.java | 24 + .../types/named/PIDSubsystemType.java | 24 + .../first/smartdashboard/types/named/PIDType.java | 25 + .../types/named/QuadratureEncoderType.java | 31 + .../smartdashboard/types/named/RelayType.java | 30 + .../smartdashboard/types/named/SchedulerType.java | 24 + .../smartdashboard/types/named/ServoType.java | 30 + .../smartdashboard/types/named/SolenoidType.java | 30 + .../types/named/SpeedControllerType.java | 30 + .../types/named/StringChooserType.java | 24 + .../smartdashboard/types/named/SubsystemType.java | 25 + .../smartdashboard/types/named/UltrasonicType.java | 30 + .../xml/SmartDashboardXMLReader.java | 248 ++++ .../xml/SmartDashboardXMLWriter.java | 99 ++ .../wpi/first/smartdashboard/xml/XMLWidget.java | 136 ++ 186 files changed, 20196 insertions(+) create mode 100644 FakeRobot/README.txt create mode 100644 FakeRobot/build.xml create mode 100644 FakeRobot/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar create mode 100644 FakeRobot/lib/NetworkTables.jar create mode 100644 FakeRobot/lib/nblibraries.properties create mode 100644 FakeRobot/lib/networktables-desktop-r156.jar create mode 100644 FakeRobot/lib/networktables-desktop-r157.jar create mode 100644 FakeRobot/lib/networktables-desktop-r158.jar create mode 100644 FakeRobot/manifest.mf create mode 100644 FakeRobot/nbproject/build-impl.xml create mode 100644 FakeRobot/nbproject/genfiles.properties create mode 100644 FakeRobot/nbproject/project.properties create mode 100644 FakeRobot/nbproject/project.xml create mode 100644 FakeRobot/src/livewindowfakerobot/LiveWindowFakeRobot.java create mode 100644 WPIJavaCV/build.xml create mode 100644 WPIJavaCV/hs_err_pid4572.log create mode 100644 WPIJavaCV/hs_err_pid5372.log create mode 100644 WPIJavaCV/hs_err_pid5412.log create mode 100644 WPIJavaCV/hs_err_pid5892.log create mode 100644 WPIJavaCV/lib/javacpp.jar create mode 100644 WPIJavaCV/lib/javacv-windows-x86.jar create mode 100644 WPIJavaCV/lib/javacv.jar create mode 100644 WPIJavaCV/manifest.mf create mode 100644 WPIJavaCV/nbproject/build-impl.xml create mode 100644 WPIJavaCV/nbproject/genfiles.properties create mode 100644 WPIJavaCV/nbproject/private/config.properties create mode 100644 WPIJavaCV/nbproject/private/private.properties create mode 100644 WPIJavaCV/nbproject/project.properties create mode 100644 WPIJavaCV/nbproject/project.xml create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIBinaryImage.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPICamera.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColor.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColorImage.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIContour.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIDisposable.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIFFmpegVideo.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIGrayscaleImage.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIImage.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIJavaCVUtils.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPILaptopCamera.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIMemoryPool.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPoint.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPolygon.java create mode 100644 WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIWindow.java create mode 100644 extensions/camera/SquareTracker/Square.pdf create mode 100644 extensions/camera/SquareTracker/build.xml create mode 100644 extensions/camera/SquareTracker/manifest.mf create mode 100644 extensions/camera/SquareTracker/nbproject/build-impl.xml create mode 100644 extensions/camera/SquareTracker/nbproject/genfiles.properties create mode 100644 extensions/camera/SquareTracker/nbproject/private/config.properties create mode 100644 extensions/camera/SquareTracker/nbproject/private/private.properties create mode 100644 extensions/camera/SquareTracker/nbproject/private/private.xml create mode 100644 extensions/camera/SquareTracker/nbproject/project.properties create mode 100644 extensions/camera/SquareTracker/nbproject/project.xml create mode 100644 extensions/camera/SquareTracker/src/edu/wpi/first/wpilibj/examples/SquareTrackerExtension.java create mode 100644 extensions/camera/WPICameraExtension/build.xml create mode 100644 extensions/camera/WPICameraExtension/manifest.mf create mode 100644 extensions/camera/WPICameraExtension/nbproject/build-impl.xml create mode 100644 extensions/camera/WPICameraExtension/nbproject/genfiles.properties create mode 100644 extensions/camera/WPICameraExtension/nbproject/private/config.properties create mode 100644 extensions/camera/WPICameraExtension/nbproject/private/private.properties create mode 100644 extensions/camera/WPICameraExtension/nbproject/private/private.xml create mode 100644 extensions/camera/WPICameraExtension/nbproject/project.properties create mode 100644 extensions/camera/WPICameraExtension/nbproject/project.xml create mode 100644 extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/VideoStreamExtension.java create mode 100644 extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/VideoStreamViewerExtension.java create mode 100644 extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/WPICameraExtension.java create mode 100644 extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/WPIFFmpegVideoViewer.java create mode 100644 extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/WPILaptopCameraExtension.java create mode 100644 smartdashboard/.classpath create mode 100644 smartdashboard/.project create mode 100644 smartdashboard/build.xml create mode 100644 smartdashboard/customSave create mode 100644 smartdashboard/lib/jcommon-1.0.16.jar create mode 100644 smartdashboard/lib/jfreechart-1.0.13.jar create mode 100644 smartdashboard/lib/junit-4.8.2.jar create mode 100644 smartdashboard/lib/nblibraries.properties create mode 100644 smartdashboard/manifest.mf create mode 100644 smartdashboard/nbbuild.xml create mode 100644 smartdashboard/nbproject/build-impl.xml create mode 100644 smartdashboard/nbproject/genfiles.properties create mode 100644 smartdashboard/nbproject/project.properties create mode 100644 smartdashboard/nbproject/project.xml create mode 100644 smartdashboard/smartdashboard.install4j create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/ArgParser.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/LogToCSV.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/extensions/FileSniffer.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardFrame.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardMenu.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPanel.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPrefs.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/DisplayElement.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/GlassPane.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/MainPanel.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/PropertyEditor.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/StaticWidget.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/Widget.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/BooleanBox.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Button.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CheckBox.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Chooser.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Command.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CommandButton.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Compass.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ConnectionIndicator.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/DefaultDisplayElementRegistrar.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/FormattedField.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Image.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Label.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/LinePlot.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/PIDEditor.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ProgressBar.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/RobotPreferences.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Scheduler.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/SimpleDial.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Subsystem.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/TextBox.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/VideoStreamViewerExtension.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractNumberDatasetWidget.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractTableWidget.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractValueWidget.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/BooleanBindable.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/NumberBindable.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/StringBindable.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/Controller.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalInputDisplay.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalOutputController.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/EncoderDisplay.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/GyroDisplay.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LWSubsystem.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LiveWindowWidgetRegistrar.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/NameTag.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/RelayController.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/ServoController.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SingleNumberDisplay.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SpeedController.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/main.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/net/TCPImageFetcher.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/BooleanProperty.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/ColorProperty.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/DoubleProperty.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/FileProperty.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/GenericProperty.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/IPAddressProperty.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerListProperty.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerProperty.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/MultiProperty.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/NumberProperty.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/Property.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/PropertyHolder.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/StringProperty.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/properties/TextInputProperty.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/robot/Robot.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/DataType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/DisplayElementRegistry.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/NamedDataType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AccelerometerType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AnalogInputType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ButtonType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CommandType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompassType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompressorType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CounterType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalInputType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalOutputType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DoubleSolenoidType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/EncoderType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GearToothSensorType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GyroType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWGyroType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWSubsystemType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDCommandType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDSubsystemType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/QuadratureEncoderType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/RelayType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SchedulerType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ServoType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SolenoidType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SpeedControllerType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/StringChooserType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SubsystemType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/types/named/UltrasonicType.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLReader.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLWriter.java create mode 100644 smartdashboard/src/edu/wpi/first/smartdashboard/xml/XMLWidget.java diff --git a/FakeRobot/README.txt b/FakeRobot/README.txt new file mode 100644 index 0000000..a688c5c --- /dev/null +++ b/FakeRobot/README.txt @@ -0,0 +1 @@ +This is for testing the LiveWindow funcitonality in the SmartDashboard. \ No newline at end of file diff --git a/FakeRobot/build.xml b/FakeRobot/build.xml new file mode 100644 index 0000000..427012b --- /dev/null +++ b/FakeRobot/build.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + Builds, tests, and runs the project FakeRobot. + + + diff --git a/FakeRobot/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar b/FakeRobot/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar new file mode 100644 index 0000000..4cabe27 Binary files /dev/null and b/FakeRobot/lib/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar differ diff --git a/FakeRobot/lib/NetworkTables.jar b/FakeRobot/lib/NetworkTables.jar new file mode 100644 index 0000000..8ed6d45 Binary files /dev/null and b/FakeRobot/lib/NetworkTables.jar differ diff --git a/FakeRobot/lib/nblibraries.properties b/FakeRobot/lib/nblibraries.properties new file mode 100644 index 0000000..81b4b5f --- /dev/null +++ b/FakeRobot/lib/nblibraries.properties @@ -0,0 +1,3 @@ +libs.CopyLibs.classpath=\ + ${base}/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar +libs.CopyLibs.displayName=CopyLibs Task diff --git a/FakeRobot/lib/networktables-desktop-r156.jar b/FakeRobot/lib/networktables-desktop-r156.jar new file mode 100644 index 0000000..6911fc6 Binary files /dev/null and b/FakeRobot/lib/networktables-desktop-r156.jar differ diff --git a/FakeRobot/lib/networktables-desktop-r157.jar b/FakeRobot/lib/networktables-desktop-r157.jar new file mode 100644 index 0000000..12b49a6 Binary files /dev/null and b/FakeRobot/lib/networktables-desktop-r157.jar differ diff --git a/FakeRobot/lib/networktables-desktop-r158.jar b/FakeRobot/lib/networktables-desktop-r158.jar new file mode 100644 index 0000000..5efdd68 Binary files /dev/null and b/FakeRobot/lib/networktables-desktop-r158.jar differ diff --git a/FakeRobot/manifest.mf b/FakeRobot/manifest.mf new file mode 100644 index 0000000..1574df4 --- /dev/null +++ b/FakeRobot/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/FakeRobot/nbproject/build-impl.xml b/FakeRobot/nbproject/build-impl.xml new file mode 100644 index 0000000..0f3637b --- /dev/null +++ b/FakeRobot/nbproject/build-impl.xml @@ -0,0 +1,1078 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + + + + + + + + Must select one file in the IDE or set profile.class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FakeRobot/nbproject/genfiles.properties b/FakeRobot/nbproject/genfiles.properties new file mode 100644 index 0000000..3fda7d0 --- /dev/null +++ b/FakeRobot/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=42e58587 +build.xml.script.CRC32=65e52e08 +build.xml.stylesheet.CRC32=28e38971@1.50.3.46 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=42e58587 +nbproject/build-impl.xml.script.CRC32=3c08be4f +nbproject/build-impl.xml.stylesheet.CRC32=fcddb364@1.50.3.46 diff --git a/FakeRobot/nbproject/project.properties b/FakeRobot/nbproject/project.properties new file mode 100644 index 0000000..fbbd528 --- /dev/null +++ b/FakeRobot/nbproject/project.properties @@ -0,0 +1,75 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=FakeRobot +application.vendor=Sam +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/FakeRobot.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.networktables-desktop-r158.jar=lib/networktables-desktop-r158.jar +includes=** +jar.compress=false +javac.classpath=\ + ${file.reference.networktables-desktop-r158.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.7 +javac.target=1.7 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=livewindowfakerobot.LiveWindowFakeRobot +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/FakeRobot/nbproject/project.xml b/FakeRobot/nbproject/project.xml new file mode 100644 index 0000000..7b230c0 --- /dev/null +++ b/FakeRobot/nbproject/project.xml @@ -0,0 +1,18 @@ + + + org.netbeans.modules.java.j2seproject + + + FakeRobot + + + + + + + + + .\lib\nblibraries.properties + + + diff --git a/FakeRobot/src/livewindowfakerobot/LiveWindowFakeRobot.java b/FakeRobot/src/livewindowfakerobot/LiveWindowFakeRobot.java new file mode 100644 index 0000000..bfb544d --- /dev/null +++ b/FakeRobot/src/livewindowfakerobot/LiveWindowFakeRobot.java @@ -0,0 +1,82 @@ + +package livewindowfakerobot; + +import edu.wpi.first.wpilibj.networktables.NetworkTable; +import edu.wpi.first.wpilibj.tables.ITable; +import java.util.Timer; +import java.util.TimerTask; + +/** + * + * @author Sam + */ +public class LiveWindowFakeRobot { + + private static final NetworkTable liveWindow = NetworkTable.getTable("LiveWindow"); + + private static final ITable STATUS = createTable(liveWindow, "~STATUS~", "LW Status"), + + wrist = createTable(liveWindow, "Wrist", "LW Subsystem"), + wPotentiometer = createTable(wrist, "Potentiometer", "Analog Input"), + wVictor = createTable(wrist, "Victor", "Speed Controller"), + + elevator = createTable(liveWindow, "Elevator", "LW Subsystem"), + ePotentiometer = createTable(elevator, "Potentiometer", "Analog Input"), + eVictor = createTable(elevator, "Victor", "Speed Controller"), + + testSys = createTable(liveWindow, "TestSystem", "LW Subsystem"), + tComp = createTable(testSys, "Compressor", "Compressor"), + tGearTooth = createTable(testSys, "Gear Tooth Sensor", "Gear Tooth Sensor"), + tVictor = createTable(testSys, "Victor", "Speed Controller"), + tPotentiometer = createTable(testSys, "Potentiometer", "Analog Input"), + tRelay = createTable(testSys, "Spike", "Relay"), + tDigitalOutput = createTable(testSys, "Digital Output", "Digital Output"), + tGyro = createTable(testSys, "Gyro", "LWGyro"), + tSolenoid = createTable(testSys, "Solenoid", "Solenoid"), + tServo = createTable(testSys, "Serov the Servo", "Servo"), + tAccel = createTable(testSys, "Accelerometer", "Accelerometer"), + tEncoder1 = createTable(testSys, "Encoder 1", "Encoder"), + tUltra = createTable(testSys, "Ultrasonic", "Ultrasonic"), + tCompass = createTable(testSys, "Compass", "Compass"), + tSwitch = createTable(testSys, "Limit Switch", "Digital Input"); + + public static void main(String[] args) { + + System.out.println(); + + STATUS.putBoolean("LW Enabled", false); + STATUS.putString("Robot", "Testing"); + wPotentiometer.putNumber("Value", 2.6); + ePotentiometer.putNumber("Value", -11.6872); + tSwitch.putString("Value", "Off"); + + + (new Timer()).schedule( + new TimerTask(){ + @Override + public void run() { + wPotentiometer.putNumber("Value", (Math.random()-.5) * 24); + ePotentiometer.putNumber("Value", (Math.random()-.5) * 24); + tPotentiometer.putNumber("Value", (Math.random()-.5) * 24); + tGyro.putNumber("Value", Math.random() * 360); + tAccel.putNumber("Value", (Math.random()-.5)*8); + tSwitch.putString("Value", Math.random() < 0.5 ? "On" : "Off"); + tEncoder1.putNumber("Speed", Math.random() * 20); + tEncoder1.putNumber("Distance", Math.random() * 10); + tEncoder1.putNumber("Distance per Tick", Math.random()); + tCompass.putNumber("Value", Math.random()); + tUltra.putNumber("Value", (Math.random()-.5) * 200); + tGearTooth.putNumber("Value", (int)(Math.random() * 100)); + }}, + 0, 500); + + } + + private static ITable createTable(ITable parent, String name, String type) { + ITable table = parent.getSubTable(name); + System.out.println(table); + table.putString("~TYPE~", type); + table.putString("Name", name); + return table; + } +} diff --git a/WPIJavaCV/build.xml b/WPIJavaCV/build.xml new file mode 100644 index 0000000..6159d51 --- /dev/null +++ b/WPIJavaCV/build.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + Builds, tests, and runs the project WPIJavaCV. + + + diff --git a/WPIJavaCV/hs_err_pid4572.log b/WPIJavaCV/hs_err_pid4572.log new file mode 100644 index 0000000..aa2e2ab --- /dev/null +++ b/WPIJavaCV/hs_err_pid4572.log @@ -0,0 +1,264 @@ +# +# A fatal error has been detected by the Java Runtime Environment: +# +# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x76f6a6c5, pid=4572, tid=5836 +# +# JRE version: 6.0_23-b05 +# Java VM: Java HotSpot(TM) Client VM (19.0-b09 mixed mode, sharing windows-x86 ) +# Problematic frame: +# C [ntdll.dll+0x6a6c5] +# +# If you would like to submit a bug report, please visit: +# http://java.sun.com/webapps/bugreport/crash.jsp +# The crash happened outside the Java Virtual Machine in native code. +# See problematic frame for where to report the bug. +# + +--------------- T H R E A D --------------- + +Current thread (0x00eb9000): JavaThread "main" [_thread_in_native, id=5836, stack(0x000c0000,0x00110000)] + +siginfo: ExceptionCode=0xc0000005, writing address 0x01b7901c + +Registers: +EAX=0x000000e8, EBX=0x01b70000, ECX=0x000027ed, EDX=0xfffffffd +ESP=0x0010f508, EBP=0x0010f530, ESI=0x01b788d8, EDI=0x01b788d8 +EIP=0x76f6a6c5, EFLAGS=0x00010206 + +Register to memory mapping: + +EAX=0x000000e8 +0x000000e8 is pointing to unknown location + +EBX=0x01b70000 +0x01b70000 is pointing to unknown location + +ECX=0x000027ed +0x000027ed is pointing to unknown location + +EDX=0xfffffffd +0xfffffffd is pointing to unknown location + +ESP=0x0010f508 +0x0010f508 is pointing into the stack for thread: 0x00eb9000 +"main" prio=6 tid=0x00eb9000 nid=0x16cc runnable [0x0010f000] + java.lang.Thread.State: RUNNABLE + +EBP=0x0010f530 +0x0010f530 is pointing into the stack for thread: 0x00eb9000 +"main" prio=6 tid=0x00eb9000 nid=0x16cc runnable [0x0010f000] + java.lang.Thread.State: RUNNABLE + +ESI=0x01b788d8 +0x01b788d8 is pointing to unknown location + +EDI=0x01b788d8 +0x01b788d8 is pointing to unknown location + + +Top of Stack: (sp=0x0010f508) +0x0010f508: 00000000 01b78910 01b70000 00000000 +0x0010f518: 00000000 05080048 05080048 01b78268 +0x0010f528: 000000e1 00000061 0010f628 76f68257 +0x0010f538: 01b70000 01b78910 0010f5d8 00000000 +0x0010f548: 6d74e4df 00000000 01b70000 01b78918 +0x0010f558: 00eb9a38 00eb9de4 0010f928 0010f6c0 +0x0010f568: 0000000b 00000000 0010f5b4 6da28a91 +0x0010f578: 0010f6b8 01b70000 01b70148 01cc9290 + +Instructions: (pc=0x76f6a6c5) +0x76f6a6b5: 8b 08 66 89 0e 66 8b 08 66 33 4b 54 8b 00 8b fe +0x76f6a6c5: 66 89 4c c6 04 e9 c9 d8 ff ff 90 00 00 1f 00 3b + + +Stack: [0x000c0000,0x00110000], sp=0x0010f508, free space=317k +Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) +C [ntdll.dll+0x6a6c5] +C [ntdll.dll+0x68257] +C [ntdll.dll+0x68492] +C [kernel32.dll+0x4c56f] +C [msvcr100.dll+0x1016a] +C [opencv_core220.dll+0x1014] +C [opencv_core220.dll+0x4d50d] +C [jniopencv_core1178301110058019210.dll+0x2051c] +j com.googlecode.javacv.cpp.opencv_core.cvReleaseMemStorage(Lcom/googlecode/javacv/cpp/opencv_core$CvMemStorage;)V+0 +j com.googlecode.javacv.cpp.opencv_core$CvMemStorage$ReleaseDeallocator.deallocate()V+1 +j com.googlecode.javacpp.Pointer$DeallocatorReference.clear()V+8 +j com.googlecode.javacpp.Pointer.deallocator(Lcom/googlecode/javacpp/Pointer$Deallocator;)Lcom/googlecode/javacpp/Pointer;+22 +j com.googlecode.javacpp.Pointer.init(JJ)V+16 +v ~StubRoutines::call_stub +V [jvm.dll+0xf33c9] +V [jvm.dll+0x188a91] +V [jvm.dll+0xf344d] +V [jvm.dll+0xfd07f] +V [jvm.dll+0x102647] +C [jniopencv_core1178301110058019210.dll+0x13264] +j com.googlecode.javacv.cpp.opencv_core$CvSize.allocate()V+0 +j com.googlecode.javacv.cpp.opencv_core$CvSize.()V+5 +j com.googlecode.javacv.cpp.opencv_core.cvSize(II)Lcom/googlecode/javacv/cpp/opencv_core$CvSize;+4 +j com.googlecode.javacv.cpp.opencv_core$IplImage.create(IIII)Lcom/googlecode/javacv/cpp/opencv_core$IplImage;+2 +j com.googlecode.javacv.cpp.opencv_core$IplImage.createFrom(Ljava/awt/image/BufferedImage;)Lcom/googlecode/javacv/cpp/opencv_core$IplImage;+164 +j wpijavacv.WpiImage.(Ljava/awt/image/BufferedImage;)V+6 +j wpijavacv.WpiColorImage.(Ljava/awt/image/BufferedImage;)V+2 +j wpijavacv.WpiCamera.getImage()Lwpijavacv/WpiColorImage;+53 +j wpijavacv.Main.main([Ljava/lang/String;)V+30 +v ~StubRoutines::call_stub +V [jvm.dll+0xf33c9] +V [jvm.dll+0x188a91] +V [jvm.dll+0xf344d] +V [jvm.dll+0xfce3c] +V [jvm.dll+0x104a8d] +C [java.exe+0x2155] +C [java.exe+0x85b4] +C [kernel32.dll+0x44911] +C [ntdll.dll+0x3e1c6] +C [ntdll.dll+0x3e199] + +Java frames: (J=compiled Java code, j=interpreted, Vv=VM code) +j com.googlecode.javacv.cpp.opencv_core.cvReleaseMemStorage(Lcom/googlecode/javacv/cpp/opencv_core$CvMemStorage;)V+0 +j com.googlecode.javacv.cpp.opencv_core$CvMemStorage$ReleaseDeallocator.deallocate()V+1 +j com.googlecode.javacpp.Pointer$DeallocatorReference.clear()V+8 +j com.googlecode.javacpp.Pointer.deallocator(Lcom/googlecode/javacpp/Pointer$Deallocator;)Lcom/googlecode/javacpp/Pointer;+22 +j com.googlecode.javacpp.Pointer.init(JJ)V+16 +v ~StubRoutines::call_stub +j com.googlecode.javacv.cpp.opencv_core$CvSize.allocate()V+0 +j com.googlecode.javacv.cpp.opencv_core$CvSize.()V+5 +j com.googlecode.javacv.cpp.opencv_core.cvSize(II)Lcom/googlecode/javacv/cpp/opencv_core$CvSize;+4 +j com.googlecode.javacv.cpp.opencv_core$IplImage.create(IIII)Lcom/googlecode/javacv/cpp/opencv_core$IplImage;+2 +j com.googlecode.javacv.cpp.opencv_core$IplImage.createFrom(Ljava/awt/image/BufferedImage;)Lcom/googlecode/javacv/cpp/opencv_core$IplImage;+164 +j wpijavacv.WpiImage.(Ljava/awt/image/BufferedImage;)V+6 +j wpijavacv.WpiColorImage.(Ljava/awt/image/BufferedImage;)V+2 +j wpijavacv.WpiCamera.getImage()Lwpijavacv/WpiColorImage;+53 +j wpijavacv.Main.main([Ljava/lang/String;)V+30 +v ~StubRoutines::call_stub + +--------------- P R O C E S S --------------- + +Java Threads: ( => current thread ) + 0x03f78400 JavaThread "Java2D Disposer" daemon [_thread_blocked, id=4680, stack(0x040f0000,0x04140000)] + 0x01c7a800 JavaThread "Thread-0" [_thread_in_native, id=5376, stack(0x03ea0000,0x03ef0000)] + 0x01c3fc00 JavaThread "Low Memory Detector" daemon [_thread_blocked, id=940, stack(0x03e00000,0x03e50000)] + 0x01c3b000 JavaThread "CompilerThread0" daemon [_thread_blocked, id=3064, stack(0x03db0000,0x03e00000)] + 0x01c38000 JavaThread "Attach Listener" daemon [_thread_blocked, id=1676, stack(0x03d60000,0x03db0000)] + 0x01c34c00 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=4684, stack(0x03d10000,0x03d60000)] + 0x01bf7000 JavaThread "Finalizer" daemon [_thread_blocked, id=4688, stack(0x03cc0000,0x03d10000)] + 0x01bf1c00 JavaThread "Reference Handler" daemon [_thread_blocked, id=4800, stack(0x00e10000,0x00e60000)] +=>0x00eb9000 JavaThread "main" [_thread_in_native, id=5836, stack(0x000c0000,0x00110000)] + +Other Threads: + 0x01bf0800 VMThread [stack: 0x00910000,0x00960000] [id=4888] + 0x01c51800 WatcherThread [stack: 0x03e50000,0x03ea0000] [id=4408] + +VM state:not at safepoint (normal execution) + +VM Mutex/Monitor currently owned by a thread: None + +Heap + def new generation total 4992K, used 1747K [0x23af0000, 0x24050000, 0x29040000) + eden space 4480K, 39% used [0x23af0000, 0x23ca4ed8, 0x23f50000) + from space 512K, 0% used [0x23f50000, 0x23f50000, 0x23fd0000) + to space 512K, 0% used [0x23fd0000, 0x23fd0000, 0x24050000) + tenured generation total 10944K, used 2148K [0x29040000, 0x29af0000, 0x33af0000) + the space 10944K, 19% used [0x29040000, 0x292591c0, 0x29259200, 0x29af0000) + compacting perm gen total 12288K, used 1545K [0x33af0000, 0x346f0000, 0x37af0000) + the space 12288K, 12% used [0x33af0000, 0x33c725e0, 0x33c72600, 0x346f0000) + ro space 10240K, 54% used [0x37af0000, 0x3806c6b0, 0x3806c800, 0x384f0000) + rw space 12288K, 55% used [0x384f0000, 0x38b901f0, 0x38b90200, 0x390f0000) + +Dynamic libraries: +0x00400000 - 0x00424000 C:\Program Files\Java\jdk1.6.0_23\bin\java.exe +0x76f00000 - 0x77028000 C:\Windows\system32\ntdll.dll +0x75e90000 - 0x75f6b000 C:\Windows\system32\kernel32.dll +0x76290000 - 0x76356000 C:\Windows\system32\ADVAPI32.dll +0x75860000 - 0x75922000 C:\Windows\system32\RPCRT4.dll +0x7c340000 - 0x7c396000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\msvcr71.dll +0x6d8a0000 - 0x6db4c000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\client\jvm.dll +0x761f0000 - 0x7628d000 C:\Windows\system32\USER32.dll +0x75d70000 - 0x75dbb000 C:\Windows\system32\GDI32.dll +0x73bd0000 - 0x73c02000 C:\Windows\system32\WINMM.dll +0x75ae0000 - 0x75b8a000 C:\Windows\system32\msvcrt.dll +0x75f70000 - 0x760b4000 C:\Windows\system32\ole32.dll +0x757d0000 - 0x7585d000 C:\Windows\system32\OLEAUT32.dll +0x746f0000 - 0x74729000 C:\Windows\system32\OLEACC.dll +0x75d50000 - 0x75d6e000 C:\Windows\system32\IMM32.DLL +0x75dc0000 - 0x75e88000 C:\Windows\system32\MSCTF.dll +0x75570000 - 0x7559c000 C:\Windows\system32\apphelp.dll +0x77030000 - 0x77039000 C:\Windows\system32\LPK.DLL +0x75a10000 - 0x75a8d000 C:\Windows\system32\USP10.dll +0x6d850000 - 0x6d85c000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\verify.dll +0x6d3d0000 - 0x6d3ef000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\java.dll +0x6d330000 - 0x6d338000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\hpi.dll +0x756d0000 - 0x756d7000 C:\Windows\system32\PSAPI.DLL +0x6d890000 - 0x6d89f000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\zip.dll +0x6d6b0000 - 0x6d6c3000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\net.dll +0x75b90000 - 0x75bbd000 C:\Windows\system32\WS2_32.dll +0x77050000 - 0x77056000 C:\Windows\system32\NSI.dll +0x74d90000 - 0x74dcb000 C:\Windows\system32\mswsock.dll +0x74df0000 - 0x74df5000 C:\Windows\System32\wship6.dll +0x74a50000 - 0x74a55000 C:\Windows\System32\wshtcpip.dll +0x74af0000 - 0x74b2b000 C:\Windows\system32\rsaenh.dll +0x75620000 - 0x7563e000 C:\Windows\system32\USERENV.dll +0x75600000 - 0x75614000 C:\Windows\system32\Secur32.dll +0x74120000 - 0x7412f000 C:\Windows\system32\NLAapi.dll +0x74fd0000 - 0x74fe9000 C:\Windows\system32\IPHLPAPI.DLL +0x74f90000 - 0x74fc5000 C:\Windows\system32\dhcpcsvc.DLL +0x75220000 - 0x7524c000 C:\Windows\system32\DNSAPI.dll +0x74f80000 - 0x74f87000 C:\Windows\system32\WINNSI.DLL +0x74f50000 - 0x74f71000 C:\Windows\system32\dhcpcsvc6.DLL +0x70df0000 - 0x70dff000 C:\Windows\system32\napinsp.dll +0x70ab0000 - 0x70ac2000 C:\Windows\system32\pnrpnsp.dll +0x70de0000 - 0x70de8000 C:\Windows\System32\winrnr.dll +0x770e0000 - 0x7712a000 C:\Windows\system32\WLDAP32.dll +0x70aa0000 - 0x70aac000 C:\Windows\system32\wshbth.dll +0x75bc0000 - 0x75d4a000 C:\Windows\system32\SETUPAPI.dll +0x71c50000 - 0x71c56000 C:\Windows\system32\rasadhlp.dll +0x6d0b0000 - 0x6d1fa000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\awt.dll +0x746a0000 - 0x746e2000 C:\Windows\system32\WINSPOOL.DRV +0x744c0000 - 0x7465e000 C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.6001.18523_none_5cdd65e20837faf2\COMCTL32.dll +0x75770000 - 0x757c8000 C:\Windows\system32\SHLWAPI.dll +0x6d4f0000 - 0x6d515000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\jpeg.dll +0x69d60000 - 0x69e1e000 C:\Windows\System32\msvcr100.dll +0x6f8f0000 - 0x6f959000 C:\Windows\System32\msvcp100.dll +0x62ea0000 - 0x63090000 C:\Program Files\OpenCV_2.2.0\bin\opencv_core220.dll +0x10000000 - 0x10062000 C:\Users\Greg\AppData\Local\Temp\jniopencv_core1178301110058019210.dll +0x62850000 - 0x62a17000 C:\Program Files\OpenCV_2.2.0\bin\opencv_imgproc220.dll +0x04160000 - 0x04192000 C:\Users\Greg\AppData\Local\Temp\jniopencv_imgproc3651609395501697683.dll +0x600e0000 - 0x60b06000 C:\Program Files\OpenCV_2.2.0\bin\opencv_ffmpeg220.dll +0x671e0000 - 0x672a8000 C:\Program Files\OpenCV_2.2.0\bin\opencv_highgui220.dll +0x70220000 - 0x70239000 C:\Windows\system32\AVIFIL32.dll +0x73740000 - 0x73754000 C:\Windows\system32\MSACM32.dll +0x73930000 - 0x73953000 C:\Windows\system32\MSVFW32.dll +0x76360000 - 0x76e70000 C:\Windows\system32\SHELL32.dll +0x70240000 - 0x70253000 C:\Windows\system32\AVICAP32.dll +0x74e00000 - 0x74e08000 C:\Windows\system32\VERSION.dll +0x04310000 - 0x0432d000 C:\Users\Greg\AppData\Local\Temp\jniopencv_highgui2924059676271149559.dll +0x747c0000 - 0x747ff000 C:\Windows\system32\uxtheme.dll +0x76e70000 - 0x76ef4000 C:\Windows\system32\CLBCatQ.DLL + +VM Arguments: +jvm_args: -Dfile.encoding=UTF-8 +java_command: wpijavacv.Main +Launcher Type: SUN_STANDARD + +Environment Variables: +CLASSPATH=.;C:\Program Files\Java\jre6\lib\ext\QTJava.zip +PATH=C:\Program Files\PC Connectivity Solution\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files\ATI Technologies\ATI.ACE\Core-Static;c:\Program Files\Microsoft SQL Server\90\Tools\binn\;C:\Program Files\Common Files\Roxio Shared\9.0\DLLShared\;C:\Program Files\IVT Corporation\BlueSoleil\Mobile;C:\Windows\system32\Wbem;C:\Program Files\QuickTime\QTSystem\;C:\Program Files\Mercurial;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\OpenCV_2.2.0\bin +USERNAME=Greg +OS=Windows_NT +PROCESSOR_IDENTIFIER=x86 Family 6 Model 15 Stepping 13, GenuineIntel + + + +--------------- S Y S T E M --------------- + +OS: Windows Vista Build 6001 Service Pack 1 + +CPU:total 2 (2 cores per cpu, 1 threads per core) family 6 model 15 stepping 13, cmov, cx8, fxsr, mmx, sse, sse2, sse3, ssse3 + +Memory: 4k page, physical 3142056k(1595804k free), swap 6491236k(4604608k free) + +vm_info: Java HotSpot(TM) Client VM (19.0-b09) for windows-x86 JRE (1.6.0_23-b05), built on Nov 12 2010 15:00:43 by "java_re" with MS VC++ 7.1 (VS2003) + +time: Tue Jul 05 11:53:19 2011 +elapsed time: 0 seconds + diff --git a/WPIJavaCV/hs_err_pid5372.log b/WPIJavaCV/hs_err_pid5372.log new file mode 100644 index 0000000..7fab28e --- /dev/null +++ b/WPIJavaCV/hs_err_pid5372.log @@ -0,0 +1,261 @@ +# +# A fatal error has been detected by the Java Runtime Environment: +# +# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x62eed33b, pid=5372, tid=1028 +# +# JRE version: 6.0_23-b05 +# Java VM: Java HotSpot(TM) Client VM (19.0-b09 mixed mode, sharing windows-x86 ) +# Problematic frame: +# C [opencv_core220.dll+0x4d33b] +# +# If you would like to submit a bug report, please visit: +# http://java.sun.com/webapps/bugreport/crash.jsp +# The crash happened outside the Java Virtual Machine in native code. +# See problematic frame for where to report the bug. +# + +--------------- T H R E A D --------------- + +Current thread (0x00c59000): JavaThread "main" [_thread_in_native, id=1028, stack(0x00320000,0x00370000)] + +siginfo: ExceptionCode=0xc0000005, reading address 0x00000108 + +Registers: +EAX=0x00000100, EBX=0x00000000, ECX=0x0036f79c, EDX=0x00000000 +ESP=0x0036f588, EBP=0x0036f67c, ESI=0x04518760, EDI=0x00000000 +EIP=0x62eed33b, EFLAGS=0x00010206 + +Register to memory mapping: + +EAX=0x00000100 +0x00000100 is pointing to unknown location + +EBX=0x00000000 +0x00000000 is pointing to unknown location + +ECX=0x0036f79c +0x0036f79c is pointing into the stack for thread: 0x00c59000 +"main" prio=6 tid=0x00c59000 nid=0x404 runnable [0x0036f000] + java.lang.Thread.State: RUNNABLE + +EDX=0x00000000 +0x00000000 is pointing to unknown location + +ESP=0x0036f588 +0x0036f588 is pointing into the stack for thread: 0x00c59000 +"main" prio=6 tid=0x00c59000 nid=0x404 runnable [0x0036f000] + java.lang.Thread.State: RUNNABLE + +EBP=0x0036f67c +0x0036f67c is pointing into the stack for thread: 0x00c59000 +"main" prio=6 tid=0x00c59000 nid=0x404 runnable [0x0036f000] + java.lang.Thread.State: RUNNABLE + +ESI=0x04518760 +0x04518760 is pointing to unknown location + +EDI=0x00000000 +0x00000000 is pointing to unknown location + + +Top of Stack: (sp=0x0036f588) +0x0036f588: 21cfae74 0036f7f8 00000000 00000000 +0x0036f598: 671ee9fc 21cfaf78 045181f0 0036f79c +0x0036f5a8: 62ec6397 00000780 0036f79c 0036f79c +0x0036f5b8: 00000003 0036f7ac 6303310f 00000280 +0x0036f5c8: 000001e0 00250366 045181f0 00000003 +0x0036f5d8: 2e011dd1 00000001 00000000 00000000 +0x0036f5e8: 00000280 000001e0 00000000 00000000 +0x0036f5f8: fffffffe 00000000 04510000 044f30c0 + +Instructions: (pc=0x62eed33b) +0x62eed32b: 00 00 89 7d c8 c6 45 b8 00 8b 46 0c 3b c7 74 03 +0x62eed33b: 8b 78 08 8b 5e 04 33 c0 3b d8 74 57 8b 56 0c 8d + + +Stack: [0x00320000,0x00370000], sp=0x0036f588, free space=317k +Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) +C [opencv_core220.dll+0x4d33b] +C [opencv_core220.dll+0x4d507] +C [jniopencv_core1361724873659260119.dll+0x2051c] +j com.googlecode.javacv.cpp.opencv_core.cvReleaseMemStorage(Lcom/googlecode/javacv/cpp/opencv_core$CvMemStorage;)V+0 +j com.googlecode.javacv.cpp.opencv_core$CvMemStorage$ReleaseDeallocator.deallocate()V+1 +j com.googlecode.javacpp.Pointer$DeallocatorReference.clear()V+8 +j com.googlecode.javacpp.Pointer.deallocator(Lcom/googlecode/javacpp/Pointer$Deallocator;)Lcom/googlecode/javacpp/Pointer;+22 +j com.googlecode.javacpp.Pointer.init(JJ)V+16 +v ~StubRoutines::call_stub +V [jvm.dll+0xf33c9] +V [jvm.dll+0x188a91] +V [jvm.dll+0xf344d] +V [jvm.dll+0xfd07f] +V [jvm.dll+0x102647] +C [jniopencv_core1361724873659260119.dll+0x13264] +j com.googlecode.javacv.cpp.opencv_core$CvSize.allocate()V+0 +j com.googlecode.javacv.cpp.opencv_core$CvSize.()V+5 +j com.googlecode.javacv.cpp.opencv_core.cvSize(II)Lcom/googlecode/javacv/cpp/opencv_core$CvSize;+4 +j com.googlecode.javacv.cpp.opencv_core$IplImage.create(IIII)Lcom/googlecode/javacv/cpp/opencv_core$IplImage;+2 +j com.googlecode.javacv.cpp.opencv_core$IplImage.createFrom(Ljava/awt/image/BufferedImage;)Lcom/googlecode/javacv/cpp/opencv_core$IplImage;+164 +j wpijavacv.WpiImage.(Ljava/awt/image/BufferedImage;)V+6 +j wpijavacv.WpiColorImage.(Ljava/awt/image/BufferedImage;)V+2 +j wpijavacv.WpiCamera.getImage()Lwpijavacv/WpiColorImage;+53 +j wpijavacv.Main.main([Ljava/lang/String;)V+30 +v ~StubRoutines::call_stub +V [jvm.dll+0xf33c9] +V [jvm.dll+0x188a91] +V [jvm.dll+0xf344d] +V [jvm.dll+0xfce3c] +V [jvm.dll+0x104a8d] +C [java.exe+0x2155] +C [java.exe+0x85b4] +C [kernel32.dll+0x44911] +C [ntdll.dll+0x3e1c6] +C [ntdll.dll+0x3e199] + +Java frames: (J=compiled Java code, j=interpreted, Vv=VM code) +j com.googlecode.javacv.cpp.opencv_core.cvReleaseMemStorage(Lcom/googlecode/javacv/cpp/opencv_core$CvMemStorage;)V+0 +j com.googlecode.javacv.cpp.opencv_core$CvMemStorage$ReleaseDeallocator.deallocate()V+1 +j com.googlecode.javacpp.Pointer$DeallocatorReference.clear()V+8 +j com.googlecode.javacpp.Pointer.deallocator(Lcom/googlecode/javacpp/Pointer$Deallocator;)Lcom/googlecode/javacpp/Pointer;+22 +j com.googlecode.javacpp.Pointer.init(JJ)V+16 +v ~StubRoutines::call_stub +j com.googlecode.javacv.cpp.opencv_core$CvSize.allocate()V+0 +j com.googlecode.javacv.cpp.opencv_core$CvSize.()V+5 +j com.googlecode.javacv.cpp.opencv_core.cvSize(II)Lcom/googlecode/javacv/cpp/opencv_core$CvSize;+4 +j com.googlecode.javacv.cpp.opencv_core$IplImage.create(IIII)Lcom/googlecode/javacv/cpp/opencv_core$IplImage;+2 +j com.googlecode.javacv.cpp.opencv_core$IplImage.createFrom(Ljava/awt/image/BufferedImage;)Lcom/googlecode/javacv/cpp/opencv_core$IplImage;+164 +j wpijavacv.WpiImage.(Ljava/awt/image/BufferedImage;)V+6 +j wpijavacv.WpiColorImage.(Ljava/awt/image/BufferedImage;)V+2 +j wpijavacv.WpiCamera.getImage()Lwpijavacv/WpiColorImage;+53 +j wpijavacv.Main.main([Ljava/lang/String;)V+30 +v ~StubRoutines::call_stub + +--------------- P R O C E S S --------------- + +Java Threads: ( => current thread ) + 0x03f7b400 JavaThread "Java2D Disposer" daemon [_thread_blocked, id=3788, stack(0x042e0000,0x04330000)] + 0x01b06400 JavaThread "Thread-0" [_thread_in_native, id=696, stack(0x03ea0000,0x03ef0000)] + 0x01ac7800 JavaThread "Low Memory Detector" daemon [_thread_blocked, id=5780, stack(0x03e00000,0x03e50000)] + 0x01ab8800 JavaThread "CompilerThread0" daemon [_thread_blocked, id=4132, stack(0x03db0000,0x03e00000)] + 0x01ab7800 JavaThread "Attach Listener" daemon [_thread_blocked, id=5612, stack(0x03d60000,0x03db0000)] + 0x01ab6800 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=2232, stack(0x03d10000,0x03d60000)] + 0x01a73c00 JavaThread "Finalizer" daemon [_thread_blocked, id=4604, stack(0x03cc0000,0x03d10000)] + 0x01a71c00 JavaThread "Reference Handler" daemon [_thread_blocked, id=4480, stack(0x03c70000,0x03cc0000)] +=>0x00c59000 JavaThread "main" [_thread_in_native, id=1028, stack(0x00320000,0x00370000)] + +Other Threads: + 0x01a70800 VMThread [stack: 0x00c00000,0x00c50000] [id=3756] + 0x01ac9400 WatcherThread [stack: 0x03e50000,0x03ea0000] [id=3988] + +VM state:not at safepoint (normal execution) + +VM Mutex/Monitor currently owned by a thread: None + +Heap + def new generation total 4992K, used 33K [0x23af0000, 0x24050000, 0x29040000) + eden space 4480K, 0% used [0x23af0000, 0x23af8480, 0x23f50000) + from space 512K, 0% used [0x23f50000, 0x23f50000, 0x23fd0000) + to space 512K, 0% used [0x23fd0000, 0x23fd0000, 0x24050000) + tenured generation total 10944K, used 3100K [0x29040000, 0x29af0000, 0x33af0000) + the space 10944K, 28% used [0x29040000, 0x29347208, 0x29347400, 0x29af0000) + compacting perm gen total 12288K, used 1545K [0x33af0000, 0x346f0000, 0x37af0000) + the space 12288K, 12% used [0x33af0000, 0x33c725c8, 0x33c72600, 0x346f0000) + ro space 10240K, 54% used [0x37af0000, 0x3806c6b0, 0x3806c800, 0x384f0000) + rw space 12288K, 55% used [0x384f0000, 0x38b901f0, 0x38b90200, 0x390f0000) + +Dynamic libraries: +0x00400000 - 0x00424000 C:\Program Files\Java\jdk1.6.0_23\bin\java.exe +0x76f00000 - 0x77028000 C:\Windows\system32\ntdll.dll +0x75e90000 - 0x75f6b000 C:\Windows\system32\kernel32.dll +0x76290000 - 0x76356000 C:\Windows\system32\ADVAPI32.dll +0x75860000 - 0x75922000 C:\Windows\system32\RPCRT4.dll +0x7c340000 - 0x7c396000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\msvcr71.dll +0x6d8a0000 - 0x6db4c000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\client\jvm.dll +0x761f0000 - 0x7628d000 C:\Windows\system32\USER32.dll +0x75d70000 - 0x75dbb000 C:\Windows\system32\GDI32.dll +0x73bd0000 - 0x73c02000 C:\Windows\system32\WINMM.dll +0x75ae0000 - 0x75b8a000 C:\Windows\system32\msvcrt.dll +0x75f70000 - 0x760b4000 C:\Windows\system32\ole32.dll +0x757d0000 - 0x7585d000 C:\Windows\system32\OLEAUT32.dll +0x746f0000 - 0x74729000 C:\Windows\system32\OLEACC.dll +0x75d50000 - 0x75d6e000 C:\Windows\system32\IMM32.DLL +0x75dc0000 - 0x75e88000 C:\Windows\system32\MSCTF.dll +0x75570000 - 0x7559c000 C:\Windows\system32\apphelp.dll +0x77030000 - 0x77039000 C:\Windows\system32\LPK.DLL +0x75a10000 - 0x75a8d000 C:\Windows\system32\USP10.dll +0x6d850000 - 0x6d85c000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\verify.dll +0x6d3d0000 - 0x6d3ef000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\java.dll +0x6d330000 - 0x6d338000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\hpi.dll +0x756d0000 - 0x756d7000 C:\Windows\system32\PSAPI.DLL +0x6d890000 - 0x6d89f000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\zip.dll +0x6d6b0000 - 0x6d6c3000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\net.dll +0x75b90000 - 0x75bbd000 C:\Windows\system32\WS2_32.dll +0x77050000 - 0x77056000 C:\Windows\system32\NSI.dll +0x74d90000 - 0x74dcb000 C:\Windows\system32\mswsock.dll +0x74df0000 - 0x74df5000 C:\Windows\System32\wship6.dll +0x74a50000 - 0x74a55000 C:\Windows\System32\wshtcpip.dll +0x74af0000 - 0x74b2b000 C:\Windows\system32\rsaenh.dll +0x75620000 - 0x7563e000 C:\Windows\system32\USERENV.dll +0x75600000 - 0x75614000 C:\Windows\system32\Secur32.dll +0x74120000 - 0x7412f000 C:\Windows\system32\NLAapi.dll +0x74fd0000 - 0x74fe9000 C:\Windows\system32\IPHLPAPI.DLL +0x74f90000 - 0x74fc5000 C:\Windows\system32\dhcpcsvc.DLL +0x75220000 - 0x7524c000 C:\Windows\system32\DNSAPI.dll +0x74f80000 - 0x74f87000 C:\Windows\system32\WINNSI.DLL +0x74f50000 - 0x74f71000 C:\Windows\system32\dhcpcsvc6.DLL +0x70df0000 - 0x70dff000 C:\Windows\system32\napinsp.dll +0x70ab0000 - 0x70ac2000 C:\Windows\system32\pnrpnsp.dll +0x70de0000 - 0x70de8000 C:\Windows\System32\winrnr.dll +0x770e0000 - 0x7712a000 C:\Windows\system32\WLDAP32.dll +0x70aa0000 - 0x70aac000 C:\Windows\system32\wshbth.dll +0x75bc0000 - 0x75d4a000 C:\Windows\system32\SETUPAPI.dll +0x71c50000 - 0x71c56000 C:\Windows\system32\rasadhlp.dll +0x6d0b0000 - 0x6d1fa000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\awt.dll +0x746a0000 - 0x746e2000 C:\Windows\system32\WINSPOOL.DRV +0x744c0000 - 0x7465e000 C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.6001.18523_none_5cdd65e20837faf2\COMCTL32.dll +0x75770000 - 0x757c8000 C:\Windows\system32\SHLWAPI.dll +0x6d4f0000 - 0x6d515000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\jpeg.dll +0x69d60000 - 0x69e1e000 C:\Windows\System32\msvcr100.dll +0x6f8f0000 - 0x6f959000 C:\Windows\System32\msvcp100.dll +0x62ea0000 - 0x63090000 C:\Program Files\OpenCV_2.2.0\bin\opencv_core220.dll +0x10000000 - 0x10062000 C:\Users\Greg\AppData\Local\Temp\jniopencv_core1361724873659260119.dll +0x62850000 - 0x62a17000 C:\Program Files\OpenCV_2.2.0\bin\opencv_imgproc220.dll +0x04550000 - 0x04582000 C:\Users\Greg\AppData\Local\Temp\jniopencv_imgproc1850122192161939134.dll +0x600e0000 - 0x60b06000 C:\Program Files\OpenCV_2.2.0\bin\opencv_ffmpeg220.dll +0x671e0000 - 0x672a8000 C:\Program Files\OpenCV_2.2.0\bin\opencv_highgui220.dll +0x70240000 - 0x70259000 C:\Windows\system32\AVIFIL32.dll +0x73740000 - 0x73754000 C:\Windows\system32\MSACM32.dll +0x73930000 - 0x73953000 C:\Windows\system32\MSVFW32.dll +0x76360000 - 0x76e70000 C:\Windows\system32\SHELL32.dll +0x70220000 - 0x70233000 C:\Windows\system32\AVICAP32.dll +0x74e00000 - 0x74e08000 C:\Windows\system32\VERSION.dll +0x04590000 - 0x045ad000 C:\Users\Greg\AppData\Local\Temp\jniopencv_highgui8897131303661785056.dll +0x747c0000 - 0x747ff000 C:\Windows\system32\uxtheme.dll +0x76e70000 - 0x76ef4000 C:\Windows\system32\CLBCatQ.DLL + +VM Arguments: +jvm_args: -Dfile.encoding=UTF-8 +java_command: wpijavacv.Main +Launcher Type: SUN_STANDARD + +Environment Variables: +CLASSPATH=.;C:\Program Files\Java\jre6\lib\ext\QTJava.zip +PATH=C:\Program Files\PC Connectivity Solution\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files\ATI Technologies\ATI.ACE\Core-Static;c:\Program Files\Microsoft SQL Server\90\Tools\binn\;C:\Program Files\Common Files\Roxio Shared\9.0\DLLShared\;C:\Program Files\IVT Corporation\BlueSoleil\Mobile;C:\Windows\system32\Wbem;C:\Program Files\QuickTime\QTSystem\;C:\Program Files\Mercurial;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\OpenCV_2.2.0\bin +USERNAME=Greg +OS=Windows_NT +PROCESSOR_IDENTIFIER=x86 Family 6 Model 15 Stepping 13, GenuineIntel + + + +--------------- S Y S T E M --------------- + +OS: Windows Vista Build 6001 Service Pack 1 + +CPU:total 2 (2 cores per cpu, 1 threads per core) family 6 model 15 stepping 13, cmov, cx8, fxsr, mmx, sse, sse2, sse3, ssse3 + +Memory: 4k page, physical 3142056k(1590372k free), swap 6491236k(4598360k free) + +vm_info: Java HotSpot(TM) Client VM (19.0-b09) for windows-x86 JRE (1.6.0_23-b05), built on Nov 12 2010 15:00:43 by "java_re" with MS VC++ 7.1 (VS2003) + +time: Tue Jul 05 11:53:38 2011 +elapsed time: 1 seconds + diff --git a/WPIJavaCV/hs_err_pid5412.log b/WPIJavaCV/hs_err_pid5412.log new file mode 100644 index 0000000..a751540 --- /dev/null +++ b/WPIJavaCV/hs_err_pid5412.log @@ -0,0 +1,261 @@ +# +# A fatal error has been detected by the Java Runtime Environment: +# +# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x6202d33b, pid=5412, tid=5096 +# +# JRE version: 6.0_23-b05 +# Java VM: Java HotSpot(TM) Client VM (19.0-b09 mixed mode, sharing windows-x86 ) +# Problematic frame: +# C [opencv_core220.dll+0x4d33b] +# +# If you would like to submit a bug report, please visit: +# http://java.sun.com/webapps/bugreport/crash.jsp +# The crash happened outside the Java Virtual Machine in native code. +# See problematic frame for where to report the bug. +# + +--------------- T H R E A D --------------- + +Current thread (0x001f9000): JavaThread "main" [_thread_in_native, id=5096, stack(0x000c0000,0x00110000)] + +siginfo: ExceptionCode=0xc0000005, reading address 0x00000c36 + +Registers: +EAX=0x00000c2e, EBX=0x00000000, ECX=0x0010f79c, EDX=0x00000000 +ESP=0x0010f588, EBP=0x0010f67c, ESI=0x041386f0, EDI=0x00000000 +EIP=0x6202d33b, EFLAGS=0x00010206 + +Register to memory mapping: + +EAX=0x00000c2e +0x00000c2e is pointing to unknown location + +EBX=0x00000000 +0x00000000 is pointing to unknown location + +ECX=0x0010f79c +0x0010f79c is pointing into the stack for thread: 0x001f9000 +"main" prio=6 tid=0x001f9000 nid=0x13e8 runnable [0x0010f000] + java.lang.Thread.State: RUNNABLE + +EDX=0x00000000 +0x00000000 is pointing to unknown location + +ESP=0x0010f588 +0x0010f588 is pointing into the stack for thread: 0x001f9000 +"main" prio=6 tid=0x001f9000 nid=0x13e8 runnable [0x0010f000] + java.lang.Thread.State: RUNNABLE + +EBP=0x0010f67c +0x0010f67c is pointing into the stack for thread: 0x001f9000 +"main" prio=6 tid=0x001f9000 nid=0x13e8 runnable [0x0010f000] + java.lang.Thread.State: RUNNABLE + +ESI=0x041386f0 +0x041386f0 is pointing to unknown location + +EDI=0x00000000 +0x00000000 is pointing to unknown location + + +Top of Stack: (sp=0x0010f588) +0x0010f588: b5b382e7 0010f7f8 00000000 00000000 +0x0010f598: f50001f4 000001b0 041381f0 0010f79c +0x0010f5a8: 00000003 00000780 000001b0 00000174 +0x0010f5b8: 00000003 1d00e1fc 00000130 00000280 +0x0010f5c8: 0531c008 001d02fa 041381f0 00000003 +0x0010f5d8: 000001b0 04133020 00000000 00000000 +0x0010f5e8: 00000280 04132fd0 00000000 04138268 +0x0010f5f8: 051d0048 815a4bd0 005aa3f9 00000000 + +Instructions: (pc=0x6202d33b) +0x6202d32b: 00 00 89 7d c8 c6 45 b8 00 8b 46 0c 3b c7 74 03 +0x6202d33b: 8b 78 08 8b 5e 04 33 c0 3b d8 74 57 8b 56 0c 8d + + +Stack: [0x000c0000,0x00110000], sp=0x0010f588, free space=317k +Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) +C [opencv_core220.dll+0x4d33b] +C [opencv_core220.dll+0x4d507] +C [jniopencv_core9213342096841979925.dll+0x2051c] +j com.googlecode.javacv.cpp.opencv_core.cvReleaseMemStorage(Lcom/googlecode/javacv/cpp/opencv_core$CvMemStorage;)V+0 +j com.googlecode.javacv.cpp.opencv_core$CvMemStorage$ReleaseDeallocator.deallocate()V+1 +j com.googlecode.javacpp.Pointer$DeallocatorReference.clear()V+8 +j com.googlecode.javacpp.Pointer.deallocator(Lcom/googlecode/javacpp/Pointer$Deallocator;)Lcom/googlecode/javacpp/Pointer;+22 +j com.googlecode.javacpp.Pointer.init(JJ)V+16 +v ~StubRoutines::call_stub +V [jvm.dll+0xf33c9] +V [jvm.dll+0x188a91] +V [jvm.dll+0xf344d] +V [jvm.dll+0xfd07f] +V [jvm.dll+0x102647] +C [jniopencv_core9213342096841979925.dll+0x13264] +j com.googlecode.javacv.cpp.opencv_core$CvSize.allocate()V+0 +j com.googlecode.javacv.cpp.opencv_core$CvSize.()V+5 +j com.googlecode.javacv.cpp.opencv_core.cvSize(II)Lcom/googlecode/javacv/cpp/opencv_core$CvSize;+4 +j com.googlecode.javacv.cpp.opencv_core$IplImage.create(IIII)Lcom/googlecode/javacv/cpp/opencv_core$IplImage;+2 +j com.googlecode.javacv.cpp.opencv_core$IplImage.createFrom(Ljava/awt/image/BufferedImage;)Lcom/googlecode/javacv/cpp/opencv_core$IplImage;+164 +j wpijavacv.WpiImage.(Ljava/awt/image/BufferedImage;)V+6 +j wpijavacv.WpiColorImage.(Ljava/awt/image/BufferedImage;)V+2 +j wpijavacv.WpiCamera.getImage()Lwpijavacv/WpiColorImage;+53 +j wpijavacv.Main.main([Ljava/lang/String;)V+30 +v ~StubRoutines::call_stub +V [jvm.dll+0xf33c9] +V [jvm.dll+0x188a91] +V [jvm.dll+0xf344d] +V [jvm.dll+0xfce3c] +V [jvm.dll+0x104a8d] +C [java.exe+0x2155] +C [java.exe+0x85b4] +C [kernel32.dll+0x44911] +C [ntdll.dll+0x3e1c6] +C [ntdll.dll+0x3e199] + +Java frames: (J=compiled Java code, j=interpreted, Vv=VM code) +j com.googlecode.javacv.cpp.opencv_core.cvReleaseMemStorage(Lcom/googlecode/javacv/cpp/opencv_core$CvMemStorage;)V+0 +j com.googlecode.javacv.cpp.opencv_core$CvMemStorage$ReleaseDeallocator.deallocate()V+1 +j com.googlecode.javacpp.Pointer$DeallocatorReference.clear()V+8 +j com.googlecode.javacpp.Pointer.deallocator(Lcom/googlecode/javacpp/Pointer$Deallocator;)Lcom/googlecode/javacpp/Pointer;+22 +j com.googlecode.javacpp.Pointer.init(JJ)V+16 +v ~StubRoutines::call_stub +j com.googlecode.javacv.cpp.opencv_core$CvSize.allocate()V+0 +j com.googlecode.javacv.cpp.opencv_core$CvSize.()V+5 +j com.googlecode.javacv.cpp.opencv_core.cvSize(II)Lcom/googlecode/javacv/cpp/opencv_core$CvSize;+4 +j com.googlecode.javacv.cpp.opencv_core$IplImage.create(IIII)Lcom/googlecode/javacv/cpp/opencv_core$IplImage;+2 +j com.googlecode.javacv.cpp.opencv_core$IplImage.createFrom(Ljava/awt/image/BufferedImage;)Lcom/googlecode/javacv/cpp/opencv_core$IplImage;+164 +j wpijavacv.WpiImage.(Ljava/awt/image/BufferedImage;)V+6 +j wpijavacv.WpiColorImage.(Ljava/awt/image/BufferedImage;)V+2 +j wpijavacv.WpiCamera.getImage()Lwpijavacv/WpiColorImage;+53 +j wpijavacv.Main.main([Ljava/lang/String;)V+30 +v ~StubRoutines::call_stub + +--------------- P R O C E S S --------------- + +Java Threads: ( => current thread ) + 0x03f2e800 JavaThread "Java2D Disposer" daemon [_thread_blocked, id=5604, stack(0x040e0000,0x04130000)] + 0x01b5c400 JavaThread "Thread-0" [_thread_in_native, id=3592, stack(0x03e80000,0x03ed0000)] + 0x01b21c00 JavaThread "Low Memory Detector" daemon [_thread_blocked, id=1920, stack(0x03de0000,0x03e30000)] + 0x01b1b000 JavaThread "CompilerThread0" daemon [_thread_blocked, id=2256, stack(0x03d90000,0x03de0000)] + 0x01b18000 JavaThread "Attach Listener" daemon [_thread_blocked, id=4736, stack(0x03d40000,0x03d90000)] + 0x01b14c00 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=1544, stack(0x03cf0000,0x03d40000)] + 0x01ad7000 JavaThread "Finalizer" daemon [_thread_blocked, id=4592, stack(0x03ca0000,0x03cf0000)] + 0x01ad1c00 JavaThread "Reference Handler" daemon [_thread_blocked, id=5312, stack(0x03c50000,0x03ca0000)] +=>0x001f9000 JavaThread "main" [_thread_in_native, id=5096, stack(0x000c0000,0x00110000)] + +Other Threads: + 0x01ad0800 VMThread [stack: 0x00e10000,0x00e60000] [id=4664] + 0x01b33400 WatcherThread [stack: 0x03e30000,0x03e80000] [id=5388] + +VM state:not at safepoint (normal execution) + +VM Mutex/Monitor currently owned by a thread: None + +Heap + def new generation total 4992K, used 99K [0x23af0000, 0x24050000, 0x29040000) + eden space 4480K, 2% used [0x23af0000, 0x23b08f60, 0x23f50000) + from space 512K, 0% used [0x23f50000, 0x23f50000, 0x23fd0000) + to space 512K, 0% used [0x23fd0000, 0x23fd0000, 0x24050000) + tenured generation total 10944K, used 3114K [0x29040000, 0x29af0000, 0x33af0000) + the space 10944K, 28% used [0x29040000, 0x2934aa80, 0x2934ac00, 0x29af0000) + compacting perm gen total 12288K, used 1545K [0x33af0000, 0x346f0000, 0x37af0000) + the space 12288K, 12% used [0x33af0000, 0x33c725a8, 0x33c72600, 0x346f0000) + ro space 10240K, 54% used [0x37af0000, 0x3806c6b0, 0x3806c800, 0x384f0000) + rw space 12288K, 55% used [0x384f0000, 0x38b901f0, 0x38b90200, 0x390f0000) + +Dynamic libraries: +0x00400000 - 0x00424000 C:\Program Files\Java\jdk1.6.0_23\bin\java.exe +0x76f00000 - 0x77028000 C:\Windows\system32\ntdll.dll +0x75e90000 - 0x75f6b000 C:\Windows\system32\kernel32.dll +0x76290000 - 0x76356000 C:\Windows\system32\ADVAPI32.dll +0x75860000 - 0x75922000 C:\Windows\system32\RPCRT4.dll +0x7c340000 - 0x7c396000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\msvcr71.dll +0x6d8a0000 - 0x6db4c000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\client\jvm.dll +0x761f0000 - 0x7628d000 C:\Windows\system32\USER32.dll +0x75d70000 - 0x75dbb000 C:\Windows\system32\GDI32.dll +0x73bd0000 - 0x73c02000 C:\Windows\system32\WINMM.dll +0x75ae0000 - 0x75b8a000 C:\Windows\system32\msvcrt.dll +0x75f70000 - 0x760b4000 C:\Windows\system32\ole32.dll +0x757d0000 - 0x7585d000 C:\Windows\system32\OLEAUT32.dll +0x746f0000 - 0x74729000 C:\Windows\system32\OLEACC.dll +0x75d50000 - 0x75d6e000 C:\Windows\system32\IMM32.DLL +0x75dc0000 - 0x75e88000 C:\Windows\system32\MSCTF.dll +0x75570000 - 0x7559c000 C:\Windows\system32\apphelp.dll +0x77030000 - 0x77039000 C:\Windows\system32\LPK.DLL +0x75a10000 - 0x75a8d000 C:\Windows\system32\USP10.dll +0x6d850000 - 0x6d85c000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\verify.dll +0x6d3d0000 - 0x6d3ef000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\java.dll +0x6d330000 - 0x6d338000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\hpi.dll +0x756d0000 - 0x756d7000 C:\Windows\system32\PSAPI.DLL +0x6d890000 - 0x6d89f000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\zip.dll +0x6d6b0000 - 0x6d6c3000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\net.dll +0x75b90000 - 0x75bbd000 C:\Windows\system32\WS2_32.dll +0x77050000 - 0x77056000 C:\Windows\system32\NSI.dll +0x74d90000 - 0x74dcb000 C:\Windows\system32\mswsock.dll +0x74df0000 - 0x74df5000 C:\Windows\System32\wship6.dll +0x74a50000 - 0x74a55000 C:\Windows\System32\wshtcpip.dll +0x74af0000 - 0x74b2b000 C:\Windows\system32\rsaenh.dll +0x75620000 - 0x7563e000 C:\Windows\system32\USERENV.dll +0x75600000 - 0x75614000 C:\Windows\system32\Secur32.dll +0x74120000 - 0x7412f000 C:\Windows\system32\NLAapi.dll +0x74fd0000 - 0x74fe9000 C:\Windows\system32\IPHLPAPI.DLL +0x74f90000 - 0x74fc5000 C:\Windows\system32\dhcpcsvc.DLL +0x75220000 - 0x7524c000 C:\Windows\system32\DNSAPI.dll +0x74f80000 - 0x74f87000 C:\Windows\system32\WINNSI.DLL +0x74f50000 - 0x74f71000 C:\Windows\system32\dhcpcsvc6.DLL +0x70df0000 - 0x70dff000 C:\Windows\system32\napinsp.dll +0x70ab0000 - 0x70ac2000 C:\Windows\system32\pnrpnsp.dll +0x70de0000 - 0x70de8000 C:\Windows\System32\winrnr.dll +0x770e0000 - 0x7712a000 C:\Windows\system32\WLDAP32.dll +0x70aa0000 - 0x70aac000 C:\Windows\system32\wshbth.dll +0x75bc0000 - 0x75d4a000 C:\Windows\system32\SETUPAPI.dll +0x71c50000 - 0x71c56000 C:\Windows\system32\rasadhlp.dll +0x671f0000 - 0x672ae000 C:\Windows\System32\msvcr100.dll +0x6f890000 - 0x6f8f9000 C:\Windows\System32\msvcp100.dll +0x61fe0000 - 0x621d0000 C:\Program Files\OpenCV_2.2.0\bin\opencv_core220.dll +0x6d0b0000 - 0x6d1fa000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\awt.dll +0x746a0000 - 0x746e2000 C:\Windows\system32\WINSPOOL.DRV +0x744c0000 - 0x7465e000 C:\Windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.6001.18523_none_5cdd65e20837faf2\COMCTL32.dll +0x75770000 - 0x757c8000 C:\Windows\system32\SHLWAPI.dll +0x6d4f0000 - 0x6d515000 C:\Program Files\Java\jdk1.6.0_23\jre\bin\jpeg.dll +0x10000000 - 0x10062000 C:\Users\Greg\AppData\Local\Temp\jniopencv_core9213342096841979925.dll +0x62cf0000 - 0x62eb7000 C:\Program Files\OpenCV_2.2.0\bin\opencv_imgproc220.dll +0x04140000 - 0x04172000 C:\Users\Greg\AppData\Local\Temp\jniopencv_imgproc4176629997143096416.dll +0x5ed90000 - 0x5f7b6000 C:\Program Files\OpenCV_2.2.0\bin\opencv_ffmpeg220.dll +0x65290000 - 0x65358000 C:\Program Files\OpenCV_2.2.0\bin\opencv_highgui220.dll +0x70190000 - 0x701a9000 C:\Windows\system32\AVIFIL32.dll +0x73740000 - 0x73754000 C:\Windows\system32\MSACM32.dll +0x73930000 - 0x73953000 C:\Windows\system32\MSVFW32.dll +0x76360000 - 0x76e70000 C:\Windows\system32\SHELL32.dll +0x70170000 - 0x70183000 C:\Windows\system32\AVICAP32.dll +0x74e00000 - 0x74e08000 C:\Windows\system32\VERSION.dll +0x04300000 - 0x0431d000 C:\Users\Greg\AppData\Local\Temp\jniopencv_highgui6399461832457287126.dll +0x747c0000 - 0x747ff000 C:\Windows\system32\uxtheme.dll +0x76e70000 - 0x76ef4000 C:\Windows\system32\CLBCatQ.DLL + +VM Arguments: +jvm_args: -Dfile.encoding=UTF-8 +java_command: wpijavacv.Main +Launcher Type: SUN_STANDARD + +Environment Variables: +CLASSPATH=.;C:\Program Files\Java\jre6\lib\ext\QTJava.zip +PATH=C:\Program Files\PC Connectivity Solution\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files\ATI Technologies\ATI.ACE\Core-Static;c:\Program Files\Microsoft SQL Server\90\Tools\binn\;C:\Program Files\Common Files\Roxio Shared\9.0\DLLShared\;C:\Program Files\IVT Corporation\BlueSoleil\Mobile;C:\Windows\system32\Wbem;C:\Program Files\QuickTime\QTSystem\;C:\Program Files\Mercurial;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\OpenCV_2.2.0\bin +USERNAME=Greg +OS=Windows_NT +PROCESSOR_IDENTIFIER=x86 Family 6 Model 15 Stepping 13, GenuineIntel + + + +--------------- S Y S T E M --------------- + +OS: Windows Vista Build 6001 Service Pack 1 + +CPU:total 2 (2 cores per cpu, 1 threads per core) family 6 model 15 stepping 13, cmov, cx8, fxsr, mmx, sse, sse2, sse3, ssse3 + +Memory: 4k page, physical 3142056k(1573244k free), swap 6491236k(4568416k free) + +vm_info: Java HotSpot(TM) Client VM (19.0-b09) for windows-x86 JRE (1.6.0_23-b05), built on Nov 12 2010 15:00:43 by "java_re" with MS VC++ 7.1 (VS2003) + +time: Tue Jul 05 12:02:14 2011 +elapsed time: 0 seconds + diff --git a/WPIJavaCV/hs_err_pid5892.log b/WPIJavaCV/hs_err_pid5892.log new file mode 100644 index 0000000..db03044 --- /dev/null +++ b/WPIJavaCV/hs_err_pid5892.log @@ -0,0 +1,30 @@ +# +# A fatal error has been detected by the Java Runtime Environment: +# +# EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x62884f2a, pid=5892, tid=4092 +# +# JRE version: 6.0_23-b05 +# Java VM: Java HotSpot(TM) Client VM (19.0-b09 mixed mode, sharing windows-x86 ) +# Problematic frame: +# C [opencv_core220.dll+0x54f2a] +# +# If you would like to submit a bug report, please visit: +# http://java.sun.com/webapps/bugreport/crash.jsp +# The crash happened outside the Java Virtual Machine in native code. +# See problematic frame for where to report the bug. +# + +--------------- T H R E A D --------------- + +Current thread (0x000d9000): JavaThread "main" [_thread_in_native, id=4092, stack(0x000e0000,0x00130000)] + +siginfo: ExceptionCode=0xc00000fd, ExceptionInformation=0x00000001 0x000e0ffc + +Registers: +EAX=0x00000000, EBX=0x00000000, ECX=0x000e1014, EDX=0x00000000 +ESP=0x000e1000, EBP=0x000e1108, ESI=0x04547c30, EDI=0x04547c30 +EIP=0x62884f2a, EFLAGS=0x00010206 + +Register to memory mapping: + +EAX=0x00000000 diff --git a/WPIJavaCV/lib/javacpp.jar b/WPIJavaCV/lib/javacpp.jar new file mode 100644 index 0000000..31fefca Binary files /dev/null and b/WPIJavaCV/lib/javacpp.jar differ diff --git a/WPIJavaCV/lib/javacv-windows-x86.jar b/WPIJavaCV/lib/javacv-windows-x86.jar new file mode 100644 index 0000000..ed77f93 Binary files /dev/null and b/WPIJavaCV/lib/javacv-windows-x86.jar differ diff --git a/WPIJavaCV/lib/javacv.jar b/WPIJavaCV/lib/javacv.jar new file mode 100644 index 0000000..06d337a Binary files /dev/null and b/WPIJavaCV/lib/javacv.jar differ diff --git a/WPIJavaCV/manifest.mf b/WPIJavaCV/manifest.mf new file mode 100644 index 0000000..1574df4 --- /dev/null +++ b/WPIJavaCV/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/WPIJavaCV/nbproject/build-impl.xml b/WPIJavaCV/nbproject/build-impl.xml new file mode 100644 index 0000000..decaf9f --- /dev/null +++ b/WPIJavaCV/nbproject/build-impl.xml @@ -0,0 +1,1400 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WPIJavaCV/nbproject/genfiles.properties b/WPIJavaCV/nbproject/genfiles.properties new file mode 100644 index 0000000..3d0effa --- /dev/null +++ b/WPIJavaCV/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=66e2c65a +build.xml.script.CRC32=c45949d1 +build.xml.stylesheet.CRC32=28e38971@1.38.3.45 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=66e2c65a +nbproject/build-impl.xml.script.CRC32=e3a86251 +nbproject/build-impl.xml.stylesheet.CRC32=6ddba6b6@1.53.1.46 diff --git a/WPIJavaCV/nbproject/private/config.properties b/WPIJavaCV/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/WPIJavaCV/nbproject/private/private.properties b/WPIJavaCV/nbproject/private/private.properties new file mode 100644 index 0000000..bb21662 --- /dev/null +++ b/WPIJavaCV/nbproject/private/private.properties @@ -0,0 +1,6 @@ +compile.on.save=false +do.depend=false +do.jar=true +javac.debug=true +javadoc.preview=true +user.properties.file=C:\\Users\\Mitchell Wills\\AppData\\Roaming\\NetBeans\\7.2\\build.properties diff --git a/WPIJavaCV/nbproject/project.properties b/WPIJavaCV/nbproject/project.properties new file mode 100644 index 0000000..428a3b2 --- /dev/null +++ b/WPIJavaCV/nbproject/project.properties @@ -0,0 +1,87 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=WPIJavaCV +application.vendor=Greg +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/WPIJavaCV.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.javacpp.jar=lib/javacpp.jar +file.reference.javacv-windows-x86.jar=lib/javacv-windows-x86.jar +file.reference.javacv.jar=lib/javacv.jar +includes=** +jar.archive.disabled=${jnlp.enabled} +jar.compress=false +jar.index=${jnlp.enabled} +javac.classpath=\ + ${file.reference.javacpp.jar}:\ + ${file.reference.javacv-windows-x86.jar}:\ + ${file.reference.javacv.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.5 +javac.target=1.5 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${libs.junit.classpath}:\ + ${libs.junit_4.classpath} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jnlp.codebase.type=no.codebase +jnlp.descriptor=application +jnlp.enabled=false +jnlp.mixed.code=defaut +jnlp.offline-allowed=false +jnlp.signed=false +main.class=test.Main +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/WPIJavaCV/nbproject/project.xml b/WPIJavaCV/nbproject/project.xml new file mode 100644 index 0000000..b878756 --- /dev/null +++ b/WPIJavaCV/nbproject/project.xml @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + WPIJavaCV + + + + + + + + + + diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIBinaryImage.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIBinaryImage.java new file mode 100644 index 0000000..1b862f9 --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIBinaryImage.java @@ -0,0 +1,277 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.wpijavacv; + +import java.util.ArrayList; +import static com.googlecode.javacv.cpp.opencv_imgproc.*; +import static com.googlecode.javacv.cpp.opencv_core.*; + +/** + * A {@link WPIBinaryImage} object is a black and white image. + * + * @author Greg Granito + */ +public class WPIBinaryImage extends WPIImage { + + /** + * Instantiates a {@link WPIBinaryImage} from the given {@link IplImage}. + * The resulting image will be directly wrapped around the given image, and so any modifications + * to the {@link WPIBinaryImage} will reflect on the given image. + * @param image the image to wrap + */ + protected WPIBinaryImage(IplImage image) { + super(image); + } + + /** + * Returns the result of "and-ing" the given image with the calling image. Every pixel in the image + * will be "and-ed" with the corresponding pixel in the other image to form a new image. The result + * of and-ing two pixels is pictured in the table below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
WhiteBlack
WhiteWhiteBlack
BlackBlackBlack
+ * + * @param image2 a second {@link WPIBinaryImage} + * @return an image that is the pixel-wise and of the two images. + */ + public WPIBinaryImage getAnd(WPIBinaryImage image2) { + validateDisposed(); + + IplImage result = IplImage.create(image.cvSize(), image.depth(), 1); + cvAnd(image, image2.image, result, null); + return new WPIBinaryImage(result); + } + + /** + * "and-s" this image with the given image. Every pixel in this image + * will be "and-ed" with the corresponding pixel in the other image. The result + * of and-ing two pixels is pictured in the table below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
WhiteBlack
WhiteWhiteBlack
BlackBlackBlack
+ * + * This will modify the image. If you do now wish to modify the image, use {@link WPIBinaryImage#getAnd(wpijavacv.WPIBinaryImage) getAnd(...)} + * instead. + * + * @param image2 a second {@link WPIBinaryImage} + */ + public void and(WPIBinaryImage image2) { + validateDisposed(); + + cvAnd(image, image2.image, image, null); + } + + /** + * Returns the result of "or-ing" the given image with the calling image. Every pixel in the image + * will be "or-ed" with the corresponding pixel in the other image to form a new image. The result + * of or-ing two pixels is pictured in the table below. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
WhiteBlack
WhiteWhiteWhite
BlackWhiteBlack
+ * + * @param image2 a second {@link WPIBinaryImage} + * @return an image that is the pixel-wise or of the two images. + */ + public WPIBinaryImage getOr(WPIBinaryImage image2) { + validateDisposed(); + + IplImage result = IplImage.create(image.cvSize(), image.depth(), 1); + cvOr(image, image2.image, result, null); + return new WPIBinaryImage(result); + } + + /** + * "or-s" this image with the given image. Every pixel in this image + * will be "or-ed" with the corresponding pixel in the other image. The result + * of or-ing two pixels is pictured in the table below. This will modify the image. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
WhiteBlack
WhiteWhiteWhite
BlackWhiteBlack
+ * + * This will modify the image. If you do now wish to modify the image, use {@link WPIBinaryImage#getOr(wpijavacv.WPIBinaryImage) getOr(...)} + * instead. + * + * @param image2 a second {@link WPIBinaryImage} + */ + public void or(WPIBinaryImage image2) { + validateDisposed(); + + cvOr(image, image2.image, image, null); + } + + /** + * Inverts this image. Everything which was white will now be black and + * everything which was black will now be white. + * This will modify the image. If you do now wish to modify the image, use {@link WPIBinaryImage#getInverse() getInverse()} instead. + * instead. + */ + public void invert() { + validateDisposed(); + + cvInv(image, image); + } + + /** + * Returns an image that is the inverse of the calling one. In other words, it returns a copy of this image except + * that the copy will replace every black pixel with white one and every white pixel with a black one. + * @return a new {@link WPIBinaryImage} that is the inverse of the image + */ + public WPIBinaryImage getInverse() { + validateDisposed(); + + IplImage result = IplImage.create(image.cvSize(), image.depth(), 1); + cvInv(image, result); + return new WPIBinaryImage(result); + } + + /** + * Dilates the image the specified number of times. Every time the image is dilated, every pixel which borders a white pixel + * will itself become white. The effect is that white pixels in the image begin to spread. + * This is useful if you wish to remove small "holes" from the image. + * + * This will modify the image. If you do now wish to modify the image, use {@link WPIBinaryImage#getDilated(int) getDilated(...)} instead. + * instead. + * + * @param iterations the number of times to perform the dilation. For example, if this is 3, then every pixel within three + * spaces of a white pixel will become white. + */ + public void dilate(int iterations) { + validateDisposed(); + + cvDilate(image, image, null, iterations); + } + + /** + * Returns an image that is the result of dilating the specified number of times. Every time an image is dilated, every pixel which borders a white pixel + * will itself become white. The effect is that white pixels in the image begin to spread. + * This is useful if you wish to remove small "holes" from the image. + * + * @param iterations the number of times to perform the dilation. For example, if this is 3, then every pixel within three + * @return returns a new {@link WPIBinaryImage} in which the surrounding pixels to each white pixel + * are changed to white + */ + public WPIBinaryImage getDilated(int iterations) { + validateDisposed(); + + IplImage result = IplImage.create(image.cvSize(), image.depth(), 1); + cvDilate(image, result, null, iterations); + return new WPIBinaryImage(result); + } + + /** + * Erodes the image the specified number of times. Every time the image is eroded, every pixel which borders a black pixel + * will itself become black. The effect is that black pixels in the image begin to spread. + * This is useful if you wish to shrink or remove white blobs from an image. + * + * This will modify the image. If you do now wish to modify the image, use {@link WPIBinaryImage#getEroded(int) getEroded(...)} instead. + * instead. + * + * @param iterations the number of times to perform the erosion. For example, if this is 3, then every pixel within three + * spaces of a black pixel will become black. + */ + public void erode(int iterations) { + validateDisposed(); + + cvErode(image, image, null, iterations); + } + + /** + * Returns an image that is the result of eroding the specified number of times. Every time the image is eroded, every pixel which borders a black pixel + * will itself become black. The effect is that black pixels in the image begin to spread. + * This is useful if you wish to shrink or remove white blobs from an image. + * + * @param iterations the number of times to perform the erosion. For example, if this is 3, then every pixel within three + * spaces of a black pixel will become black. + * @return a new {@link WPIBinaryImage} in which the surrounding pixels to each black pixel + * are changed to black + */ + public WPIBinaryImage getEroded(int iterations) { + validateDisposed(); + + IplImage result = IplImage.create(image.cvSize(), image.depth(), 1); + cvErode(image, result, null, iterations); + return new WPIBinaryImage(result); + } + + /** + * Finds all the "contours" in the image. A contour is basically an outline. + * @return an array of {@link WpiContour} that is all of the edges in the image + */ + public WPIContour[] findContours() { + validateDisposed(); + + IplImage tempImage = IplImage.create(image.cvSize(), image.depth(), 1); + + cvCopy(image, tempImage); + + final CvMemStorage storage = CvMemStorage.create(); + WPIMemoryPool pool = new WPIMemoryPool() { + + @Override + protected void disposed() { + cvClearMemStorage(storage); + storage.release(); + } + }; + + CvSeq contours = new CvSeq(); + cvFindContours(tempImage, storage, contours, 256, CV_RETR_LIST, CV_CHAIN_APPROX_TC89_KCOS); + ArrayList results = new ArrayList(); + while (!isNull(contours)) { + WPIContour contour = new WPIContour(cvCloneSeq(contours, storage)); + results.add(contour); + pool.addToPool(contour); + contours = contours.h_next(); + } + + tempImage.release(); + WPIContour[] array = new WPIContour[results.size()]; + return results.toArray(array); + } +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPICamera.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPICamera.java new file mode 100644 index 0000000..ed9752f --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPICamera.java @@ -0,0 +1,26 @@ +package edu.wpi.first.wpijavacv; + +/** + * A class used to gather images from the robot's camera. + * @author Joe Grinstead and Greg Granito + */ +public class WPICamera extends WPIFFmpegVideo { + + private static final int DEFAULT_ENDING_IP = 11; + + public WPICamera(String loginName, String password, int team) { + this(loginName + ":" + password + "@10." + (team / 100) + "." + (team % 100) + "." + DEFAULT_ENDING_IP); + } + + public WPICamera(int team) { + this("10." + (team / 100) + "." + (team % 100) + "." + DEFAULT_ENDING_IP); + } + + public WPICamera(String loginName, String password, String ip) { + this(loginName + ":" + password + "@" + ip); + } + + public WPICamera(String ip) { + super("http://" + ip + "/mjpg/video.mjpg"); + } +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColor.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColor.java new file mode 100644 index 0000000..50f585b --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColor.java @@ -0,0 +1,64 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package edu.wpi.first.wpijavacv; + +import java.awt.Color; +import static com.googlecode.javacv.cpp.opencv_core.*; +/** + * A class of colors used for drawing function + * @author Greg Granito + */ +public class WPIColor { + public static final WPIColor BLACK = new WPIColor(CvScalar.BLACK, Color.BLACK); + public static final WPIColor BLUE = new WPIColor(CvScalar.BLUE, Color.BLUE); + public static final WPIColor CYAN = new WPIColor(CvScalar.CYAN, Color.CYAN); + public static final WPIColor GRAY = new WPIColor(CvScalar.GRAY, Color.GRAY); + public static final WPIColor GREEN = new WPIColor(CvScalar.GREEN, Color.GREEN); + public static final WPIColor MAGENTA = new WPIColor(CvScalar.MAGENTA, Color.MAGENTA); + public static final WPIColor ONE = new WPIColor(CvScalar.ONE); + public static final WPIColor ONEHALF = new WPIColor(CvScalar.ONEHALF); + public static final WPIColor RED = new WPIColor(CvScalar.RED, Color.RED); + public static final WPIColor WHITE = new WPIColor(CvScalar.WHITE, Color.WHITE); + public static final WPIColor YELLOW = new WPIColor(CvScalar.YELLOW, Color.YELLOW); + public static final WPIColor ZERO = new WPIColor(CvScalar.ZERO); + + private final CvScalar scalar; + private Color color; + + WPIColor(CvScalar scalar) { + this.scalar = scalar; + } + + WPIColor(CvScalar scalar, Color color) { + this(scalar); + this.color = color; + } + + /** + * Creates a new WPIColor with the specified rgb values + * @param red red value, 0 - 255 + * @param green green value, 0 - 255 + * @param blue blue value, 0 - 255 + */ + public WPIColor(int red, int green, int blue){ + this(CV_RGB(red, green, blue)); + } + + public WPIColor(Color color) { + this(CV_RGB(color.getRed(), color.getGreen(), color.getBlue()), color); + } + + CvScalar toCvScalar(){ + return scalar; + } + + public Color toColor() { + if (color == null) { + color = new Color((int) scalar.red(), (int) scalar.green(), (int) scalar.blue()); + } + return color; + } +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColorImage.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColorImage.java new file mode 100644 index 0000000..b643efa --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIColorImage.java @@ -0,0 +1,158 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.wpijavacv; + +import java.awt.image.BufferedImage; +import static com.googlecode.javacv.cpp.opencv_core.*; + +/** + * A color image + * @author Greg Granito + */ +public class WPIColorImage extends WPIImage { + + private WPIGrayscaleImage red; + private WPIGrayscaleImage blue; + private WPIGrayscaleImage green; + + /** + * creates a WpiColorImage based on the BufferedImage source + * @param imageSrc the source image + */ + public WPIColorImage(BufferedImage imageSrc) { + super(imageSrc); + } + + WPIColorImage(IplImage imageSrc) { + super(imageSrc); + } + + /** + * Draws a WpiContour + * @param c the contour + * @param color the desired WPIColor + * @param thickness the thickness in pixels + */ + public void drawContour(WPIContour c, WPIColor color, int thickness) { + cvDrawContours(image, c.getCVSeq(), color.toCvScalar(), color.toCvScalar(), 100, thickness, 8); + } + + /** + * Draws all the WpiContours in an array + * @param c the contours + * @param color the desired WPIColor + * @param thickness the thickness in pixels + */ + public void drawContours(WPIContour[] c, WPIColor color, int thickness){ + for(WPIContour con:c){ + drawContour(con, color, thickness); + } + } + + /** + * Draws a line between the two specified WpiPoints + * @param p1 Point 1 + * @param p2 Point 2 + * @param color the desired WPIColor + * @param thickness the thickness in pixels + */ + public void drawLine(WPIPoint p1, WPIPoint p2, WPIColor color, int thickness){ + cvLine(image, p1.getCvPoint(), p2.getCvPoint(), color.toCvScalar(), thickness, 8, 0); + } + + /** + * Draws a WpiPolygon + * @param p the WpiPolygon + * @param color the desired WPIColor + * @param thickness the thickness in pixels + */ + public void drawPolygon(WPIPolygon p, WPIColor color, int thickness){ + cvDrawContours(image, p.getCVSeq(), color.toCvScalar(), color.toCvScalar(), 100, thickness, 8); + } + + /** + * Draws a WpiPoint + * @param p the WpiPoint + * @param color the desired WPIColor + * @param thickness the thickness in pixels + */ + public void drawPoint(WPIPoint p, WPIColor color, int thickness){ + cvDrawCircle(image,p.getCvPoint(), thickness, color.toCvScalar(), CV_FILLED, 8, 0); + } + /** + * Draws all the WpiPoints in an array + * @param p the WpiPoint array + * @param color the desired WPIColor + * @param thickness the thickness in pixels + */ + public void drawPoints(WPIPoint[] p, WPIColor color, int thickness){ + for(int i = 0; i< p.length; i++) + drawPoint(p[i], color, thickness); + } + /** + * Draws all the WpiPolygons in an array + * @param p the WpiPolygon array + * @param color the desired WPIColor + * @param thickness the thickness in pixels + */ + public void drawPolygons(WPIPolygon[] p, WPIColor color, int thickness){ + for(WPIPolygon polygon : p){ + if(polygon != null && !polygon.getCVSeq().isNull()) + drawPolygon(polygon, color, thickness); + } + } + /** + * Draws a rectangle + * @param x the top left corner x coord + * @param y the top left corner y coord + * @param width the width of the rectangle + * @param height the height of the rectangle + * @param color the desired WPIColor + * @param thickness the thickness in pixels + */ + public void drawRect(int x, int y, int width, int height, WPIColor color, int thickness){ + cvDrawRect(image, cvPoint(x, y),cvPoint(x+width, y+height), color.toCvScalar(), thickness, 8, 0); + } + + private void generateChannels() { + if (red == null) { + IplImage redChannel = IplImage.create(image.cvSize(), 8, 1); + IplImage greenChannel = IplImage.create(image.cvSize(), 8, 1); + IplImage blueChannel = IplImage.create(image.cvSize(), 8, 1); + cvSplit(image, blueChannel, greenChannel, redChannel, null); + red = new WPIGrayscaleImage(redChannel); + blue = new WPIGrayscaleImage(blueChannel); + green = new WPIGrayscaleImage(greenChannel); + } + } + + /** + * Gets the red channel from the color image + * @return a WpiGrayscaleImage that represents the red channel of the WPIColor image + */ + public WPIGrayscaleImage getRedChannel() { + generateChannels(); + return red; + } + + /** + * Gets the blue channel from the color image + * @return a WpiGrayscaleImage that represents the blue channel of the WPIColor image + */ + public WPIGrayscaleImage getBlueChannel() { + generateChannels(); + return blue; + } + + /** + * Gets the green channel from the color image + * @return a WpiGrayscaleImage that represents the green channel of the WPIColor image + */ + + public WPIGrayscaleImage getGreenChannel() { + generateChannels(); + return green; + } +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIContour.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIContour.java new file mode 100644 index 0000000..5cd9fc4 --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIContour.java @@ -0,0 +1,100 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package edu.wpi.first.wpijavacv; + +import static com.googlecode.javacv.cpp.opencv_core.*; +import static com.googlecode.javacv.cpp.opencv_imgproc.*; +/** + * This is a class that represents contours, it can only be obtained from + * the findContours() method of WpiBinaryImage + * @author Greg Granito + */ +public class WPIContour extends WPIDisposable { + + CvSeq contours; + CvRect rect; + + WPIContour(CvSeq contours) { + this.contours = contours; +// memStorage = contours.storage(); + + } + + CvSeq getCVSeq(){ + return contours; + } + + /** + * + * @return the height of the bounding rectangle of the contour + */ + public int getHeight(){ + if(rect == null || rect.isNull()) + rect = cvBoundingRect(contours, 1); + + return rect.height(); + } + + /** + * + * @return the width of the bounding rectangle of the contour + */ + public int getWidth(){ + if(rect == null || rect.isNull()) + rect = cvBoundingRect(contours, 1); + + return rect.width(); + } + + /** + * + * @return the x coord of the top left corner of the bounding rectangle + */ + public int getX(){ + if(rect == null || rect.isNull()) + rect = cvBoundingRect(contours, 1); + + return rect.x(); + } + + /** + * + * @return the y coord of the top left corner of the bounding rectangle + */ + public int getY(){ + if(rect == null || rect.isNull()) + rect = cvBoundingRect(contours, 1); + + return rect.y(); + } + + /** + * + * @param percentAccuracy the percentage the perimeter of the polygon can be off + * the perimeter of the contour. The higher the value, the fewer points the polygon + * will have. A value of 4-5 is recommended. + * @return the approximated WpiPolygon + */ + public synchronized WPIPolygon approxPolygon(double percentAccuracy){ + WPIPolygon polygon = new WPIPolygon(cvApproxPoly(contours, contours.header_size(), contours.storage(), CV_POLY_APPROX_DP, percentAccuracy, 0)); + if (getPool() != null) { + getPool().addToPool(polygon); + } + return polygon; + } + + /** + * + * @return the perimeter of the contour + */ + public int getlength(){ + return (int) cvContourPerimeter(contours); + } + + public void disposed() { + contours.deallocate(); + } +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIDisposable.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIDisposable.java new file mode 100644 index 0000000..6d4b23b --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIDisposable.java @@ -0,0 +1,119 @@ +package edu.wpi.first.wpijavacv; + +import com.googlecode.javacpp.Pointer; + +/** + * This class is an abstract class defining disposable elements. + * It is the superclass of most things which links directly to the javacv core. + * + * @author Joe Grinstead + */ +public abstract class WPIDisposable { + + /** The memory pool to report to when disposed, or null if there is none */ + private WPIMemoryPool pool = null; + + /** Whether or not this is disposed */ + private boolean disposed = false; + + /** + * Sets the {@link WPIMemoryPool} that this disposable is linked to. + * @param pool the pool to link to (null is allowed) + */ + protected void setPool(WPIMemoryPool pool) { + this.pool = pool; + } + + protected WPIMemoryPool getPool() { + return pool; + } + + /** + * Disposes this object. This may be called multiple times. + * + * Programmers should call this when they no longer need an object. However, even if they don't, this will be called + * when this object is collected by the garbage collector. + */ + public void dispose() { + if (!disposed) { + disposed = true; + disposed(); + if (pool != null) { + pool.removeFromPool(this); + } + } + } + + /** + * This is called when {@link WPIDisposable#dispose() dispose()} is called for the first time. + * Subclasses should clear out whatever internal resources they are using. + */ + protected abstract void disposed(); + + /** + * Returns whether or not this object is disposed. + * @return whether or not this object is disposed + * + * @see WPIDisposable#dispose() dispose() + */ + public boolean isDisposed() { + return disposed; + } + + /** + * Checks if this {@link WPIDisposable} has already been disposed. If it has, + * then it will throw a {@link DisposedException} with a default message. + */ + protected void validateDisposed() { + if (disposed) { + throw new DisposedException(this + " has been disposed"); + } + } + + /** + * Checks if this {@link WPIDisposable} has already been disposed. If it has, + * then it will throw a {@link DisposedException} with the given message. + * @param message the message to give the exception + */ + protected void validateDisposed(String message) { + if (disposed) { + throw new DisposedException(message); + } + } + + /** + * Returns whether or not the given pointer is null in either the java sense or the javacv sense. + * @param pointer the pointer + * @return whether it is null + */ + protected static boolean isNull(Pointer pointer) { + return pointer == null || pointer.isNull(); + } + + /** + * Attempts to free (in the c sense) the given pointer. Does nothing if given null. + * @param pointer the pointer to free + */ + protected static void free(Pointer pointer) { + if (pointer != null && !pointer.isNull()) { + pointer.deallocate(); + } + } + + /** + * An exception to be thrown if an element has already been disposed and the user attempts to + * perform an operation on it. + */ + public static class DisposedException extends RuntimeException { + + public DisposedException(String message) { + super(message); + } + } + + @Override + protected void finalize() throws Throwable { + dispose(); + super.finalize(); + } +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIFFmpegVideo.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIFFmpegVideo.java new file mode 100644 index 0000000..e52bced --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIFFmpegVideo.java @@ -0,0 +1,151 @@ +package edu.wpi.first.wpijavacv; + +import com.googlecode.javacv.FFmpegFrameGrabber; +import static com.googlecode.javacv.cpp.opencv_core.*; +import com.googlecode.javacv.cpp.opencv_core.IplImage; + +/** + * A class used to gather images from the robot's camera. + * @author Joe Grinstead and Greg Granito + */ +public class WPIFFmpegVideo extends WPIDisposable { + + private FFmpegFrameGrabber grabber; + private IplImage image; + private boolean readImage = true; + private boolean badConnection = false; + private final Object imageLock = new Object(); + private final Object grabberLock = new Object(); + + public WPIFFmpegVideo(final String path) { + new Thread() { + + @Override + public void run() { + grabber = new FFmpegFrameGrabber(path); + grabber.setFrameRate(1.0); + try { + grabber.start(); + + while (!isDisposed()) { + try { + IplImage newest; + synchronized (grabberLock) { + if (isDisposed()) { + return; + } + newest = grabber.grab(); + } + if (isNull(newest)) { + synchronized (imageLock) { + badConnection = true; + imageLock.notify(); + } + return; + } else { + synchronized (imageLock) { + if (image == null) { + image = cvCreateImage(newest.cvSize(), newest.depth(), newest.nChannels()); + } + cvCopy(newest, image); + readImage = false; + badConnection = false; + imageLock.notify(); + } + } + } catch (Exception ex) { + synchronized (imageLock) { + badConnection = true; + imageLock.notify(); + } + ex.printStackTrace(); + return; + } + try { + Thread.sleep(20); + } catch (InterruptedException ex) { + } + } + } catch (Exception ex) { + synchronized (imageLock) { + badConnection = true; + imageLock.notify(); + } + ex.printStackTrace(); + } + } + }.start(); + } + + public WPIImage getImage() throws BadConnectionException { + validateDisposed(); + + synchronized (imageLock) { + if (badConnection) { + throw new BadConnectionException(); + } else if (image == null) { + return null; + } else if (image.nChannels() == 1) { + return new WPIGrayscaleImage(image.clone()); + } else { + assert image.nChannels() == 3; + return new WPIColorImage(image.clone()); + } + } + } + + public WPIImage getNewImage(double timeout) throws BadConnectionException { + validateDisposed(); + + synchronized (imageLock) { + readImage = true; + while (readImage && !badConnection) { + try { + badConnection = true; + imageLock.wait((long) (timeout * 1000)); + } catch (InterruptedException ex) { + } + } + readImage = true; + + if (badConnection) { + throw new BadConnectionException(); + } else if (image.nChannels() == 1) { + return new WPIGrayscaleImage(image.clone()); + } else { + assert image.nChannels() == 3; + return new WPIColorImage(image.clone()); + } + } + } + + public WPIImage getNewImage() throws BadConnectionException { + return getNewImage(0); + } + + @Override + protected void disposed() { + try { + synchronized (imageLock) { + if (!isNull(image)) { + image.release(); + } + image = null; + } + } catch (Exception ex) { + } + } + + /** + * An exception that occurs when the camera can not be reached. + * @author Greg Granito + */ + public static class BadConnectionException extends Exception { + } + + @Override + protected void finalize() throws Throwable { + grabber.stop(); + super.finalize(); + } +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIGrayscaleImage.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIGrayscaleImage.java new file mode 100644 index 0000000..facbb09 --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIGrayscaleImage.java @@ -0,0 +1,53 @@ +/* + * To change this template, choose Tools | Templates + * getAnd open the template in the editor. + */ +package edu.wpi.first.wpijavacv; + +import static com.googlecode.javacv.cpp.opencv_core.*; +import static com.googlecode.javacv.cpp.opencv_imgproc.*; + +/** + * A grayscale image + * + * @author Greg Granito + */ +public class WPIGrayscaleImage extends WPIImage { + + WPIGrayscaleImage(IplImage imageSrc) { + super(imageSrc); + } + + /** + * Returns a black and white image where every pixel that is higher (in the 0-255 scale) than the given threshold is white, + * and everything below is black. + * @param threshold a value 0-255. if a pixel has a value below the theshold, it becomes black + * if the pixel value is above or equal to the threshold, the pixel becomes white + * @return a new {@link WPIBinaryImage} that represents the threshold + */ + public WPIBinaryImage getThreshold(int threshold) { + validateDisposed(); + + IplImage bin = IplImage.create(image.cvSize(), 8, 1); + cvThreshold(image, bin, threshold, 255, CV_THRESH_BINARY); + return new WPIBinaryImage(bin); + } + + /** + * Returns a black and white image where every pixel that is higher (in the 0-255 scale) than the given threshold is black, + * and everything below is white. + * + * In other words, this will return the inverted image of {@link WpiGrayscaleImage#getThreshold(int) getThreshold(...)} but is + * more efficient than calling {@link WpiGrayscaleImage#getThreshold(int) getThreshold(...)}.{@link WPIBinaryImage#getInverse() getInverse()} + * @param threshold a value 0-255. if a pixel has a value below the theshold, it becomes black + * if the pixel value is above or equal to the threshold, the pixel becomes white + * @return a new {@link WPIBinaryImage} that represents the threshold + */ + public WPIBinaryImage getThresholdInverted(int threshold) { + validateDisposed(); + + IplImage bin = IplImage.create(image.cvSize(), 8, 1); + cvThreshold(image, bin, threshold, 255, CV_THRESH_BINARY_INV); + return new WPIBinaryImage(bin); + } +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIImage.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIImage.java new file mode 100644 index 0000000..609aaaa --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIImage.java @@ -0,0 +1,76 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.wpijavacv; + +import static com.googlecode.javacv.cpp.opencv_core.*; +import java.awt.image.BufferedImage; + +/** + * This class is the superclass of all images. + * + * @author Greg Granito + */ +public class WPIImage extends WPIDisposable { + + /** The underlying {@link IplImage} */ + protected IplImage image; + + /** + * Instantiates a {@link WPIImage} from a {@link BufferedImage}. + * Useful for interacting with swing. + * This will not keep a reference to the given image, so any modification to this + * {@link WPIImage} will not change the given image. + * @param image the image to copy into a {@link WPIImage} + */ + public WPIImage(BufferedImage image) { + this(IplImage.createFrom(image)); + } + + /** + * Instantiates a {@link WPIImage} from the given {@link IplImage}. + * The resulting image will be directly wrapped around the given image, and so any modifications + * to the {@link WPIImage} will reflect on the given image. + * @param image the image to wrap + */ + public WPIImage(IplImage image) { + this.image = image; + } + + /** + * Returns the width of the image. + * @return the width in pixels of the image + */ + public int getWidth() { + validateDisposed(); + + return image.width(); + } + + /** + * Returns the height of the image. + * @return the height in pixels of the image + */ + public int getHeight() { + validateDisposed(); + + return image.height(); + } + + /** + * Copies this {@link WPIImage} into a {@link BufferedImage}. + * This method will always generate a new image. + * @return a copy of the image + */ + public BufferedImage getBufferedImage() { + validateDisposed(); + + return image.getBufferedImage(); + } + + @Override + protected void disposed() { + image.release(); + } +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIJavaCVUtils.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIJavaCVUtils.java new file mode 100644 index 0000000..b31da02 --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIJavaCVUtils.java @@ -0,0 +1,41 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package edu.wpi.first.wpijavacv; + +import static com.googlecode.javacv.cpp.opencv_highgui.*; + +/** + * A class with utility functions. Use this instead of Thread.sleep() + * @author Greg Granito + */ +public class WPIJavaCVUtils { + + /** + * Waits for the specified key to be pressed + * @param key the key (case sensitive) + */ + public static void waitForKey(char key){ + while(cvWaitKey() != key); + } + + /** + * waits until any key has been pressed + * @return the key that was pressed + */ + public static char waitForAnyKey(){ + return (char)cvWaitKey(); + } + + /** + * Waits until the timeout or until a key is pressed + * @param key the case sensitive key + * @param timeoutMillis the timeout + * @return returns whether the key was pressed (false if the timeout occurs) + */ + public static boolean keyIsPressed(char key, int timeoutMillis){ + return cvWaitKey(timeoutMillis) == key; + } +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPILaptopCamera.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPILaptopCamera.java new file mode 100644 index 0000000..1414b35 --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPILaptopCamera.java @@ -0,0 +1,28 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package edu.wpi.first.wpijavacv; + +import static com.googlecode.javacv.cpp.opencv_highgui.*; +/** + * A class used to gather images from cameras connected to the laptop + * @author Greg + */ +public class WPILaptopCamera extends WPIDisposable { + CvCapture cam; + + public WPILaptopCamera() { + cam = cvCreateCameraCapture(0); + } + + public WPIColorImage getCurrentFrame(){ + return new WPIColorImage(cvQueryFrame(cam)); + } + + @Override + protected void disposed() { + } + +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIMemoryPool.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIMemoryPool.java new file mode 100644 index 0000000..1d9a3b4 --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIMemoryPool.java @@ -0,0 +1,34 @@ +package edu.wpi.first.wpijavacv; + +/** + * This class allows a bunch of disposable items to be put into a pool which will do something if all of them are disposed. + * Subclasses should override the dispose method to react to when everything in the pool is disposed. + * @author Joe Grinstead + */ +public abstract class WPIMemoryPool extends WPIDisposable { + + /** The number of elements remaining in the pool */ + private int remaining; + + /** + * Adds the given disposable item to the memory pool + * @param disposable the item + */ + public synchronized void addToPool(WPIDisposable disposable) { + validateDisposed(); + disposable.setPool(this); + remaining++; + } + + /** + * Removes the given disposable item from the memory pool + * @param disposable + */ + public synchronized void removeFromPool(WPIDisposable disposable) { + validateDisposed(); + disposable.setPool(null); + if (--remaining <= 0) { + dispose(); + } + } +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPoint.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPoint.java new file mode 100644 index 0000000..a5a2016 --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPoint.java @@ -0,0 +1,56 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package edu.wpi.first.wpijavacv; + +import com.googlecode.javacv.cpp.opencv_core.CvPoint; + +/** + * A class representing a point + * @author Greg Granito + */ +public class WPIPoint { + + CvPoint p; + /** + * Creates a new WpiPoint with the specified x and y coordinates + * @param x the x coord + * @param y the y coord + */ + public WPIPoint(int x, int y) { + p = new CvPoint(x, y); + } + + WPIPoint(CvPoint c) { + p = c; + } + + /** + * + * @return the x coord + */ + public int getX(){ + return p.x(); + } + + /** + * + * @return the y coord + */ + public int getY(){ + return p.y(); + } + + CvPoint getCvPoint(){ + return p; + } + + @Override + public String toString() { + return p.toString(); + } + + +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPolygon.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPolygon.java new file mode 100644 index 0000000..a54a297 --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIPolygon.java @@ -0,0 +1,126 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.wpijavacv; + +import static com.googlecode.javacv.cpp.opencv_imgproc.*; +import static com.googlecode.javacv.cpp.opencv_core.*; + +/** + * A class that represents a polygon, can be obtained from approxPolygon() + * in WpiContour + * @author Greg Granito + */ +public class WPIPolygon extends WPIDisposable { + + CvSeq polygon; + CvRect boundingRect; + + WPIPolygon(CvSeq data) { + polygon = data; + } + + CvSeq getCVSeq() { + return polygon; + } + + /** + * + * @return an array of WpiPoints of the vertices of the polygon + */ + public WPIPoint[] getPoints() { + CvPoint points = new CvPoint(getNumVertices()); + WPIPoint[] wpiPoints= new WPIPoint[getNumVertices()]; + cvCvtSeqToArray(polygon, points.position(0), CV_WHOLE_SEQ); + for (int j = 0; j < getNumVertices(); j++) { + + wpiPoints[j] = new WPIPoint(points.position(j).x(), points.position(j).y()); + + } + return wpiPoints; + } + + /** + * + * @return the width of the bounding rectangle of the polygon + */ + public int getWidth() { + if (boundingRect == null) { + boundingRect = cvBoundingRect(polygon, 0); + } + return boundingRect.width(); + } + + /** + * + * @return the height of the bounding rectangle of the polygon + */ + public int getHeight() { + if (boundingRect == null) { + boundingRect = cvBoundingRect(polygon, 0); + } + return boundingRect.height(); + } + + /** + * + * @return the x coord of the top left corner of the bounding + * rectangle of the polygon + */ + public int getX() { + if (boundingRect == null) { + boundingRect = cvBoundingRect(polygon, 0); + } + return boundingRect.x(); + } + + /** + * + * @return the y coord of the top left corner of the bounding + * rectangle of the polygon + */ + public int getY() { + if (boundingRect == null) { + boundingRect = cvBoundingRect(polygon, 0); + } + return boundingRect.y(); + } + + /** + * + * @return the number of vertices in the polygon + */ + public int getNumVertices() { + return polygon.total(); + + } + + /** + * + * @return whether or not the polygon is convex + */ + public boolean isConvex() { + return cvCheckContourConvexity(polygon) == 0 ? false : true; + } + + /** + * + * @return the area in pixels of the polygon + */ + public int getArea() { + return Math.abs((int) cvContourArea(polygon, CV_WHOLE_SEQ, -1)); + } + + /** + * + * @return the perimeter in pixels of the polygon + */ + public int getPerimeter() { + return (int) cvArcLength(polygon, CV_WHOLE_SEQ, -1); + } + + public void disposed() { + polygon.deallocate(); + } +} diff --git a/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIWindow.java b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIWindow.java new file mode 100644 index 0000000..f15db45 --- /dev/null +++ b/WPIJavaCV/src/edu/wpi/first/wpijavacv/WPIWindow.java @@ -0,0 +1,52 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.wpijavacv; + +import static com.googlecode.javacv.cpp.opencv_highgui.*; + +/** + * A class that can be used for displaying images + * @author Greg Granito + */ +public class WPIWindow { + + private static int count = 0; + + private String name; + + /** + * Creates a new window with a default name that will be in the format + * "Window " + windowNumber + */ + public WPIWindow() { + count++; + + name = "Window " +count; + cvNamedWindow(name); + + } + + /** + * Creates a new window with the specified name must be unique + * (including the windows named using the default constructor) + * @param name the desired name + */ + public WPIWindow(String name) { + this.name = name; + } + + + /** + * Shows the specified image, must be the same resolution and depth + * as the first image the window shows + * @param image the image + */ + public void showImage(WPIImage image) { + + if(image != null) + cvShowImage(name, image.image); + else cvShowImage(name, null); + } +} diff --git a/extensions/camera/SquareTracker/Square.pdf b/extensions/camera/SquareTracker/Square.pdf new file mode 100644 index 0000000..23cb55e Binary files /dev/null and b/extensions/camera/SquareTracker/Square.pdf differ diff --git a/extensions/camera/SquareTracker/build.xml b/extensions/camera/SquareTracker/build.xml new file mode 100644 index 0000000..90c2964 --- /dev/null +++ b/extensions/camera/SquareTracker/build.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + Builds, tests, and runs the project SquareTrackerExtension. + + + diff --git a/extensions/camera/SquareTracker/manifest.mf b/extensions/camera/SquareTracker/manifest.mf new file mode 100644 index 0000000..1574df4 --- /dev/null +++ b/extensions/camera/SquareTracker/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/extensions/camera/SquareTracker/nbproject/build-impl.xml b/extensions/camera/SquareTracker/nbproject/build-impl.xml new file mode 100644 index 0000000..99dd2f9 --- /dev/null +++ b/extensions/camera/SquareTracker/nbproject/build-impl.xml @@ -0,0 +1,950 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/camera/SquareTracker/nbproject/genfiles.properties b/extensions/camera/SquareTracker/nbproject/genfiles.properties new file mode 100644 index 0000000..b58eb40 --- /dev/null +++ b/extensions/camera/SquareTracker/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=0e31bf96 +build.xml.script.CRC32=fe93e624 +build.xml.stylesheet.CRC32=28e38971@1.38.3.45 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=0e31bf96 +nbproject/build-impl.xml.script.CRC32=1f2b204c +nbproject/build-impl.xml.stylesheet.CRC32=229523de@1.38.3.45 diff --git a/extensions/camera/SquareTracker/nbproject/private/config.properties b/extensions/camera/SquareTracker/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/extensions/camera/SquareTracker/nbproject/private/private.properties b/extensions/camera/SquareTracker/nbproject/private/private.properties new file mode 100644 index 0000000..7b67f1f --- /dev/null +++ b/extensions/camera/SquareTracker/nbproject/private/private.properties @@ -0,0 +1,6 @@ +compile.on.save=false +do.depend=false +do.jar=true +javac.debug=true +javadoc.preview=true +user.properties.file=C:\\Users\\Paul\\.netbeans\\6.9\\build.properties diff --git a/extensions/camera/SquareTracker/nbproject/private/private.xml b/extensions/camera/SquareTracker/nbproject/private/private.xml new file mode 100644 index 0000000..cc2c0e5 --- /dev/null +++ b/extensions/camera/SquareTracker/nbproject/private/private.xml @@ -0,0 +1,4 @@ + + + + diff --git a/extensions/camera/SquareTracker/nbproject/project.properties b/extensions/camera/SquareTracker/nbproject/project.properties new file mode 100644 index 0000000..c9b6617 --- /dev/null +++ b/extensions/camera/SquareTracker/nbproject/project.properties @@ -0,0 +1,91 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=SquareTrackerExtension +application.vendor=Greg +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/SquareTrackerExtension.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.javacpp.jar=../../../WPIJavaCV/lib/javacpp.jar +file.reference.javacv-windows-x86.jar=../../../WPIJavaCV/lib/javacv-windows-x86.jar +file.reference.javacv.jar=../../../WPIJavaCV/lib/javacv.jar +includes=** +jar.compress=false +javac.classpath=\ + ${reference.WPIJavaCV.jar}:\ + ${reference.WPICameraExtension.jar}:\ + ${reference.SmartDashboard.jar}:\ + ${reference.NetworkTable_Client.jar}:\ + ${file.reference.javacpp.jar}:\ + ${file.reference.javacv-windows-x86.jar}:\ + ${file.reference.javacv.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.5 +javac.target=1.5 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${libs.junit.classpath}:\ + ${libs.junit_4.classpath} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class=squaretrackerextension.Main +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +platform.active=default_platform +project.NetworkTable_Client=../../../NetworkTable/Client/Java/trunk +project.SmartDashboard=../../../smartdashboard +project.WPICameraExtension=../WPICameraExtension +project.WPIJavaCV=../../../WPIJavaCV +reference.NetworkTable_Client.jar=${project.NetworkTable_Client}/dist/NetworkTable_Client.jar +reference.SmartDashboard.jar=${project.SmartDashboard}/dist/SmartDashboard.jar +reference.WPICameraExtension.jar=${project.WPICameraExtension}/dist/WPICameraExtension.jar +reference.WPIJavaCV.jar=${project.WPIJavaCV}/dist/WPIJavaCV.jar +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/extensions/camera/SquareTracker/nbproject/project.xml b/extensions/camera/SquareTracker/nbproject/project.xml new file mode 100644 index 0000000..a69eeae --- /dev/null +++ b/extensions/camera/SquareTracker/nbproject/project.xml @@ -0,0 +1,49 @@ + + + org.netbeans.modules.java.j2seproject + + + SquareTrackerExtension + + + + + + + + + + NetworkTable_Client + jar + + jar + clean + jar + + + SmartDashboard + jar + + jar + clean + jar + + + WPICameraExtension + jar + + jar + clean + jar + + + WPIJavaCV + jar + + jar + clean + jar + + + + diff --git a/extensions/camera/SquareTracker/src/edu/wpi/first/wpilibj/examples/SquareTrackerExtension.java b/extensions/camera/SquareTracker/src/edu/wpi/first/wpilibj/examples/SquareTrackerExtension.java new file mode 100644 index 0000000..e5886a2 --- /dev/null +++ b/extensions/camera/SquareTracker/src/edu/wpi/first/wpilibj/examples/SquareTrackerExtension.java @@ -0,0 +1,118 @@ +package edu.wpi.first.wpilibj.examples; + +import edu.wpi.first.smartdashboard.camera.WPILaptopCameraExtension; +import edu.wpi.first.smartdashboard.robot.Robot; +import edu.wpi.first.wpijavacv.WPIBinaryImage; +import edu.wpi.first.wpijavacv.WPIColor; +import edu.wpi.first.wpijavacv.WPIColorImage; +import edu.wpi.first.wpijavacv.WPIContour; +import edu.wpi.first.wpijavacv.WPIImage; +import edu.wpi.first.wpijavacv.WPIPoint; +import edu.wpi.first.wpijavacv.WPIPolygon; +import edu.wpi.first.wpilibj.networking.NetworkTable; +import java.util.ArrayList; + +/** + * + * @author Greg Granito + */ +public class SquareTrackerExtension extends WPILaptopCameraExtension { + public static final String NAME = "Laptop Camera Square Tracker"; + + NetworkTable table = NetworkTable.getTable("camera"); + WPIColor targetColor = new WPIColor(255, 0, 0); + + @Override + public WPIImage processImage(WPIColorImage rawImage) { + + WPIBinaryImage blueBin = rawImage.getBlueChannel().getThresholdInverted(60); + WPIBinaryImage greenBin = rawImage.getGreenChannel().getThresholdInverted(60); + WPIBinaryImage redBin = rawImage.getRedChannel().getThresholdInverted(60); + + WPIBinaryImage finalBin = blueBin.getAnd(redBin).getAnd(greenBin); + + finalBin.erode(2); + finalBin.dilate(6); + + WPIContour[] contours = finalBin.findContours(); + + ArrayList polygons = new ArrayList(); + + for(WPIContour c : contours){ + double ratio = ((double)c.getHeight()) / ((double)c.getWidth()); + if(ratio < 1.5 && ratio> 0.75){ + polygons.add(c.approxPolygon(45)); + } + } + + ArrayList possiblePolygons = new ArrayList(); + + for(WPIPolygon p : polygons){ + if(p.isConvex() && p.getNumVertices() == 4){ + possiblePolygons.add(p); + }else{ + rawImage.drawPolygon(p, WPIColor.CYAN, 5); + } + } + + WPIPolygon square = null; + int squareArea = 0; + + for(WPIPolygon p : possiblePolygons){ + rawImage.drawPolygon(p, WPIColor.GREEN, 5); + for(WPIPolygon q : possiblePolygons){ + if(p == q) continue; + + int pCenterX = (p.getX() + (p.getWidth()/2)); + + int qCenterX = q.getX() + (q.getWidth()/2); + + int pCenterY = (p.getY() + (p.getHeight()/2)); + + int qCenterY = q.getY() + (q.getHeight()/2); + + rawImage.drawPoint(new WPIPoint(pCenterX, pCenterY), targetColor, 5); + rawImage.drawPoint(new WPIPoint(qCenterX, qCenterY), targetColor, 5); + + if(Math.abs(pCenterX - qCenterX) < 20 && + Math.abs(pCenterY - qCenterY) < 20){ + int pArea = Math.abs(p.getArea()); + int qArea = Math.abs(q.getArea()); + if(pArea > qArea){ + square = p; + squareArea = pArea; + }else{ + square = q; + squareArea = qArea; + } + break; + } + } + } + + if(square != null){ + double x = square.getX() + (square.getWidth()/2); + x = (2 * (x/rawImage.getWidth())) - 1; + + double area = ((double)squareArea) / ((double)(rawImage.getWidth() * rawImage.getHeight())); + + synchronized(table) { + table.beginTransaction(); + table.putBoolean("found", true); + table.putDouble("x", x); + table.putDouble("area", area); + table.endTransaction(); + } + + Robot.getTable().putBoolean("found", true); + Robot.getTable().putDouble("X", x); + Robot.getTable().putDouble("Area", area); + rawImage.drawPolygon(square, targetColor, 7); + }else{ + table.putBoolean("found", false); + Robot.getTable().putBoolean("found", false); + } + + return rawImage; + } +} diff --git a/extensions/camera/WPICameraExtension/build.xml b/extensions/camera/WPICameraExtension/build.xml new file mode 100644 index 0000000..5ca1581 --- /dev/null +++ b/extensions/camera/WPICameraExtension/build.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + Builds, tests, and runs the project WPICameraExtension. + + + diff --git a/extensions/camera/WPICameraExtension/manifest.mf b/extensions/camera/WPICameraExtension/manifest.mf new file mode 100644 index 0000000..1574df4 --- /dev/null +++ b/extensions/camera/WPICameraExtension/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/extensions/camera/WPICameraExtension/nbproject/build-impl.xml b/extensions/camera/WPICameraExtension/nbproject/build-impl.xml new file mode 100644 index 0000000..ea6d3b7 --- /dev/null +++ b/extensions/camera/WPICameraExtension/nbproject/build-impl.xml @@ -0,0 +1,1400 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/camera/WPICameraExtension/nbproject/genfiles.properties b/extensions/camera/WPICameraExtension/nbproject/genfiles.properties new file mode 100644 index 0000000..35733b3 --- /dev/null +++ b/extensions/camera/WPICameraExtension/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=0d2b3442 +build.xml.script.CRC32=f9e70049 +build.xml.stylesheet.CRC32=28e38971@1.53.1.46 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=0d2b3442 +nbproject/build-impl.xml.script.CRC32=55f5fbcf +nbproject/build-impl.xml.stylesheet.CRC32=6ddba6b6@1.53.1.46 diff --git a/extensions/camera/WPICameraExtension/nbproject/private/config.properties b/extensions/camera/WPICameraExtension/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/extensions/camera/WPICameraExtension/nbproject/private/private.properties b/extensions/camera/WPICameraExtension/nbproject/private/private.properties new file mode 100644 index 0000000..bb21662 --- /dev/null +++ b/extensions/camera/WPICameraExtension/nbproject/private/private.properties @@ -0,0 +1,6 @@ +compile.on.save=false +do.depend=false +do.jar=true +javac.debug=true +javadoc.preview=true +user.properties.file=C:\\Users\\Mitchell Wills\\AppData\\Roaming\\NetBeans\\7.2\\build.properties diff --git a/extensions/camera/WPICameraExtension/nbproject/private/private.xml b/extensions/camera/WPICameraExtension/nbproject/private/private.xml new file mode 100644 index 0000000..cc2c0e5 --- /dev/null +++ b/extensions/camera/WPICameraExtension/nbproject/private/private.xml @@ -0,0 +1,4 @@ + + + + diff --git a/extensions/camera/WPICameraExtension/nbproject/project.properties b/extensions/camera/WPICameraExtension/nbproject/project.properties new file mode 100644 index 0000000..5cfab74 --- /dev/null +++ b/extensions/camera/WPICameraExtension/nbproject/project.properties @@ -0,0 +1,96 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=WPICameraExtension +application.vendor=Greg +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/WPICameraExtension.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.javacpp.jar=lib/javacpp.jar +file.reference.javacv-windows-x86.jar=lib/javacv-windows-x86.jar +file.reference.javacv.jar=lib/javacv.jar +file.reference.SmartDashboard.jar=lib/SmartDashboard.jar +file.reference.WPIJavaCV.jar=lib/WPIJavaCV.jar +includes=** +jar.archive.disabled=${jnlp.enabled} +jar.compress=false +jar.index=${jnlp.enabled} +javac.classpath=\ + ${file.reference.WPIJavaCV.jar}:\ + ${file.reference.SmartDashboard.jar}:\ + ${file.reference.javacpp.jar}:\ + ${file.reference.javacv-windows-x86.jar}:\ + ${file.reference.javacv.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.5 +javac.target=1.5 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${libs.junit.classpath}:\ + ${libs.junit_4.classpath} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jnlp.codebase.type=no.codebase +jnlp.descriptor=application +jnlp.enabled=false +jnlp.mixed.code=default +jnlp.offline-allowed=false +jnlp.signed=false +jnlp.signing= +jnlp.signing.alias= +jnlp.signing.keystore= +main.class= +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=src +test.src.dir=test diff --git a/extensions/camera/WPICameraExtension/nbproject/project.xml b/extensions/camera/WPICameraExtension/nbproject/project.xml new file mode 100644 index 0000000..41107a0 --- /dev/null +++ b/extensions/camera/WPICameraExtension/nbproject/project.xml @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + WPICameraExtension + + + + + + + + + + diff --git a/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/VideoStreamExtension.java b/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/VideoStreamExtension.java new file mode 100644 index 0000000..6937b86 --- /dev/null +++ b/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/VideoStreamExtension.java @@ -0,0 +1,166 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.camera; + +import edu.wpi.first.smartdashboard.gui.StaticWidget; +import edu.wpi.first.smartdashboard.properties.Property; +import edu.wpi.first.smartdashboard.properties.StringProperty; +import edu.wpi.first.wpijavacv.WPIColorImage; +import edu.wpi.first.wpijavacv.WPIFFmpegVideo; +import edu.wpi.first.wpijavacv.WPIGrayscaleImage; +import edu.wpi.first.wpijavacv.WPIImage; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; + +/** + * + * @author Greg Granito + */ +public class VideoStreamExtension extends StaticWidget { + + public static final String NAME = "Video Stream"; + private boolean connected = false; + + public class GCThread extends Thread { + + boolean destroyed = false; + + @Override + public void run() { + while (!destroyed) { + try { + Thread.sleep(10000); + } catch (InterruptedException ex) { + } + System.gc(); + } + } + + public void destroy() { + destroyed = true; + interrupt(); + } + } + + public class BGThread extends Thread { + + boolean destroyed = false; + + public BGThread() { + super("Video Stream Background"); + } + + @Override + public void run() { + WPIImage image; + while (!destroyed) { + if (cam == null) { + cam = new WPIFFmpegVideo(pathProperty.getSaveValue()); + } + try { + image = cam.getNewImage(5.0); + + if (image instanceof WPIColorImage) { + drawnImage = processImage((WPIColorImage) image).getBufferedImage(); + repaint(); + + } else if (image instanceof WPIGrayscaleImage) { + drawnImage = processImage((WPIGrayscaleImage) image).getBufferedImage(); + repaint(); + } + } catch (final Exception e) { + e.printStackTrace(); + cam.dispose(); + cam = null; + drawnImage = null; + repaint(); + try { + Thread.sleep(2000); + } catch (InterruptedException ex) { + } + } + } + + } + + @Override + public void destroy() { + destroyed = true; + } + } + private boolean resized = false; + private WPIFFmpegVideo cam; + private BufferedImage drawnImage; + private BGThread bgThread = new BGThread(); + private GCThread gcThread = new GCThread(); + public final StringProperty pathProperty = new StringProperty(this, "Video Path", "http://localhost:8080/something"); + + @Override + public void init() { + setPreferredSize(new Dimension(100, 100)); + bgThread.start(); + gcThread.start(); + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == pathProperty) { + if (cam != null) { + cam.dispose(); + } + try { + cam = new WPIFFmpegVideo(pathProperty.getSaveValue()); + } catch (Exception e) { + e.printStackTrace(); + drawnImage = null; + setPreferredSize(new Dimension(100, 100)); + revalidate(); + repaint(); + } + } + + } + + @Override + public void disconnect() { + bgThread.destroy(); + gcThread.destroy(); + if(cam != null) cam.dispose(); + super.disconnect(); + } + + @Override + protected void paintComponent(Graphics g) { + if (drawnImage != null) { + if (!resized) { + setPreferredSize(new Dimension(drawnImage.getWidth(), drawnImage.getHeight())); + revalidate(); + } + int width = getBounds().width; + int height = getBounds().height; + double scale = Math.min((double) width / (double) drawnImage.getWidth(), (double) height / (double) drawnImage.getHeight()); + g.drawImage(drawnImage, (int) (width - (scale * drawnImage.getWidth())) / 2, (int) (height - (scale * drawnImage.getHeight())) / 2, + (int) ((width + scale * drawnImage.getWidth()) / 2), (int) (height + scale * drawnImage.getHeight()) / 2, + 0, 0, drawnImage.getWidth(), drawnImage.getHeight(), null); + } else { + g.setColor(Color.PINK); + g.fillRect(0, 0, getBounds().width, getBounds().height); + g.setColor(Color.BLACK); + g.drawString("NO CONNECTION", 10, 10); + } + } + + public WPIImage processImage(WPIColorImage rawImage) { + return rawImage; + } + + public WPIImage processImage(WPIGrayscaleImage rawImage) { + return rawImage; + } +} diff --git a/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/VideoStreamViewerExtension.java b/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/VideoStreamViewerExtension.java new file mode 100644 index 0000000..86f55cc --- /dev/null +++ b/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/VideoStreamViewerExtension.java @@ -0,0 +1,129 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.camera; + +import edu.wpi.first.smartdashboard.gui.DashboardPrefs; +import edu.wpi.first.smartdashboard.gui.StaticWidget; +import edu.wpi.first.smartdashboard.properties.Property; +import edu.wpi.first.smartdashboard.properties.StringProperty; +import edu.wpi.first.wpijavacv.WPIColorImage; +import edu.wpi.first.wpijavacv.WPIFFmpegVideo; +import edu.wpi.first.wpijavacv.WPIGrayscaleImage; +import edu.wpi.first.wpijavacv.WPIImage; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; + +/** + * + * @author Greg Granito + */ +public class VideoStreamViewerExtension extends StaticWidget { + + public static final String NAME = "Video Stream Viewer"; + private boolean connected = false; + + public class BGThread extends Thread { + + boolean destroyed = false; + + public BGThread() { + super("Video Stream Background"); + } + + @Override + public void run() { + WPIImage image; + while (!destroyed) { + if (cam == null) { + cam = new WPIFFmpegVideoViewer(pathProperty.getSaveValue()); + } + try { + drawnImage = cam.getNewImage(5.0); + repaint(); + + } catch (final Exception e) { + e.printStackTrace(); + cam.dispose(); + cam = null; + drawnImage = null; + repaint(); + try { + Thread.sleep(2000); + } catch (InterruptedException ex) { + } + } + } + + } + + @Override + public void destroy() { + destroyed = true; + } + } + private boolean resized = false; + private WPIFFmpegVideoViewer cam; + private BufferedImage drawnImage; + private BGThread bgThread = new BGThread(); + private final int team = DashboardPrefs.getInstance().team.getValue(); + public final StringProperty pathProperty = new StringProperty(this, "Video Path", "http://10." + (team / 100) + "." + (team % 100) + ".11/mjpg/video.mjpg"); + + @Override + public void init() { + setPreferredSize(new Dimension(100, 100)); + bgThread.start(); + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == pathProperty) { + if (cam != null) { + cam.dispose(); + } + try { + cam = new WPIFFmpegVideoViewer(pathProperty.getSaveValue()); + } catch (Exception e) { + e.printStackTrace(); + drawnImage = null; + setPreferredSize(new Dimension(100, 100)); + revalidate(); + repaint(); + } + } + + } + + @Override + public void disconnect() { + bgThread.destroy(); + if(cam != null) cam.dispose(); + super.disconnect(); + } + + @Override + protected void paintComponent(Graphics g) { + if (drawnImage != null) { + if (!resized) { + setPreferredSize(new Dimension(drawnImage.getWidth(), drawnImage.getHeight())); + revalidate(); + } + int width = getBounds().width; + int height = getBounds().height; + double scale = Math.min((double) width / (double) drawnImage.getWidth(), (double) height / (double) drawnImage.getHeight()); + g.drawImage(drawnImage, (int) (width - (scale * drawnImage.getWidth())) / 2, (int) (height - (scale * drawnImage.getHeight())) / 2, + (int) ((width + scale * drawnImage.getWidth()) / 2), (int) (height + scale * drawnImage.getHeight()) / 2, + 0, 0, drawnImage.getWidth(), drawnImage.getHeight(), null); + } else { + g.setColor(Color.PINK); + g.fillRect(0, 0, getBounds().width, getBounds().height); + g.setColor(Color.BLACK); + g.drawString("NO CONNECTION", 10, 10); + } + } +} diff --git a/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/WPICameraExtension.java b/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/WPICameraExtension.java new file mode 100644 index 0000000..d10d633 --- /dev/null +++ b/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/WPICameraExtension.java @@ -0,0 +1,169 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.camera; + +import edu.wpi.first.smartdashboard.gui.DashboardFrame; +import edu.wpi.first.smartdashboard.gui.DashboardPrefs; +import edu.wpi.first.smartdashboard.gui.StaticWidget; +import edu.wpi.first.smartdashboard.properties.IPAddressProperty; +import edu.wpi.first.smartdashboard.properties.Property; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import javax.swing.SwingUtilities; +import edu.wpi.first.wpijavacv.WPICamera; +import edu.wpi.first.wpijavacv.WPIColorImage; +import edu.wpi.first.wpijavacv.WPIGrayscaleImage; +import edu.wpi.first.wpijavacv.WPIImage; + +/** + * + * @author Greg Granito + */ +public class WPICameraExtension extends StaticWidget { + + public static final String NAME = "Camera"; + private boolean connected = false; + + public class GCThread extends Thread { + + boolean destroyed = false; + + @Override + public void run() { + while (!destroyed) { + try { + Thread.sleep(10000); + } catch (InterruptedException ex) { + } + System.gc(); + } + } + + public void destroy() { + destroyed = true; + interrupt(); + } + } + + public class BGThread extends Thread { + + boolean destroyed = false; + + public BGThread() { + super("Camera Background"); + } + + @Override + public void run() { + WPIImage image; + while (!destroyed) { + if (cam == null) { + cam = new WPICamera(ipProperty.getSaveValue()); + } + try { + image = cam.getNewImage(5.0); + + if (image instanceof WPIColorImage) { + drawnImage = processImage((WPIColorImage) image).getBufferedImage(); + repaint(); + + } else if (image instanceof WPIGrayscaleImage) { + drawnImage = processImage((WPIGrayscaleImage) image).getBufferedImage(); + repaint(); + } + } catch (final Exception e) { + e.printStackTrace(); + cam.dispose(); + cam = null; + drawnImage = null; + repaint(); + try { + Thread.sleep(2000); + } catch (InterruptedException ex) { + } + } + } + + } + + @Override + public void destroy() { + destroyed = true; + } + } + private boolean resized = false; + private WPICamera cam; + private BufferedImage drawnImage; + private BGThread bgThread = new BGThread(); + private GCThread gcThread = new GCThread(); + public final IPAddressProperty ipProperty = new IPAddressProperty(this, "Camera IP Address", new int[]{10, (DashboardPrefs.getInstance().team.getValue() / 100), (DashboardPrefs.getInstance().team.getValue() % 100), 11}); + + @Override + public void init() { + setPreferredSize(new Dimension(100, 100)); + bgThread.start(); + gcThread.start(); + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == ipProperty) { + if (cam != null) { + cam.dispose(); + } + try { + cam = new WPICamera(ipProperty.getSaveValue()); + } catch (Exception e) { + e.printStackTrace(); + drawnImage = null; + setPreferredSize(new Dimension(100, 100)); + revalidate(); + repaint(); + } + } + + } + + @Override + public void disconnect() { + bgThread.destroy(); + gcThread.destroy(); + if(cam != null) cam.dispose(); + super.disconnect(); + } + + @Override + protected void paintComponent(Graphics g) { + if (drawnImage != null) { + if (!resized) { + setPreferredSize(new Dimension(drawnImage.getWidth(), drawnImage.getHeight())); + revalidate(); + } + int width = getBounds().width; + int height = getBounds().height; + double scale = Math.min((double) width / (double) drawnImage.getWidth(), (double) height / (double) drawnImage.getHeight()); + g.drawImage(drawnImage, (int) (width - (scale * drawnImage.getWidth())) / 2, (int) (height - (scale * drawnImage.getHeight())) / 2, + (int) ((width + scale * drawnImage.getWidth()) / 2), (int) (height + scale * drawnImage.getHeight()) / 2, + 0, 0, drawnImage.getWidth(), drawnImage.getHeight(), null); + } else { + g.setColor(Color.PINK); + g.fillRect(0, 0, getBounds().width, getBounds().height); + g.setColor(Color.BLACK); + g.drawString("NO CONNECTION", 10, 10); + } + } + + public WPIImage processImage(WPIColorImage rawImage) { + return rawImage; + } + + public WPIImage processImage(WPIGrayscaleImage rawImage) { + return rawImage; + } +} diff --git a/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/WPIFFmpegVideoViewer.java b/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/WPIFFmpegVideoViewer.java new file mode 100644 index 0000000..b8a149d --- /dev/null +++ b/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/WPIFFmpegVideoViewer.java @@ -0,0 +1,133 @@ +package edu.wpi.first.smartdashboard.camera; + +import com.googlecode.javacv.FFmpegFrameGrabber; +import static com.googlecode.javacv.cpp.opencv_core.*; +import com.googlecode.javacv.cpp.opencv_core.IplImage; +import edu.wpi.first.wpijavacv.WPIDisposable; +import edu.wpi.first.wpijavacv.WPIImage; +import java.awt.image.BufferedImage; + +/** + * A class used to gather images from the robot's camera. + * @author Joe Grinstead and Greg Granito + */ +public class WPIFFmpegVideoViewer extends WPIDisposable { + + private FFmpegFrameGrabber grabber; + private IplImage image; + private boolean readImage = true; + private boolean badConnection = false; + private final Object imageLock = new Object(); + private final Object grabberLock = new Object(); + + public WPIFFmpegVideoViewer(final String path) { + new Thread() { + + @Override + public void run() { + grabber = new FFmpegFrameGrabber(path); + grabber.setFrameRate(1.0); + try { + grabber.start(); + + while (!isDisposed()) { + try { + IplImage newest; + synchronized (grabberLock) { + if (isDisposed()) { + return; + } + newest = grabber.grab(); + } + if (isNull(newest)) { + synchronized (imageLock) { + badConnection = true; + imageLock.notify(); + } + return; + } else { + synchronized (imageLock) { + if (image == null) { + image = cvCreateImage(newest.cvSize(), newest.depth(), newest.nChannels()); + } + cvCopy(newest, image); + readImage = false; + badConnection = false; + imageLock.notify(); + } + } + } catch (Exception ex) { + synchronized (imageLock) { + badConnection = true; + imageLock.notify(); + } + ex.printStackTrace(); + return; + } + try { + Thread.sleep(20); + } catch (InterruptedException ex) { + } + } + } catch (Exception ex) { + synchronized (imageLock) { + badConnection = true; + imageLock.notify(); + } + ex.printStackTrace(); + } + } + }.start(); + } + + public BufferedImage getNewImage(double timeout) throws BadConnectionException { + validateDisposed(); + + synchronized (imageLock) { + readImage = true; + while (readImage && !badConnection) { + try { + badConnection = true; + imageLock.wait((long) (timeout * 1000)); + } catch (InterruptedException ex) { + } + } + readImage = true; + + + if (badConnection) { + throw new BadConnectionException(); + } else if (image == null) { + return null; + } else { + return image.getBufferedImage(); + } + } + } + + @Override + protected void disposed() { + try { + synchronized (imageLock) { + if (!isNull(image)) { + image.release(); + } + image = null; + } + } catch (Exception ex) { + } + } + + /** + * An exception that occurs when the camera can not be reached. + * @author Greg Granito + */ + public static class BadConnectionException extends Exception { + } + + @Override + protected void finalize() throws Throwable { + grabber.stop(); + super.finalize(); + } +} diff --git a/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/WPILaptopCameraExtension.java b/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/WPILaptopCameraExtension.java new file mode 100644 index 0000000..32e17cc --- /dev/null +++ b/extensions/camera/WPICameraExtension/src/edu/wpi/first/smartdashboard/camera/WPILaptopCameraExtension.java @@ -0,0 +1,148 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.camera; + +import edu.wpi.first.smartdashboard.gui.DashboardFrame; +import edu.wpi.first.smartdashboard.gui.StaticWidget; +import edu.wpi.first.smartdashboard.properties.Property; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import javax.swing.SwingUtilities; +import edu.wpi.first.wpijavacv.WPIColorImage; +import edu.wpi.first.wpijavacv.WPIGrayscaleImage; +import edu.wpi.first.wpijavacv.WPIImage; +import edu.wpi.first.wpijavacv.WPILaptopCamera; + +/** + * An extension that gets images from a webcam on the computer. + * @author Greg Granito + */ +public class WPILaptopCameraExtension extends StaticWidget { + + public static final String NAME = "Laptop Camera"; + private boolean connected = false; + + public class GCThread extends Thread { + + boolean destroyed = false; + + @Override + public void run() { + while (!destroyed) { + try { + Thread.sleep(10000); + } catch (InterruptedException ex) { + } + System.gc(); + } + } + + public void destroy() { + destroyed = true; + interrupt(); + } + } + + public class BGThread extends Thread { + + boolean destroyed = false; + + public BGThread() { + super("Camera Background"); + } + + @Override + public void run() { + WPIImage image; + while (!destroyed) { + if (cam == null) { + cam = new WPILaptopCamera(); + } + try { + image = cam.getCurrentFrame(); + + if (image instanceof WPIColorImage) { + drawnImage = processImage((WPIColorImage) image).getBufferedImage(); + repaint(); + } else if (image instanceof WPIGrayscaleImage) { + drawnImage = processImage((WPIGrayscaleImage) image).getBufferedImage(); + repaint(); + } + } catch (final Exception e) { + drawnImage = null; + repaint(); + try { + Thread.sleep(500); + } catch (InterruptedException ex) { + } + } + } + + } + + @Override + public void destroy() { + destroyed = true; + } + } + private boolean resized = false; + private WPILaptopCamera cam; + private BufferedImage drawnImage; + private BGThread bgThread = new BGThread(); + private GCThread gcThread = new GCThread(); + + @Override + public void init() { + setPreferredSize(new Dimension(100, 100)); + bgThread.start(); + gcThread.start(); + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + + } + + @Override + public void disconnect() { + bgThread.destroy(); + gcThread.destroy(); + cam.dispose(); + super.disconnect(); + } + + @Override + protected void paintComponent(Graphics g) { + if (drawnImage != null) { + if (!resized) { + setPreferredSize(new Dimension(drawnImage.getWidth(), drawnImage.getHeight())); + revalidate(); + } + int width = getBounds().width; + int height = getBounds().height; + double scale = Math.min((double) width / (double) drawnImage.getWidth(), (double) height / (double) drawnImage.getHeight()); + g.drawImage(drawnImage, (int) (width - (scale * drawnImage.getWidth())) / 2, (int) (height - (scale * drawnImage.getHeight())) / 2, + (int) ((width + scale * drawnImage.getWidth()) / 2), (int) (height + scale * drawnImage.getHeight()) / 2, + 0, 0, drawnImage.getWidth(), drawnImage.getHeight(), null); + } else { + g.setColor(Color.PINK); + g.fillRect(0, 0, getBounds().width, getBounds().height); + g.setColor(Color.BLACK); + g.drawString("NO CONNECTION", 10, 10); + } + } + + public WPIImage processImage(WPIColorImage rawImage) { + return rawImage; + } + + public WPIImage processImage(WPIGrayscaleImage rawImage) { + return rawImage; + } +} diff --git a/smartdashboard/.classpath b/smartdashboard/.classpath new file mode 100644 index 0000000..59d0db7 --- /dev/null +++ b/smartdashboard/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/smartdashboard/.project b/smartdashboard/.project new file mode 100644 index 0000000..38d413f --- /dev/null +++ b/smartdashboard/.project @@ -0,0 +1,17 @@ + + + smartdashboard + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/smartdashboard/build.xml b/smartdashboard/build.xml new file mode 100644 index 0000000..6585753 --- /dev/null +++ b/smartdashboard/build.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Smart Dashboard + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/smartdashboard/customSave b/smartdashboard/customSave new file mode 100644 index 0000000..39a81cd --- /dev/null +++ b/smartdashboard/customSave @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/smartdashboard/lib/jcommon-1.0.16.jar b/smartdashboard/lib/jcommon-1.0.16.jar new file mode 100644 index 0000000..4cd6807 Binary files /dev/null and b/smartdashboard/lib/jcommon-1.0.16.jar differ diff --git a/smartdashboard/lib/jfreechart-1.0.13.jar b/smartdashboard/lib/jfreechart-1.0.13.jar new file mode 100644 index 0000000..83c6993 Binary files /dev/null and b/smartdashboard/lib/jfreechart-1.0.13.jar differ diff --git a/smartdashboard/lib/junit-4.8.2.jar b/smartdashboard/lib/junit-4.8.2.jar new file mode 100644 index 0000000..5b4bb84 Binary files /dev/null and b/smartdashboard/lib/junit-4.8.2.jar differ diff --git a/smartdashboard/lib/nblibraries.properties b/smartdashboard/lib/nblibraries.properties new file mode 100644 index 0000000..849ac15 --- /dev/null +++ b/smartdashboard/lib/nblibraries.properties @@ -0,0 +1,12 @@ +libs.junit.classpath=\ + ${base}/junit/junit-3.8.2.jar +libs.junit.javadoc=\ + ${base}/junit/junit-3.8.2-api.zip +libs.junit_4.classpath=\ + ${base}/junit_4/junit-4.5.jar +libs.junit_4.javadoc=\ + ${base}/junit_4/junit-4.5-api.zip +libs.junit_4.src=\ + ${base}/junit_4/junit-4.5-src.jar +libs.CopyLibs.classpath=\ + ${base}/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar diff --git a/smartdashboard/manifest.mf b/smartdashboard/manifest.mf new file mode 100644 index 0000000..1574df4 --- /dev/null +++ b/smartdashboard/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/smartdashboard/nbbuild.xml b/smartdashboard/nbbuild.xml new file mode 100644 index 0000000..d960259 --- /dev/null +++ b/smartdashboard/nbbuild.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + Builds, tests, and runs the project SmartDashboardClientSVN. + + + + + + + + + + + + + + diff --git a/smartdashboard/nbproject/build-impl.xml b/smartdashboard/nbproject/build-impl.xml new file mode 100644 index 0000000..b792a47 --- /dev/null +++ b/smartdashboard/nbproject/build-impl.xml @@ -0,0 +1,1400 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.tests.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + + + + + + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/smartdashboard/nbproject/genfiles.properties b/smartdashboard/nbproject/genfiles.properties new file mode 100644 index 0000000..fb785da --- /dev/null +++ b/smartdashboard/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +nbbuild.xml.data.CRC32=685859a1 +nbbuild.xml.script.CRC32=0d9c8ec5 +nbbuild.xml.stylesheet.CRC32=28e38971@1.38.1.45 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=4f9ed213 +nbproject/build-impl.xml.script.CRC32=b7ebbfab +nbproject/build-impl.xml.stylesheet.CRC32=6ddba6b6@1.53.1.46 diff --git a/smartdashboard/nbproject/project.properties b/smartdashboard/nbproject/project.properties new file mode 100644 index 0000000..33ad4c1 --- /dev/null +++ b/smartdashboard/nbproject/project.properties @@ -0,0 +1,94 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +application.title=SmartDashboard +application.vendor=Paul +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +buildfile=nbbuild.xml +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/SmartDashboard.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes= +file.reference.jcommon-1.0.16.jar=lib/jcommon-1.0.16.jar +file.reference.jfreechart-1.0.13.jar=lib/jfreechart-1.0.13.jar +file.reference.junit-4.8.2.jar=lib/junit-4.8.2.jar +file.reference.networktables-desktop.jar=lib/networktables-desktop.jar +file.reference.smartdashboard-src=src +includes=** +jar.archive.disabled=${jnlp.enabled} +jar.compress=false +jar.index=${jnlp.enabled} +javac.classpath=\ + ${file.reference.jcommon-1.0.16.jar}:\ + ${file.reference.jfreechart-1.0.13.jar}:\ + ${file.reference.junit-4.8.2.jar}:\ + ${file.reference.networktables-desktop.jar} +# Space-separated list of extra javac options +javac.compilerargs= +javac.deprecation=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.5 +javac.target=1.5 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${file.reference.jcommon-1.0.16.jar}:\ + ${file.reference.jfreechart-1.0.13.jar}:\ + ${file.reference.junit-4.8.2.jar} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +jnlp.codebase.type=no.codebase +jnlp.descriptor=application +jnlp.enabled=false +jnlp.mixed.code=default +jnlp.offline-allowed=false +jnlp.signed=false +jnlp.signing= +jnlp.signing.alias= +jnlp.signing.keystore= +main.class=edu.wpi.first.smartdashboard.main +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project +# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value +# or test-sys-prop.name=value to set system properties for unit tests): +run.jvmargs= +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.dir=${file.reference.smartdashboard-src} +test.tests.dir=tests diff --git a/smartdashboard/nbproject/project.xml b/smartdashboard/nbproject/project.xml new file mode 100644 index 0000000..e3a1776 --- /dev/null +++ b/smartdashboard/nbproject/project.xml @@ -0,0 +1,16 @@ + + + org.netbeans.modules.java.j2seproject + + + SmartDashboard + + + + + + + + + + diff --git a/smartdashboard/smartdashboard.install4j b/smartdashboard/smartdashboard.install4j new file mode 100644 index 0000000..9b1b402 --- /dev/null +++ b/smartdashboard/smartdashboard.install4j @@ -0,0 +1,460 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sys.installationDir + + + + + + + context.getBooleanVariable("sys.confirmedUpdateInstallation") + + + + + + + + + + false + + + + + !context.getBooleanVariable("sys.confirmedUpdateInstallation") + + + + + + + + + + + + sys.installationDir + + + + + + + context.getVariable("sys.responseFile") == null + + + + + + + + + + + + + + + + + + + + + + + ${compiler:sys.fullName} + + + + + !context.getBooleanVariable("sys.confirmedUpdateInstallation") + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${i18n:UninstallerMenuEntry(${compiler:sys.fullName})} + + + + + !context.getBooleanVariable("sys.programGroupDisabled") + + + + + + + ${compiler:sys.fullName} ${compiler:sys.version} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/ArgParser.java b/smartdashboard/src/edu/wpi/first/smartdashboard/ArgParser.java new file mode 100644 index 0000000..c3f2438 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/ArgParser.java @@ -0,0 +1,53 @@ +package edu.wpi.first.smartdashboard; + +import java.util.*; + +public class ArgParser { + private Map argValues = new HashMap(); + private Set flags = new HashSet(); + private final boolean ignoreLeadingDash; + private final boolean ignoreCase; + private String getProcessedName(String name){ + if(ignoreLeadingDash && name.startsWith("-")) + name = name.substring(1); + if(ignoreCase) + name = name.toLowerCase(); + return name; + } + public ArgParser(String[] args, boolean ignoreLeadingDash, boolean ignoreCase, String[] valueArgs) { + this.ignoreLeadingDash = ignoreLeadingDash; + this.ignoreCase = ignoreCase; + + if(ignoreLeadingDash){ + for(int i = 0; i sysclass = URLClassLoader.class; + + Method method = null; + try { + method = sysclass.getDeclaredMethod("addURL", new Class[]{URL.class}); + method.setAccessible(true); + } catch (Exception e) { + e.printStackTrace(); + monitor.setProgress(max); + return; + } + + for (File lib : new File[]{lib1, lib2, extensions}) { + if (!lib.exists()) { + monitor.setProgress(min + (max - min) / 5); + continue; + } + System.out.println("Searching Folder:" + lib); + monitor.setNote("Searching Folder:" + lib); + + File[] files = lib.listFiles(new FilenameFilter() { + + public boolean accept(File dir, String name) { + return name.endsWith(".jar"); + } + }); + if (files == null) { + monitor.setProgress(min + (max - min) / 5); + continue; + } + + for (File file : files) { + System.out.println("Adding Jar:" + file); + + try { + method.invoke(sysloader, new Object[]{file.toURI().toURL()}); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + monitor.setProgress(min + (max - min) / 5); + } + + if (!extensions.exists()) { + System.out.println("No Extension Folder"); + monitor.setProgress(max); + return; + } + + File[] files = extensions.listFiles(new FilenameFilter() { + + public boolean accept(File dir, String name) { + return name.endsWith(".jar"); + } + }); + + double fileCount = 0; + for (File file : files) { + System.out.println("Searching Jar:" + file); + monitor.setProgress((int) ((min + max) / 2.0 * (1.0 + fileCount++ / files.length))); + monitor.setNote("Searching Jar:" + file); + + try { + JarFile jar = new JarFile(file); + + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + + if (name.endsWith(".class")) { + Class clazz = null; + try { + // Get rid of class + name = name.substring(0, name.length() - 6); + // Change to package name + name = name.replaceAll("/", "."); + + clazz = Class.forName(name, false, sysloader); + + Class element = clazz.asSubclass(Widget.class); + DisplayElementRegistry.registerWidget(element); + + System.out.println("Custom Widget:" + clazz.getSimpleName()); + } catch (ClassCastException ex) { + try { + Class element = clazz.asSubclass(StaticWidget.class); + DisplayElementRegistry.registerStaticWidget(element); + + System.out.println("Custom Static Widget:" + clazz.getSimpleName()); + } catch (ClassCastException ex2) { + } + } catch (ClassNotFoundException ex) { + } catch (NoClassDefFoundError ex) { + } + } + } + } catch (Throwable t) { + t.printStackTrace(); + System.out.println("Error, could not add URL to system classloader"); + } + } + + monitor.setProgress(max); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardFrame.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardFrame.java new file mode 100644 index 0000000..42c957e --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardFrame.java @@ -0,0 +1,380 @@ +package edu.wpi.first.smartdashboard.gui; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.util.*; +import java.util.List; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.*; +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.livewindow.elements.LWSubsystem; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.robot.Robot; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.xml.*; +import edu.wpi.first.wpilibj.networktables.NetworkTable; +import edu.wpi.first.wpilibj.tables.ITable; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This class defines the main window for the FRC program. It contains almost no + * logic except for the {@link DashboardFrame#load(java.lang.String) load(...)} + * and {@link DashboardFrame#save(java.lang.String) save(...)} method. + * + * @author Joe Grinstead + */ +public class DashboardFrame extends JFrame { + + /* + * If the menu bar is set to "hidden," then this defines what portion of the + * top screen is reserved for revealing the menu bar when the mouse moves + * over it + */ + private static final int MENU_HEADER = 10; + /** The size the frame should be when displayed on the netbook */ + private static final Dimension NETBOOK_SIZE = new Dimension(1024, 400); + /** The minimum size of the frame */ + private static final Dimension MINIMUM_SIZE = new Dimension(300, 200); + + public enum DisplayMode { + SmartDashboard, + LiveWindow; + } + + private final DashboardPrefs prefs = new DashboardPrefs(this); + /** The content of the frame */ + private final DashboardPanel smartDashboardPanel; + private final DashboardPanel liveWindowPanel; + private final MainPanel mainPanel; + private DisplayMode displayMode = DisplayMode.SmartDashboard; + /** The menu bar */ + private final JMenuBar menuBar; + /** The property table (there is only this one ever) */ + private final PropertyEditor propEditor; + /** Whether or not the menu bar should be hidden */ + private boolean shouldHideMenu = prefs.hideMenu.getValue(); + + private static final String LW_SAVE = "_"+Robot.getLiveWindow().getSubTable("~STATUS~").getString("Robot", "LiveWindow")+".xml"; + + private final LogToCSV logger = new LogToCSV(this); + + private static DashboardFrame INSTANCE = null; + public static DashboardFrame getInstance(){ + return INSTANCE; + } + + /** + * Initializes the frame. + * + * @param competition + * whether or not to display as though it were on the netbook + */ + public DashboardFrame(boolean competition) { + super("SmartDashboard - "); + + setLayout(new BorderLayout()); + + // The content of the frame is really contained in the panel and menu + smartDashboardPanel = new DashboardPanel(this, Robot.getTable()); + smartDashboardPanel.setName("SmartDashboard"); + liveWindowPanel = new DashboardPanel(this, Robot.getLiveWindow()); + liveWindowPanel.setName("LiveWindow"); + mainPanel = new MainPanel(new CardLayout(), smartDashboardPanel, liveWindowPanel, smartDashboardPanel); + mainPanel.add(smartDashboardPanel, DisplayMode.SmartDashboard.toString()); + mainPanel.add(liveWindowPanel, DisplayMode.LiveWindow.toString()); + setDisplayMode(DisplayMode.SmartDashboard); + menuBar = new DashboardMenu(this, mainPanel); + propEditor = new PropertyEditor(this); + + if (!shouldHideMenu) { + add(menuBar, BorderLayout.NORTH); + } + add(mainPanel, BorderLayout.CENTER); + + // Look for when the menu bar should be displayed + MouseAdapter hideListener = new MouseAdapter() { + + @Override + public void mouseMoved(MouseEvent e) { + if (shouldHideMenu && e.getY() < MENU_HEADER) { + add(menuBar, BorderLayout.NORTH); + validate(); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + if (shouldHideMenu) { + remove(menuBar); + validate(); + } + } + }; + smartDashboardPanel.addMouseListener(hideListener); + smartDashboardPanel.addMouseMotionListener(hideListener); + + // Set the size / look + if (competition) { + setPreferredSize(NETBOOK_SIZE); + setUndecorated(true); + setLocation(0, 0); + setResizable(false); + } else { + setMinimumSize(MINIMUM_SIZE); + + // Closing operation is handled manually + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + + setPreferredSize(new Dimension(prefs.width.getValue(), prefs.height.getValue())); + setLocation(prefs.x.getValue(), prefs.y.getValue()); + } + + // Call our own exit function when the frame is closed + addWindowListener(new WindowAdapter() { + + @Override + public void windowClosing(WindowEvent e) { + exit(); + } + }); + + // Make resizing affect the preference variables for window size + addComponentListener(new ComponentListener() { + + public void componentResized(ComponentEvent e) { + prefs.width.setValue(getWidth()); + prefs.height.setValue(getHeight()); + } + + public void componentMoved(ComponentEvent e) { + prefs.x.setValue(getX()); + prefs.y.setValue(getY()); + } + + public void componentShown(ComponentEvent e) { + } + + public void componentHidden(ComponentEvent e) { + } + }); + + INSTANCE = this;//will only be instanciated once so make this a singleton so plugins can get it for compatability + } + + /** + * Sets the current DisplayMode to the given mode. + * @param mode The mode to put the frame into. + */ + public final void setDisplayMode(DisplayMode mode) { + displayMode = mode; + CardLayout cl = (CardLayout)(mainPanel.getLayout()); + cl.show(mainPanel, mode.toString()); + } + + /** + * Returns the property editor + * + * @return the property editor + */ + public PropertyEditor getPropertyEditor() { + return propEditor; + } + + /** + * Sets whether or not the menu should be hidden. This does not attempt to + * change the property setting, instead the property setting should call + * this. + * + * @param shouldHide + * whether or not the menu should hide + */ + public void setShouldHideMenu(boolean shouldHide) { + if (shouldHideMenu != shouldHide) { + shouldHideMenu = shouldHide; + if (shouldHideMenu) { + remove(menuBar); + } else { + add(menuBar, BorderLayout.NORTH); + } + validate(); + } + } + + /** + * Saves the frame to the file of the given name + * + * @param path + * the path to save the file to + */ + public void save(String path) { + try { + System.out.println("Saving to:\t"+path); + SmartDashboardXMLWriter writer = new SmartDashboardXMLWriter(path); + + writer.beginSmartDashboard(); + saveElements(writer, smartDashboardPanel); + writer.endSmartDashboard(); + + writer.beginLiveWindow(); + saveElements(writer, liveWindowPanel); + writer.endLiveWindow(); + + for (String field : smartDashboardPanel.getHiddenFields()) { + writer.addHiddenField(field); + } + + writer.close(); + } catch (Exception ex) { + Logger.getLogger(DashboardFrame.class.getName()).log(Level.SEVERE, null, ex); + } + } + + private void saveElements(SmartDashboardXMLWriter writer, DashboardPanel toSave) throws IOException { + for (DisplayElement element : toSave.getElements()) { + boolean isWidget = element instanceof Widget; + assert isWidget || element instanceof StaticWidget; + if (isWidget) { + writer.beginWidget(((Widget) element).getFieldName(), ((Widget) element).getType().getName(), element.getClass().getName()); + } else { + writer.beginStaticWidget(element.getClass().getName()); + } + if(element instanceof LWSubsystem) { + for(Widget w : ((LWSubsystem) element).getWidgets()) { + System.out.println(" Saving "+((LWSubsystem)element).getFieldName()+"|"+w.getFieldName()); + writer.addSubWidget(w.getFieldName(), w.getType().getName(), w.getClass().getName()); + writer.addSubWidgetLocation(w.getLocation()); + writer.addSubWudgetHeight(w.getHeight()); + writer.addSubWidgetWidth(w.getWidth()); + writer.endSubWidget(); + } + } + writer.addLocation(element.getLocation()); + + Dimension size = element.getSavedSize(); + if (size.width > 0) { + writer.addWidth(size.width); + } + if (size.height > 0) { + writer.addHeight(size.height); + } + + for (Map.Entry prop : element.getProperties().entrySet()) { + if (!prop.getValue().isDefault()) { + writer.addProperty(prop.getKey(), prop.getValue().getSaveValue()); + } + } + + if (isWidget) { + writer.endWidget(); + } else { + writer.endStaticWidget(); + } + } + } + + /** + * Loads the current state of this MainWindow and any significant objects it + * contains + */ + public void load(String path) { + try { + SmartDashboardXMLReader reader = new SmartDashboardXMLReader(path); + + System.out.println("\nLoading from \t"+path); + + List sdWidgets = reader.getXMLWidgets(); + for (int i = sdWidgets.size(); i > 0; i--) { + System.out.println("Loading SmartDashboard widgets...."); + XMLWidget widget = sdWidgets.get(i - 1); + DisplayElement element = widget.convertToDisplayElement(); + if (element instanceof Widget) { + Widget e = (Widget) element; + Object value = null; + if (Robot.getTable().containsKey(e.getFieldName())) { + value = Robot.getTable().getValue(e.getFieldName()); + DataType type = DataType.getType(value); + if (DisplayElementRegistry.supportsType(e.getClass(), type)) { + smartDashboardPanel.setField(e.getFieldName(), e, type, value, e.getSavedLocation()); + } + } else { + smartDashboardPanel.setField(e.getFieldName(), e, widget.getType(), null, e.getSavedLocation()); + } + } else if (element instanceof StaticWidget) { + StaticWidget e = (StaticWidget) element; + smartDashboardPanel.addElement(e, widget.getLocation()); + } else { + // TODO tell the user it was null + } + } + + LWSubsystem mostRecentParent = null; + for(XMLWidget subsys : reader.getSubsystems().keySet()) { + System.out.println("\nLoading \""+subsys.getField()+"\""); + LWSubsystem subsystem = (LWSubsystem) subsys.convertToDisplayElement(); + mostRecentParent = subsystem; + Object value1 = null; + if(Robot.getLiveWindow().containsKey(subsystem.getFieldName())) { + value1 = Robot.getTable().getValue(subsystem.getFieldName()); + DataType type = DataType.getType(value1); + if(DisplayElementRegistry.supportsType(subsystem.getClass(), type)) { + liveWindowPanel.setField(subsystem.getFieldName(), subsystem, type, value1, subsystem.getSavedLocation()); + } + } else { + liveWindowPanel.setField(subsystem.getFieldName(), subsystem, subsystem.getType(), null, subsystem.getSavedLocation()); + } + for(XMLWidget component : reader.getSubwidgetMap(subsys).values()) { + System.out.println("Adding subcomponent \""+component.getField()+"\""); + AbstractTableWidget w = (AbstractTableWidget) component.convertToDisplayElement(); + Object value2 = null; + value2 = Robot.getLiveWindow().getSubTable(mostRecentParent.getFieldName()).getSubTable(w.getFieldName()); + DataType type = DataType.getType(value2); + mostRecentParent.addWidget(w); + w.setField(w.getFieldName(), w, type, value2, mostRecentParent, w.getSavedLocation()); + mostRecentParent.setSize(mostRecentParent.getPreferredSize()); + } + } + + + for (String field : reader.getHiddenFields()) { + smartDashboardPanel.removeField(field); + } + + Map prefMap = prefs.getProperties(); + for (Map.Entry entry : reader.getProperties().entrySet()) { + Property prop = prefMap.get(entry.getKey()); + prop.setValue(entry.getValue()); + } + + repaint(); + } catch (FileNotFoundException e) { + // TODO tell the user + } + } + + /** + * Exits the program, prompting the user to save. + */ + public void exit() { + int result = JOptionPane.showConfirmDialog(this, new String[] { "Do you wish to save this layout?" }, "Save before quitting?", + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); + switch (result) { + case JOptionPane.YES_OPTION: + save(prefs.saveFile.getValue()); + case JOptionPane.NO_OPTION: + System.exit(0); + default: // Do Nothing (they called cancel) + } + } + + public DashboardPrefs getPrefs() { + return prefs; + } + + public LogToCSV getLogger() { + return logger; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardMenu.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardMenu.java new file mode 100644 index 0000000..28ea979 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardMenu.java @@ -0,0 +1,238 @@ +package edu.wpi.first.smartdashboard.gui; + +import edu.wpi.first.smartdashboard.livewindow.elements.LWSubsystem; +import edu.wpi.first.smartdashboard.livewindow.elements.Controller; +import edu.wpi.first.smartdashboard.robot.Robot; +import edu.wpi.first.smartdashboard.types.DisplayElementRegistry; +import edu.wpi.first.wpilibj.tables.ITable; +import edu.wpi.first.wpilibj.tables.ITableListener; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.io.File; +import java.util.Set; +import javax.swing.*; +import javax.swing.event.MenuEvent; +import javax.swing.event.MenuListener; +import org.jfree.ui.ExtensionFileFilter; + +/** + * This is the menu bar on top of the window. It can be set to hide + * automatically in the preferences. + * + * @author Joe Grinstead + */ +public class DashboardMenu extends JMenuBar { + + /** + * Creates a menu for the given panel. + * + */ + DashboardMenu(final DashboardFrame frame, final MainPanel mainPanel) { + JMenu fileMenu = new JMenu("File"); + JMenuItem loadMenu = new JMenuItem("Open..."); + loadMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_DOWN_MASK)); + loadMenu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(frame.getPrefs().saveFile.getValue())); + fc.addChoosableFileFilter(new ExtensionFileFilter("XML File", ".xml")); + fc.setMultiSelectionEnabled(false); + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + fc.setApproveButtonText("Open"); + fc.setDialogTitle("Open"); + + if (fc.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) { + String filepath = fc.getSelectedFile().getAbsolutePath(); + + frame.load(filepath); + frame.getPrefs().saveFile.setValue(filepath); + } + } + }); + fileMenu.add(loadMenu); + + JMenuItem newMenu = new JMenuItem("New"); + newMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK)); + newMenu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + mainPanel.getPanel("SmartDashboard").clear(); + } + }); + fileMenu.add(newMenu); + + JMenuItem saveMenu = new JMenuItem("Save"); + saveMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_DOWN_MASK)); + saveMenu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + frame.save(frame.getPrefs().saveFile.getValue()); + } + }); + fileMenu.add(saveMenu); + + JMenuItem saveAs = new JMenuItem("Save As..."); + saveAs.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); + saveAs.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JFileChooser fc = new JFileChooser("."); + fc.addChoosableFileFilter(new ExtensionFileFilter("XML File", ".xml")); + fc.setApproveButtonText("Save"); + fc.setDialogTitle("Save As..."); + + fc.setMultiSelectionEnabled(false); + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + + if (fc.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) { + String filepath = fc.getSelectedFile().getAbsolutePath(); + if (!filepath.endsWith(".xml")) { + filepath = filepath + ".xml"; + } + frame.save(filepath); + frame.getPrefs().saveFile.setValue(filepath); + } + } + }); + fileMenu.add(saveAs); + + JMenuItem prefMenu = new JMenuItem("Preferences"); + prefMenu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + PropertyEditor editor = frame.getPropertyEditor(); + editor.setPropertyHolder(frame.getPrefs()); + editor.setTitle("Edit Preferences"); + editor.setVisible(true); + } + }); + fileMenu.add(prefMenu); + + JMenuItem exitMenu = new JMenuItem("Exit"); + exitMenu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + frame.exit(); + } + }); + fileMenu.add(exitMenu); + + JMenu viewMenu = new JMenu("View"); + final JCheckBoxMenuItem editMode = new JCheckBoxMenuItem("Editable"); + editMode.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + for (DashboardPanel panel : MainPanel.panels.values()) { + panel.setEditable(!panel.isEditable()); + } + } + }); + editMode.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.CTRL_DOWN_MASK)); + viewMenu.add(editMode); + + JCheckBoxMenuItem editSystems = new JCheckBoxMenuItem("Edit Subsystems"); + editSystems.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + LWSubsystem.setEditable(!LWSubsystem.isEditable()); + } + }); + editSystems.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); + editSystems.doClick(); + viewMenu.add(editSystems); + + final JMenuItem resetLW = new JMenuItem("Reset LiveWindow"); + resetLW.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + for (LWSubsystem subsystem : MainPanel.getPanel("LiveWindow").getSubsystems()) { + for (Widget component : subsystem.getWidgets()) { + if (component instanceof Controller) { + System.out.println("\tResetting " + component.getFieldName()); + ((Controller) component).reset(); + } + } + } + } + }); + resetLW.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK)); + Robot.getLiveWindow().getSubTable("~STATUS~").addTableListener("LW Enabled", new ITableListener() { + public void valueChanged(ITable itable, String string, Object o, boolean bln) { + final boolean isInLW = Robot.getLiveWindow().getSubTable("~STATUS~").getBoolean("LW Enabled", false); + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + frame.setDisplayMode(isInLW + ? DashboardFrame.DisplayMode.LiveWindow + : DashboardFrame.DisplayMode.SmartDashboard); + + mainPanel.setCurrentPanel(isInLW + ? MainPanel.getPanel("LiveWindow") + : MainPanel.getPanel("SmartDashboard")); + if (!isInLW) { + resetLW.doClick(); + } + + } + }); + } + }, true); + viewMenu.add(resetLW); + + JMenu addMenu = new JMenu("Add..."); + Set> panels = DisplayElementRegistry.getStaticWidgets(); + for (final Class option : panels) { + JMenuItem item = new JMenuItem(DisplayElement.getName(option)); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + StaticWidget element = option.newInstance(); + mainPanel.getPanel("SmartDashboard").addElement(element, null); + } catch (InstantiationException ex) { + } catch (IllegalAccessException ex) { + } + } + }); + + addMenu.add(item); + } + + viewMenu.add(addMenu); + + final JMenu revealMenu = new JMenu("Reveal..."); + + viewMenu.addMenuListener(new MenuListener() { + public void menuSelected(MenuEvent e) { + revealMenu.removeAll(); + + int count = 0; + for (final String field : mainPanel.getPanel("SmartDashboard").getHiddenFields()) { + if (mainPanel.getPanel("SmartDashboard").getTable().containsKey(field)) { + count++; + revealMenu.add(new JMenuItem(new AbstractAction(field) { + public void actionPerformed(ActionEvent e) { + mainPanel.getPanel("SmartDashboard").addField(field); + } + })); + } + } + + revealMenu.setEnabled(count != 0); + } + + public void menuDeselected(MenuEvent e) { + } + + public void menuCanceled(MenuEvent e) { + } + }); + + viewMenu.add(revealMenu); + + JMenuItem removeUnusedMenu = new JMenuItem("Remove Unused"); + removeUnusedMenu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + MainPanel.getCurrentPanel().removeUnusedFields(); + } + }); + + viewMenu.add(removeUnusedMenu); + + add(fileMenu); + add(viewMenu); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPanel.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPanel.java new file mode 100644 index 0000000..386a134 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPanel.java @@ -0,0 +1,596 @@ +package edu.wpi.first.smartdashboard.gui; + +import edu.wpi.first.smartdashboard.livewindow.elements.LWSubsystem; +import java.awt.*; +import java.awt.event.*; +import java.util.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * This is the main panel, it sits within the {@link DashboardFrame} and + * contains everything the user sees except for the window outline. This class + * is the workhorse of the GUI. Inside here is where the logic is contained for + * how to respond to new fields and various other things. + * + * @author Joe Grinstead + */ +public class DashboardPanel extends JPanel { + + /** + * We use a glass pane technique for editable mode + */ + private GlassPane glassPane; + /** + * This panel contains everything except the glass pane + */ + private JPanel backPane = new JPanel(); + /** + * All the elements currently being displayed + */ + private LinkedList elements = new LinkedList(); + /** + * All the fields that are currently being displayed + */ + private Map fields = new HashMap(); + /** + * All the fields which are hidden (they have no widget) + */ + private Set hiddenFields = new HashSet(); + /** + * Whether or not this is editable + */ + private boolean editable = false; + /** + * The listener which connects the panel to keep up to date on the robot + */ + private final RobotListener listener = new RobotListener(); + private final ArrayList subsystems = new ArrayList(); + private final DashboardFrame frame; + private final ITable table; + + /** + * Instantiates the panel + */ + public DashboardPanel(DashboardFrame frame, ITable table) { + this.frame = frame; + this.table = table; + glassPane = new GlassPane(frame, this); + add(glassPane); + add(backPane); + + backPane.setLayout(new DashboardLayout()); + backPane.setFocusable(true); + + setLayout(new DashboardLayout()); + + setEditable(editable); + + table.addTableListener(listener, true); + table.addSubTableListener(listener); + } + + public ITable getTable() { + return table; + } + + @Override + public synchronized void addMouseListener(MouseListener l) { + glassPane.addMouseListener(l); + backPane.addMouseListener(l); + } + + @Override + public synchronized void addMouseMotionListener(MouseMotionListener l) { + glassPane.addMouseMotionListener(l); + backPane.addMouseMotionListener(l); + } + + /** + * Revalidates the content behind the glass pane + */ + public void revalidateBacking() { + backPane.revalidate(); + } + + /** + * Sets whether or not this panel is editable. If the panel becomes + * editable, then it will pull the focus from the widgets. + * + * @param editable whether or not the pane should be editable + */ + public void setEditable(boolean editable) { + this.editable = editable; + + glassPane.setVisible(editable); + if (editable) { + glassPane.requestFocus(); + } + } + + /** + * Returns whether or not this panel is editable. + * + * @return whether or not this panel is editable + */ + public boolean isEditable() { + return editable; + } + + /** + * Returns the names of all the fields marked as hidden (they are the ones + * in the {@link ITable} that have been explicitly declared to be ignored by + * the user). + * + * @return the hidden fields + */ + public Iterable getHiddenFields() { + return hiddenFields; + } + + /** + * Returns all the {@link DisplayElement DisplayElements} that are in this + * panel. + * + * @return all the elements + */ + public Iterable getElements() { + return elements; + } + + /** + * Takes the given value and manipulates it based on the given type to be + * able to be handed to a {@link Widget}. Basically, if the type is a + * primitive, the value will be returned. If the type is a + * {@link NamedDataType}, then the data subtable will be returned. If either + * the type of the value is {@code null}, then {@code null} will be + * returned. + * + * @param type the type of the data + * @param value the data + * @return the data to be handed to a {@link Widget} + */ + private Object verifyValue(DataType type, Object value) { + if (type == null || value == null) { + return null; + } else if (type instanceof NamedDataType) { + return value; + } else { + return value; + } + } + + /** + * Clears the panel of all of its data, and then reload it. This basically + * starts the panel over from the beginning. + */ + public void clear() { + hiddenFields.clear(); + fields.clear(); + for (DisplayElement element : elements) { + disconnect(element); + backPane.remove(element); + } + elements.clear(); + + + + table.removeTableListener(listener); + table.addTableListener(listener, true); + table.addSubTableListener(listener); + + repaint(); + } + + /** + * Removes all fields which do not have values in the + */ + public void removeUnusedFields() { + ArrayList unused = new ArrayList(); + for (String field : fields.keySet()) { + if (!(table.containsKey(field) || table.containsSubTable(field))) { + unused.add(field); + } + } + for (String field : unused) { + removeField(field); + hiddenFields.remove(field); + } + } + + /** + * Removes the field with the given name from the screen. The field will + * then be added to the list of fields which should remain hidden. + * + * @param field the field to remove + */ + public void removeField(String field) { + Widget elem = fields.get(field); + hiddenFields.add(field); + if (elem != null) { + disconnect(elem); + backPane.remove(elem); + fields.remove(field); + elements.remove(elem); + repaint(elem.getBounds()); + } + } + + /** + * Removes the given element from the screen. If you would like to remove a + * {@link Widget} instead of a {@link StaticWidget}, then consider using + * {@link DashboardPanel#removeField(java.lang.String) removeField(...)} + * + * @param widget the widget to remove + */ + public void removeElement(StaticWidget widget) { + disconnect(widget); + backPane.remove(widget); + elements.remove(widget); + repaint(widget.getBounds()); + } + + /** + * Shifts the given element behind all the other ones. Every other element + * will then be drawn in front of the given one. + * + * @param element the element to shift + */ + public void shiftToBack(DisplayElement element) { + int count = 0; + + elements.remove(element); + + for (DisplayElement e : elements) { + backPane.setComponentZOrder(e, count++); + } + backPane.setComponentZOrder(element, count); + + elements.add(element); + + repaint(); + } + + /** + * Adds the given display element to the screen, putting it at the given + * point. If the given point is {@code null}, then it will find a place to + * put it. If the point is not {@code null}, then the given + * {@link DisplayElement} should already have set its size. + * + * @param element the element to add + * @param point the location to put it (or null, if one needs to be found) + */ + public void addElement(DisplayElement element, Point point) { + // Initialize the element + element.init(); + + // Set the elements location + if (point == null) { + Dimension saved = element.getSavedSize(); + Dimension preferred = element.getPreferredSize(); + if (saved.width > 0) { + preferred.width = saved.width; + } + if (saved.height > 0) { + preferred.height = saved.height; + } + element.setSize(preferred); + point = findSpace(element); + element.setBounds(new Rectangle(point, preferred)); + } + element.setSavedLocation(point); + + backPane.add(element); + + // Put the new element in front (shift everything back first) + int count = 1; + for (DisplayElement e : elements) { + backPane.setComponentZOrder(e, count++); + } + backPane.setComponentZOrder(element, 0); + + elements.addFirst(element); + + // Repaint + revalidate(); + repaint(); + } + + /** + * Sets the element to use for the given field, removing the current element + * if one exists for that field. This is used mostly by the + * {@link DashboardFrame#load(java.lang.String) load(...)} method. + * + * @param key the name of the field + * @param element the element to give to that field + * @param type the type of the data + * @param value the value of the data + * @param point the point to put it + */ + public void setField(String key, Widget element, DataType type, Object value, Point point) { + removeField(key); + + hiddenFields.remove(key); + + value = verifyValue(type, value); + + element.setFieldName(key); + if (type != null) { + element.setType(type); + } + + fields.put(key, element); + + addElement(element, point); + + if (value != null) { + element.setValue(value); + } + } + + /** + * Adds the field of the given name to the screen. The field does not need + * to be in the SmartDashboard table. This will add some default widget (for + * that type) with no value. + * + * @param key the key to add + */ + public void addField(String key) { + setField(key, null, table.containsKey(key) ? table.getValue(key) : null, null); + } + + /** + * Sets the field to use the given values. + * + * @param key the key of the field + * @param preferred the preferred widget to use + * @param value the value of the field (must not be {@code null}) + * @param point the point the widget should be (can be {@code null}) + */ + public void setField(String key, Class preferred, Object value, Point point) { + setField(key, preferred, DataType.getType(value), value, point); + } + + /** + * Sets the field to use the given values. + * + * @param key the key of the field + * @param preferred the preferred widget to use + * @param type the type of the field + * @param value the value of the field (can be {@code null}) + * @param point the point the widget should be (can be {@code null}) + */ + @SuppressWarnings("unchecked") + public void setField(String key, Class preferred, final DataType type, Object value, Point point) { + Widget element = fields.get(key); + + if (type == null) { + System.out.println("WARNING: has no way of handling data at field \"" + key + "\""); + removeField(key); + } else if (element != null && preferred == null && (element.getType() == type || element.supportsType(type))) { + if (element.getType() != type) { + element.setType(type); + } + + value = verifyValue(type, value); + if (value != null) { + element.setValue(value); + } + } else { + Class clazz = preferred == null ? type.getDefault() : preferred; + + if (clazz == null) { + Set> candidates = DisplayElementRegistry.getWidgetsForType(type); + + if (candidates.isEmpty()) { + System.out.println("WARNING: has no way of handling type " + type); + return; + } else { + clazz = (Class) candidates.toArray()[0]; + } + } + + try { + element = clazz.newInstance(); + + setField(key, element, type, value, point); + } catch (InstantiationException ex) { + System.out.println("ERROR: " + clazz.getName() + " has no default constructor!"); + } catch (IllegalAccessException ex) { + System.out.println("ERROR: " + clazz.getName() + " has no public default constructor!"); + } + } + } + + /** + * Returns the element that covers the given point. It will return the + * forward most element. + * + * @param point the point on the screen + * @return the element + */ + public DisplayElement findElementContaining(Point point) { + for (DisplayElement element : elements) { + if (element.getBounds().contains(point)) { + return element; + } + } + return null; + } + /** + * Just a standard random + */ + private static final Random random = new Random(); + + /** + * Finds a space to put the newest element, using its preferred size + * + * @param toPlace the element to place + * @return the place where it should go + */ + private Point findSpace(DisplayElement toPlace) { + Stack positions = new Stack(); + positions.add(new Point(0, 0)); + + Dimension size = toPlace.getSize(); + Dimension panelBounds = getSize(); + + PositionLoop: + while (!positions.isEmpty()) { + Point position = positions.pop(); + Rectangle area = new Rectangle(position, size); + + if (area.x < 0 || area.y < 0 + || area.x + area.width > panelBounds.width + || area.y + area.height > panelBounds.height) { + continue; + } + + for (DisplayElement element : elements) { + if (element != toPlace && element.isObstruction()) { + Rectangle bounds = element.getBounds(); + // Test Intersection + if (!(bounds.x > area.x + area.width + || bounds.x + bounds.width < area.x + || bounds.y > area.y + area.height + || bounds.y + bounds.height < area.y)) { + Point right = new Point(bounds.x + bounds.width + 1, position.y); + if (positions.isEmpty()) { + positions.add(right); + right = null; + } + positions.add(new Point(position.x, bounds.y + bounds.height + 1)); + if (right != null && Math.abs(right.x - area.x) < area.width / 3) { + positions.add(right); + } + continue PositionLoop; + } + } + } + + System.out.println("Adding an element at [" + position.x + "," + position.y + "]"); + return position; + } + + // If no space was found, jumble them at the beginning + return new Point(random.nextInt(32), random.nextInt(32)); + } + + /** + * Calls the {@link DisplayElement#disconnect() disconnect()} method of the + * given {@link DisplayElement}, catching and printing any exceptions in the + * process. + * + * @param element the element to disconnect + */ + private void disconnect(DisplayElement element) { + try { + element.disconnect(); + } catch (Exception e) { + String message = "An exception occurred while removing the " + + DisplayElement.getName(element.getClass()) + + " of type " + e.getClass() + + ".\nThe message is:\n" + e.getMessage() + + "\nThe stack trace is:\n"; + for (StackTraceElement trace : e.getStackTrace()) { + message += trace.toString() + "\n"; + } + JOptionPane.showMessageDialog(frame, + message, + "Exception When Removing Element", JOptionPane.ERROR_MESSAGE); + } + } + + private class RobotListener implements ITableListener { + + @Override + public void valueChanged(final ITable source, final String key, final Object value, final boolean isNew) { + if (isNew && !frame.getPrefs().autoShowWidgets.getValue() && !fields.containsKey(key)) { + hiddenFields.add(key); + } else { + if (!hiddenFields.contains(key)) { + if (value instanceof ITable) { + final ITable table = (ITable) value; + table.addTableListener("~TYPE~", new ITableListener() { + public void valueChanged(final ITable typeSource, final String typeKey, final Object typeValue, final boolean typeIsNew) { + table.removeTableListener(this); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + setField(key, null, value, null); + } + }); + } + }, true); + } else { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + setField(key, null, value, null); + } + }); + } + } + } + + } + } + + /** + * Gets the subsystems stored in this panel. + */ + public ArrayList getSubsystems() { + return subsystems; + } + + /** + * For resetting the LiveWindow. + * + * @param subsystem The subsystem to add to this panel's list of subsystems. + */ + public void addSubsystem(LWSubsystem subsystem) { + subsystems.add(subsystem); + } + + private class DashboardLayout implements LayoutManager { + + public void addLayoutComponent(String name, Component comp) { + } + + public void removeLayoutComponent(Component comp) { + } + + public Dimension preferredLayoutSize(Container parent) { + return new Dimension(640, 480); // Not going to be used + } + + public Dimension minimumLayoutSize(Container parent) { + return new Dimension(0, 0); // Not going to be used + } + + public void layoutContainer(Container parent) { + if (parent == DashboardPanel.this) { + Dimension size = getSize(); + glassPane.setBounds(0, 0, size.width, size.height); + backPane.setBounds(0, 0, size.width, size.height); + } else { // Back Pane + for (DisplayElement element : elements) { + element.setLocation(element.getSavedLocation()); + + Dimension savedSize = element.getSavedSize(); + Dimension preferredSize = element.getPreferredSize(); + Dimension size = new Dimension(preferredSize); + if (savedSize != null && savedSize.width != -1) { + size.width = savedSize.width; + } + if (savedSize != null && savedSize.height != -1) { + size.height = savedSize.height; + } + + element.setSize(size); + } + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPrefs.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPrefs.java new file mode 100644 index 0000000..79d0d30 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPrefs.java @@ -0,0 +1,117 @@ +package edu.wpi.first.smartdashboard.gui; + +import edu.wpi.first.smartdashboard.main; +import java.util.*; +import java.util.prefs.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.robot.*; +import java.io.File; + +/** + * + * @author brad + */ +public class DashboardPrefs implements PropertyHolder { + private static final File USER_HOME = new File(System.getProperty("user.home")); + private static final File USER_SMARTDASHBOARD_HOME = new File(USER_HOME, "SmartDashboard"); + static{ + if(!USER_SMARTDASHBOARD_HOME.exists()){ + USER_SMARTDASHBOARD_HOME.mkdirs(); + } + } + + private Map properties = new LinkedHashMap(); + public final IntegerProperty team = new IntegerProperty(this, "Team Number", 0); + public final BooleanProperty hideMenu = new BooleanProperty(this, "Hide Menu", false); + public final BooleanProperty autoShowWidgets = new BooleanProperty(this, "Automatically Show Widgets", true); + public final IntegerListProperty grid_widths = new IntegerListProperty(this, "Grid Cell Width(s)", new int[] { 16 }); + public final IntegerListProperty grid_heights = new IntegerListProperty(this, "Grid Cell Height(s)", new int[] { 16 }); + public final IntegerProperty x = new IntegerProperty(this, "Window X Position", 0); + public final IntegerProperty y = new IntegerProperty(this, "Window Y Position", 0); + public final IntegerProperty width = new IntegerProperty(this, "Window Width", 640); + public final IntegerProperty height = new IntegerProperty(this, "Window Height", 480); + public final FileProperty saveFile = new FileProperty(this, "Save File", new File(USER_SMARTDASHBOARD_HOME, "save.xml").getAbsolutePath()); + public final BooleanProperty logToCSV = new BooleanProperty(this, "Log to CSV", false); + public final FileProperty csvFile = new FileProperty(this, "CSV File", new File(USER_SMARTDASHBOARD_HOME, "csv.txt").getAbsolutePath()); + private Preferences node; + + + public static DashboardPrefs getInstance(){ + return DashboardFrame.getInstance().getPrefs(); + } + + private final DashboardFrame frame; + public DashboardPrefs(DashboardFrame frame) { + this.frame = frame; + node = Preferences.userNodeForPackage(main.class); + + for (Property property : properties.values()) { + if (property == logToCSV) {//always set logtoCSV to default on load + continue; + } + load(property); + } + } + + private void load(Property property) { + property.setSaveValue(node.get(property.getName(), property.getSaveValue())); + } + + public Map getProperties() { + return properties; + } + + public boolean validatePropertyChange(Property property, Object value) { + if (property == team || property == width || property == height) { + return (Integer) value > 0; + } else if (property == grid_widths || property == grid_heights) { + int[] values = (int[]) value; + + if (values.length == 0) { + return false; + } else { + for (int i : values) { + if (i <= 0) { + return false; + } + } + return true; + } + } else if (property == logToCSV) { + if ((Boolean) value) { + int result = JOptionPane.showOptionDialog(null, "Should SmartDashboard start logging to the CSV file? (This will override the existing file)", + "Warning", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, null, false); + return result == JOptionPane.YES_OPTION; + } + } + return true; + } + + public void propertyChanged(Property property) { + node.put(property.getName(), property.getSaveValue()); + + if (property == x) { + frame.setLocation(x.getValue(), frame.getY()); + } else if (property == y) { + frame.setLocation(frame.getX(), y.getValue()); + } else if (property == width) { + frame.setSize(width.getValue(), frame.getHeight()); + } else if (property == height) { + frame.setSize(frame.getWidth(), height.getValue()); + } else if (property == team) { + Robot.setTeam(team.getValue()); + frame.setTitle("SmartDashboard - " + team.getValue()); + } else if (property == hideMenu) { + frame.setShouldHideMenu(hideMenu.getValue()); + } else if (property == logToCSV) { + if (logToCSV.getValue()) { + frame.getLogger().start(csvFile.getValue()); + } else { + frame.getLogger().stop(); + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DisplayElement.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DisplayElement.java new file mode 100644 index 0000000..a26210b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DisplayElement.java @@ -0,0 +1,166 @@ +package edu.wpi.first.smartdashboard.gui; + +import java.awt.*; +import java.lang.reflect.*; +import java.util.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; + +/** + * This class is the super class of everything which can be seen on the SmartDashboard + * besides the file menu. + * @author pmalmsten + * @author Joe Grinstead + */ +public abstract class DisplayElement extends JPanel implements PropertyHolder { + + /** The point that the element was saved at or should save to */ + private Point savedLocation = new Point(0, 0); + /** The saved dimension, -1 means the preferred dimension should be used */ + private Dimension savedDimension = new Dimension(-1, -1); + /** The property map */ + private Map properties = new LinkedHashMap(); + /** Whether or not this element can be resized by the user dragging on an edge */ + private boolean resizable = true; + /** Whether or not new display elements should not be placed on this object */ + private boolean obstruction = true; + + /** + * Instantiates the element. Most creation code should be done in the + * {@link DisplayElement#init() init()} method. + */ + public DisplayElement() { + setOpaque(false); + } + + /** + * Sets up and displays any internal subcomponents managed by this UI element. + * + * This will be called from within the GUI thread, so don't worry about + * running things in the EventQueue. A DisplayElement should be fully drawn + * on the screen and ready to receive data when this method returns. + */ + public abstract void init(); + + /** + * This method is called when an element is removed, and should be used to + * clean up any loose ends. It will be called from the GUI thread. + */ + public void disconnect() { + } + + public boolean validatePropertyChange(Property property, Object value) { + return true; + } + + /** + * Returns whether or not this element should be considered an "obstruction." + * + *

An obstruction is any object which the SmartDashboard should automatically + * try to avoid when placing new {@link DisplayElement DisplayElements}. + * Pretty much everything is an obstruction except for {@link edu.wpi.first.smartdashboard.gui.elements.Image Images} + * because images will sometimes be used for background effect, in which case it is + * fine if a new {@link DisplayElement} spawns over it.

+ * @return whether or not this element should be considered an obstruction + */ + public boolean isObstruction() { + return obstruction; + } + + /** + * Sets whether or not this element is an "obstruction." + * + *

An obstruction is any object which the SmartDashboard should automatically + * try to avoid when placing new {@link DisplayElement DisplayElements}. + * Pretty much everything is an obstruction except for {@link edu.wpi.first.smartdashboard.gui.elements.Image Images} + * because images will sometimes be used for background effect, in which case it is + * fine if a new {@link DisplayElement} spawns over it.

+ * @param obstruction whether or not it should be an obstruction + */ + public void setObstruction(boolean obstruction) { + this.obstruction = obstruction; + } + + public Map getProperties() { + return properties; + } + + /** + * Returns the point that the element was saved at or should save to. + * @return the point that the element was saved at or should save to + */ + public Point getSavedLocation() { + return new Point(savedLocation); + } + + /** + * Returns the saved size of this element. + * @return the saved size of this element + */ + public Dimension getSavedSize() { + return new Dimension(savedDimension); + } + + /** + * Sets the saved location for this element. + * @param p the location that this element should save to a file + */ + public void setSavedLocation(Point p) { + savedLocation = p; + } + + /** + * Sets the saved dimension for this element. If a value is -1, + * the preferred size will be used internally. + * @param d the dimension + */ + public void setSavedSize(Dimension d) { + savedDimension = new Dimension(d); + } + + /** + * Returns whether or not this element can be resized by the user dragging on an edge. + * @return whether or not this element can be resized by the user dragging on an edge + */ + public boolean isResizable() { + return resizable; + } + + /** + * Sets whether or not this element can be resized by the user dragging on an edge. + */ + public void setResizable(boolean resizable) { + this.resizable = resizable; + } + + protected void update(Property property, Object defaultValue) { + if (property.hasDefault()) { + propertyChanged(property); + } else if (property.hasValue()) { + property.setDefault(defaultValue); + propertyChanged(property); + } else { + property.setDefault(defaultValue); + } + } + + public static String getName(Class clazz) { + try { + Field field = clazz.getDeclaredField("NAME"); + int modifiers = field.getModifiers(); + if (!Modifier.isStatic(modifiers)) { + throw new RuntimeException("TYPES must be static"); + } else if (!Modifier.isFinal(modifiers)) { + throw new RuntimeException("TYPES must be final"); + } + if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { + return (String) field.get(null); + } + } catch (Exception e) { + } + + return clazz.getSimpleName(); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/GlassPane.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/GlassPane.java new file mode 100644 index 0000000..8d06cdf --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/GlassPane.java @@ -0,0 +1,552 @@ +package edu.wpi.first.smartdashboard.gui; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author Joe Grinstead + */ +public class GlassPane extends JPanel { + + private static final int DRAG_BUFFER = 5; + private boolean dragging; + private Rectangle dragStartBounds; + private Dimension dragMinSizeDelta; + private Dimension dragMaxSizeDelta; + private Point dragStartPoint; + private int dragType = -1; + private Map areas = new HashMap(); + private JPopupMenu elementMenu; + private JMenuItem resizeMenu; + private JMenu changeToMenu; + private DisplayElement selectedElement; + private DisplayElement menuElement; + private boolean showGrid = false; + + private final DashboardPanel panel; + private final DashboardFrame frame; + + GlassPane(DashboardFrame frame, DashboardPanel panel) { + this.frame = frame; + this.panel = panel; + elementMenu = new JPopupMenu(); + elementMenu.add(changeToMenu = new JMenu("Change to...")); + elementMenu.add(new JMenuItem(new PropertiesItemAction("Properties..."))); + elementMenu.add(new JMenuItem(new MoveToBackAction("Send to Back"))); + elementMenu.add(resizeMenu = new JMenuItem(new ResetSizeAction())); + elementMenu.add(new JMenuItem(new DeleteItemAction())); + + addComponentListener(new ComponentAdapter() { + + @Override + public void componentShown(ComponentEvent e) { + requestFocus(); + setShowingGrid(false); + } + }); + + addKeyListener(new KeyAdapter() { + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_SHIFT) { + setShowingGrid(true); + } + } + + @Override + public void keyReleased(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_SHIFT) { + setShowingGrid(false); + } + } + }); + + setOpaque(false); + setFocusable(true); + + GlassMouseListener listener = new GlassMouseListener(); + addMouseListener(listener); + addMouseMotionListener(listener); + } + + public void setShowingGrid(boolean showGrid) { + if (this.showGrid != showGrid) { + this.showGrid = showGrid; + repaint(); + } + } + + private DisplayElement findElementContaining(Point point) { + return panel.findElementContaining(point); + } + + private void prepareElementMenu(DisplayElement element) { + menuElement = element; + + Dimension savedSize = menuElement.getSavedSize(); + resizeMenu.setEnabled(savedSize.width != -1 || savedSize.height != -1); + + if (element instanceof Widget) { + DataType type = ((Widget) element).getType(); + + if (type == null) { + changeToMenu.setEnabled(false); + } else { + changeToMenu.setEnabled(true); + + Set> choices = DisplayElementRegistry.getWidgetsForType(type); + + changeToMenu.removeAll(); + int count = 0; + for (Class c : choices) { + if (!c.equals(element.getClass())) { + count++; + changeToMenu.add(new ChangeToAction(DisplayElement.getName(c), c)); + } + } + if (count == 0) { + changeToMenu.setEnabled(false); + } + } + } else { + changeToMenu.setEnabled(false); + } + } + + private void showEditor(DisplayElement element) { + PropertyEditor editor = frame.getPropertyEditor(); + editor.setPropertyHolder(element); + editor.setVisible(true); + } + + private void defineBounds() { + Rectangle sb = selectedElement.getBounds(); + + int ybuffer = Math.max(Math.min(sb.height / 5, DRAG_BUFFER), 1); + int xbuffer = Math.max(Math.min(sb.width / 5, DRAG_BUFFER), 1); + + areas.clear(); + + if (selectedElement.isResizable()) { + Rectangle area = new Rectangle(sb.x - xbuffer, sb.y - ybuffer, 2 * xbuffer, 2 * ybuffer); + areas.put(SwingConstants.NORTH_WEST, area); + + area = new Rectangle(sb.x + xbuffer, sb.y - ybuffer, sb.width - 2 * xbuffer, 2 * ybuffer); + areas.put(SwingConstants.NORTH, area); + + area = new Rectangle(sb.x + sb.width - xbuffer, sb.y - ybuffer, 2 * xbuffer, 2 * ybuffer); + areas.put(SwingConstants.NORTH_EAST, area); + + area = new Rectangle(sb.x + sb.width - xbuffer, sb.y + ybuffer, 2 * xbuffer, sb.height - 2 * ybuffer); + areas.put(SwingConstants.EAST, area); + + area = new Rectangle(sb.x + sb.width - xbuffer, sb.y + sb.height - ybuffer, 2 * xbuffer, 2 * ybuffer); + areas.put(SwingConstants.SOUTH_EAST, area); + + area = new Rectangle(sb.x + xbuffer, sb.y + sb.height - ybuffer, sb.width - 2 * xbuffer, 2 * ybuffer); + areas.put(SwingConstants.SOUTH, area); + + area = new Rectangle(sb.x - xbuffer, sb.y + sb.height - ybuffer, 2 * xbuffer, 2 * ybuffer); + areas.put(SwingConstants.SOUTH_WEST, area); + + area = new Rectangle(sb.x - xbuffer, sb.y + ybuffer, 2 * xbuffer, sb.height - 2 * ybuffer); + areas.put(SwingConstants.WEST, area); + + area = new Rectangle(sb.x + xbuffer, sb.y + ybuffer, sb.width - 2 * xbuffer, sb.height - 2 * ybuffer); + areas.put(SwingConstants.CENTER, area); + } else { + areas.put(SwingConstants.CENTER, sb); + } + } + + private static final Color GRID_COLOR = new Color(0, 0, 0, 40); + + @Override + protected void paintComponent(Graphics g) { + Rectangle bounds = getBounds(); + + if (selectedElement != null) { + Rectangle eb = selectedElement.getBounds(); + + g.setColor(Color.GRAY); + g.drawRoundRect(eb.x - 1, eb.y - 1, eb.width + 1, eb.height + 1, 8, 8); + } + + if (showGrid) { + g.setColor(GRID_COLOR); + + DashboardPrefs pref = frame.getPrefs(); + int[] w = pref.grid_widths.getValue(); + int[] h = pref.grid_heights.getValue(); + + int cell = -1; + for (int i = 0; i < bounds.width; i += w[cell = (cell + 1) % w.length]) { + g.drawLine(i, 0, i, bounds.height); + } + + cell = -1; + for (int i = 0; i < bounds.height; i += h[cell = (cell + 1) % h.length]) { + g.drawLine(0, i, bounds.width, i); + } + } + } + + private void setSelected(DisplayElement element) { + if (selectedElement != element) { + selectedElement = element; + if (selectedElement == null) { + areas.clear(); + } else { + defineBounds(); + } + repaint(); + } + } + + private class GlassMouseListener extends MouseAdapter { + + private int lastDW; + private int lastDH; + private int lastDX; + private int lastDY; + + private int adjust(int delta, int original, int[] cells) { + if (showGrid) { + int total = 0; + for (int cell : cells) { + total += cell; + } + + int n = (delta + original) % total; + if (n < 0) { + n += total; + } + + for (int i = 0, cumulative = 0; i < cells.length; cumulative += cells[i++]) { + if (n < cumulative + cells[i] / 2) { + return delta - n + cumulative; + } + } + + return delta - n + total; + } else { + return delta; + } + } + + private int adjustX(int value) { + return adjust(value, dragStartBounds.x, frame.getPrefs().grid_widths.getValue()); + } + + private int adjustY(int value) { + return adjust(value, dragStartBounds.y, frame.getPrefs().grid_heights.getValue()); + } + + private int adjustW(int value) { + return adjust(value, dragStartBounds.x + dragStartBounds.width, frame.getPrefs().grid_widths.getValue()); + } + + private int adjustH(int value) { + return adjust(value, dragStartBounds.y + dragStartBounds.height, frame.getPrefs().grid_heights.getValue()); + } + + private int inRange(boolean horizontal, int value) { + int min = horizontal ? dragMinSizeDelta.width : dragMinSizeDelta.height; + int max = horizontal ? dragMaxSizeDelta.width : dragMaxSizeDelta.height; + return value <= max ? value < min ? min : value : max; + } + + @Override + public void mousePressed(MouseEvent e) { + dragType = -1; + + if (selectedElement != null) { + if (e.isPopupTrigger()) { + prepareElementMenu(selectedElement); + elementMenu.show(GlassPane.this, e.getPoint().x, e.getPoint().y); + } else { + for (Map.Entry entry : areas.entrySet()) { + if (entry.getValue().contains(e.getPoint())) { + dragType = entry.getKey(); + break; + } + } + } + } + } + + @Override + public void mouseDragged(MouseEvent e) { + if (e.isMetaDown()) { + return; + } + + if (selectedElement != null && dragType != -1) { + if (!dragging) { + dragging = true; + + dragStartBounds = selectedElement.getBounds(); + dragMinSizeDelta = selectedElement.getMinimumSize(); + dragMinSizeDelta.width -= dragStartBounds.width; + dragMinSizeDelta.height -= dragStartBounds.height; + dragMaxSizeDelta = selectedElement.getMaximumSize(); + dragMaxSizeDelta.width -= dragStartBounds.width; + dragMaxSizeDelta.height -= dragStartBounds.height; + dragStartPoint = e.getPoint(); + + lastDH = lastDW = lastDX = lastDY = 0; + } + + int dx, dy, dw, dh; + dx = dy = dw = dh = 0; + + switch (dragType) { + case SwingConstants.NORTH: + dh = inRange(false, -adjustY(e.getPoint().y - dragStartPoint.y)); + dy = -dh; + break; + case SwingConstants.NORTH_EAST: + dh = inRange(false, -adjustY(e.getPoint().y - dragStartPoint.y)); + dy = -dh; + dw = inRange(true, adjustW(e.getPoint().x - dragStartPoint.x)); + break; + case SwingConstants.EAST: + dw = inRange(true, adjustW(e.getPoint().x - dragStartPoint.x)); + break; + case SwingConstants.SOUTH_EAST: + dw = inRange(true, adjustW(e.getPoint().x - dragStartPoint.x)); + dh = inRange(false, adjustH(e.getPoint().y - dragStartPoint.y)); + break; + case SwingConstants.SOUTH: + dh = inRange(false, adjustH(e.getPoint().y - dragStartPoint.y)); + break; + case SwingConstants.SOUTH_WEST: + dh = inRange(false, adjustH(e.getPoint().y - dragStartPoint.y)); + dw = inRange(true, -adjustX(e.getPoint().x - dragStartPoint.x)); + dx = -dw; + break; + case SwingConstants.WEST: + dw = inRange(true, -adjustX(e.getPoint().x - dragStartPoint.x)); + dx = -dw; + break; + case SwingConstants.NORTH_WEST: + dh = inRange(false, -adjustY(e.getPoint().y - dragStartPoint.y)); + dy = -dh; + dw = inRange(true, -adjustX(e.getPoint().x - dragStartPoint.x)); + dx = -dw; + break; + case SwingConstants.CENTER: + DashboardPrefs prefs = frame.getPrefs(); + int leading = adjust(e.getPoint().x - dragStartPoint.x, dragStartBounds.x, prefs.grid_widths.getValue()); + int trailing = adjust(e.getPoint().x - dragStartPoint.x, dragStartBounds.x + dragStartBounds.width, prefs.grid_widths.getValue()); + dx = Math.abs(leading) < Math.abs(trailing) ? leading : trailing; + leading = adjust(e.getPoint().y - dragStartPoint.y, dragStartBounds.y, prefs.grid_heights.getValue()); + trailing = adjust(e.getPoint().y - dragStartPoint.y, dragStartBounds.y + dragStartBounds.height, prefs.grid_heights.getValue()); + dy = Math.abs(leading) < Math.abs(trailing) ? leading : trailing; + break; + default: + assert false; + } + + boolean changed = false; + + if (dw != lastDW || dh != lastDH) { + changed = true; + Dimension size = selectedElement.getSavedSize(); + if (dw != lastDW) { + size.width = dragStartBounds.width + dw; + lastDW = dw; + } + if (dh != lastDH) { + size.height = dragStartBounds.height + dh; + lastDH = dh; + } + selectedElement.setSavedSize(size); + } + if (dx != lastDX || dy != lastDY) { + changed = true; + Point origin = dragStartBounds.getLocation(); + origin.translate(dx, dy); + selectedElement.setSavedLocation(origin); + lastDX = dx; + lastDY = dy; + } + + if (changed) { + panel.revalidateBacking(); + frame.repaint(); + } + } + } + + @Override + public void mouseExited(MouseEvent e) { + dragType = -1; + dragging = false; + setSelected(null); + } + + @Override + public void mouseReleased(MouseEvent e) { + if (dragging == true) { + dragging = false; + defineBounds(); + mouseMoved(e); + } else { + if (selectedElement != null) { + if (e.isPopupTrigger()) { + prepareElementMenu(selectedElement); + elementMenu.show(GlassPane.this, e.getPoint().x, e.getPoint().y); + } else if (e.getClickCount() == 2) { + showEditor(selectedElement); + } + } + } + } + + @Override + public void mouseMoved(MouseEvent e) { + DisplayElement element = findElementContaining(e.getPoint()); + if (element != selectedElement) { + if (element == null) { + boolean found = false; + for (Rectangle area : areas.values()) { + if (area.contains(e.getPoint())) { + found = true; + break; + } + } + if (!found) { + setSelected(null); + } + } else { + setSelected(element); + } + } + + if (!areas.isEmpty()) { + AreaLoop: for (Map.Entry entry : areas.entrySet()) { + Rectangle area = entry.getValue(); + if (area.contains(e.getPoint())) { + switch (entry.getKey()) { + case SwingConstants.NORTH: + setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.NORTH_EAST: + setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.EAST: + setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.SOUTH_EAST: + setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.SOUTH: + setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.SOUTH_WEST: + setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.WEST: + setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.NORTH_WEST: + setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.CENTER: + setCursor(Cursor.getDefaultCursor()); + break AreaLoop; + default: + assert false; + } + } + } + } else { + setCursor(Cursor.getDefaultCursor()); + } + } + } + + private class ChangeToAction extends AbstractAction { + + Class elementClass; + + private ChangeToAction(String string, Class elementClass) { + super(string); + this.elementClass = elementClass; + } + + public void actionPerformed(ActionEvent e) { + if (menuElement instanceof Widget) { + Widget oldElement = (Widget) menuElement; + + if (panel.getTable().containsKey(oldElement.getFieldName())) { + Object value = panel.getTable().getValue(oldElement.getFieldName()); + panel.setField(oldElement.getFieldName(), elementClass, value, oldElement.getLocation()); + } else { + panel.setField(oldElement.getFieldName(), elementClass, oldElement.getType(), null, oldElement.getLocation()); + } + } + } + } + + private class MoveToBackAction extends AbstractAction { + + private MoveToBackAction(String string) { + super(string); + } + + public void actionPerformed(ActionEvent e) { + panel.shiftToBack(menuElement); + } + } + + private class ResetSizeAction extends AbstractAction { + + private ResetSizeAction() { + super("Reset Size"); + } + + public void actionPerformed(ActionEvent e) { + menuElement.setSavedSize(new Dimension(-1, -1)); + panel.revalidateBacking(); + } + } + + /** + * Display the properties for a display element. The properties are + * displayed for a display element so they can be viewed and updated. + */ + private class PropertiesItemAction extends AbstractAction { + + private PropertiesItemAction(String string) { + super(string); + } + + public void actionPerformed(ActionEvent ae) { + showEditor(menuElement); + } + } + + private class DeleteItemAction extends AbstractAction { + + public DeleteItemAction() { + super("Remove"); + } + + public void actionPerformed(ActionEvent e) { + if (menuElement instanceof StaticWidget) { + panel.removeElement((StaticWidget) menuElement); + } else if (menuElement instanceof Widget) { + panel.removeField(((Widget) menuElement).getFieldName()); + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/MainPanel.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/MainPanel.java new file mode 100644 index 0000000..95a4855 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/MainPanel.java @@ -0,0 +1,50 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.gui; + +import java.awt.LayoutManager; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import javax.swing.JPanel; + +/** + * Contains the DashboardPanels of the SmartDashboard and + * gives functionality to swap between them. + * @author Sam + */ +public final class MainPanel extends JPanel { + + public static final HashMap panels = new HashMap(); + private static DashboardPanel currentPanel; + + public MainPanel(LayoutManager layout, DashboardPanel defaultPanel, DashboardPanel... panels) { + super(layout); + for(DashboardPanel panel : panels) + MainPanel.panels.put(panel.getName(), panel); + currentPanel = defaultPanel; + } + + public static DashboardPanel getCurrentPanel() { + return currentPanel; + } + + public static DashboardPanel getPanel(String name) { + return panels.get(name); + } + + public void setCurrentPanel(DashboardPanel panel) { + if(panels.containsValue(panel)) + currentPanel = panel; + else throw new IllegalArgumentException("Not a valid panel"); + } + + public void addPanel(String name, DashboardPanel panel) { + if(!panels.containsValue(panel)) + panels.put(name, panel); + else throw new IllegalArgumentException("That panel already exists"); + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/PropertyEditor.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/PropertyEditor.java new file mode 100644 index 0000000..0cb2c84 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/PropertyEditor.java @@ -0,0 +1,118 @@ +package edu.wpi.first.smartdashboard.gui; + +import java.awt.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.table.*; + +import edu.wpi.first.smartdashboard.properties.*; + +/** + * + * @author brad + */ +public class PropertyEditor extends JDialog { + + private JTable table; + private PropTableModel tableModel; + private Map values; + private String[] names; + + public PropertyEditor(JFrame frame) { + super(frame, true); + tableModel = new PropTableModel(); + table = new PropertiesTable(tableModel); + table.setGridColor(Color.LIGHT_GRAY); + table.setRowSelectionAllowed(false); + JScrollPane scrollPane = new JScrollPane(table); + setBounds(100, 100, 300, 400); + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(scrollPane, BorderLayout.CENTER); + } + + void setPropertyHolder(PropertyHolder data) { + if (table.isEditing()) + table.getCellEditor().stopCellEditing(); + + if (data instanceof Widget) { + this.setTitle(((Widget) data).getFieldName()); + } else { + this.setTitle("Edit Properties"); + } + values = data.getProperties(); + names = values.keySet().toArray(new String[values.size()]); + tableModel.fireTableDataChanged(); + } + + class PropertiesTable extends JTable { + + AbstractTableModel model; + + PropertiesTable(AbstractTableModel model) { + super(model); + this.model = model; + } + + @Override + public TableCellEditor getCellEditor(int row, int col) { + TableCellEditor editor = values.get(names[row]).getEditor(PropertyEditor.this); + return editor == null ? super.getCellEditor(row, col) : editor; + } + + @Override + public TableCellRenderer getCellRenderer(int row, int col) { + if (col == 0) { + return super.getCellRenderer(row, col); + } + TableCellRenderer renderer = values.get(names[row]).getRenderer(); + return renderer == null ? super.getCellRenderer(row, col) : renderer; + } + } + + class PropTableModel extends AbstractTableModel { + + public int getRowCount() { + return values.size(); + } + + public int getColumnCount() { + return 2; + } + + @Override + public String getColumnName(int i) { + if (i == 0) { + return "Property"; + } else if (i == 1) { + return "Value"; + } else { + return "Error"; + } + } + + @Override + public boolean isCellEditable(int row, int col) { + boolean editable = (col == 1); + return editable; + } + + public Object getValueAt(int row, int col) { + switch (col) { + case 0: + return names[row]; + case 1: + return values.get(names[row]).getTableValue(); + default: + assert false; + return "Bad row, col"; + } + } + + @Override + public void setValueAt(Object value, int row, int col) { + assert (col == 1); + values.get(names[row]).setValue(value); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/StaticWidget.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/StaticWidget.java new file mode 100644 index 0000000..f006f2c --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/StaticWidget.java @@ -0,0 +1,10 @@ +package edu.wpi.first.smartdashboard.gui; + + +/** + * + * @author Joe Grinstead + */ +public abstract class StaticWidget extends DisplayElement { + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/Widget.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/Widget.java new file mode 100644 index 0000000..db69afc --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/Widget.java @@ -0,0 +1,591 @@ +package edu.wpi.first.smartdashboard.gui; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.StringBindable; +import edu.wpi.first.smartdashboard.gui.elements.bindings.NumberBindable; +import edu.wpi.first.smartdashboard.gui.elements.bindings.BooleanBindable; +import java.awt.event.*; +import java.text.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.event.*; + +import org.jfree.data.general.*; + +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Joe Grinstead + */ +public abstract class Widget extends DisplayElement { + + private String name; + private DataType type; + + public void setFieldName(String name) { + this.name = name; + } + + public String getFieldName() { + return name; + } + + public void setType(DataType type) { + this.type = type; + } + + public DataType getType() { + return type; + } + + public abstract void setValue(Object value); + + public boolean supportsType(DataType type) { + return DisplayElementRegistry.supportsType(getClass(), type); + } + + public interface MultiTypeBindable extends BooleanBindable, NumberBindable, StringBindable { + } + + public static class NumberSlider extends JSlider implements NumberBindable, ComponentListener, ChangeListener { + + private double min = 0; + private double max = 100; + private double value = 0; + private int pixelWidth = 0; + private final NumberBindable bindable; + + public NumberSlider(NumberBindable bindable) { + this.bindable = bindable; + addComponentListener(this); + addChangeListener(this); + calcBounds(); + } + + @Override + public void setBindableValue(double value) { + this.value = value; + setUnscaledValue(value); + revalidate(); + repaint(); + } + + private void setUnscaledValue(double value) { + if (value < min) { + value = min; + } + if (value > max) { + value = max; + } + double percent = (value - min) / (max - min); + setValue((int) (percent * pixelWidth)); + } + + @Override + public void stateChanged(ChangeEvent e) { + if (!getValueIsAdjusting())//TODO implement better delay + { + bindable.setBindableValue(getValue() * (max - min) / pixelWidth + min); + } + } + + public void setMin(double min) { + this.min = min; + calcBounds(); + } + + public void setMax(double max) { + this.max = max; + calcBounds(); + } + + private void calcBounds() { + pixelWidth = getWidth(); + setMinimum(0); + setMaximum(pixelWidth); + setUnscaledValue(value); + } + + @Override + public void componentResized(ComponentEvent e) { + calcBounds(); + } + + @Override + public void componentMoved(ComponentEvent e) { + } + + @Override + public void componentShown(ComponentEvent e) { + } + + @Override + public void componentHidden(ComponentEvent e) { + } + } + + public static class NumberProgressBar extends JProgressBar implements NumberBindable, ComponentListener { + + private double min = 0; + private double max = 100; + private double value = 0; + private int pixelWidth = 0; + + public NumberProgressBar() { + addComponentListener(this); + calcBounds(); + } + + @Override + public void setBindableValue(double value) { + this.value = value; + setUnscaledValue(value); + revalidate(); + repaint(); + } + + private void setUnscaledValue(double value) { + if (value < min) { + value = min; + } + if (value > max) { + value = max; + } + double percent = (value - min) / (max - min); + setValue((int) (percent * pixelWidth)); + } + + public void setMin(double min) { + this.min = min; + calcBounds(); + } + + public void setMax(double max) { + this.max = max; + calcBounds(); + } + + private void calcBounds() { + pixelWidth = getWidth(); + setMinimum(0); + setMaximum(pixelWidth); + setUnscaledValue(value); + } + + @Override + public void componentResized(ComponentEvent e) { + calcBounds(); + } + + @Override + public void componentMoved(ComponentEvent e) { + } + + @Override + public void componentShown(ComponentEvent e) { + } + + @Override + public void componentHidden(ComponentEvent e) { + } + } + + public static class NumberDatasetDisplayer extends DefaultValueDataset implements NumberBindable { + + public NumberDatasetDisplayer(double defaultValue) { + super(defaultValue); + } + + @Override + public void setBindableValue(double value) { + setValue(value); + } + } + + public static class ThreadSafeTextField extends JTextField { + + public ThreadSafeTextField(String text) { + super(text); + } + + public ThreadSafeTextField() { + super(); + } + + public void setText(final String text) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + ThreadSafeTextField.super.setText(text); + } + }); + } + } + + public static abstract class EditorTextField extends ThreadSafeTextField { + + public EditorTextField() { + addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + textChanged(getText()); + } + }); + addFocusListener(new FocusListener() { + public void focusGained(FocusEvent fe) {} + public void focusLost(FocusEvent fe) { + textChanged(getText()); + } + }); + setHorizontalAlignment(JTextField.LEFT); + } + + protected abstract void textChanged(String text); + } + + public static class ThreadSafeCheckBox extends JCheckBox { + + public void setText(final boolean selected) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + ThreadSafeCheckBox.super.setSelected(selected); + } + }); + } + } + + public static abstract class BooleanCheckBox extends ThreadSafeCheckBox implements BooleanBindable { + + private boolean value; + + public BooleanCheckBox() { + value = isSelected(); + addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + boolean newValue = isSelected(); + if (value != newValue) { + if (setValue(newValue)) { + value = newValue; + } else { + resetValue(); + } + } + } + }); + } + + protected void resetValue() { + setBindableValue(value); + } + + @Override + public void setBindableValue(boolean value) { + this.value = value; + setSelected(value); + } + + protected abstract boolean setValue(boolean value); + } + + public static abstract class BooleanField extends EditorTextField implements BooleanBindable { + + private boolean value = false; + + protected void textChanged(String text) { + if (!Boolean.toString(value).equals(text)) { + boolean newValue = Boolean.parseBoolean(text); + if (value != newValue) { + if (setValue(newValue)) { + setBindableValue(newValue); + } else { + resetValue(); + } + } else { + resetValue();//reset the text but don't set the value again + } + } + } + + protected void resetValue() { + setBindableValue(value); + } + + @Override + public void setBindableValue(boolean value) { + this.value = value; + setText(Boolean.toString(value)); + } + + protected abstract boolean setValue(boolean value); + } + + public static abstract class NumberField extends EditorTextField implements NumberBindable { + + private double value = Double.NaN; + private DecimalFormat formatter = new DecimalFormat("0.000", new DecimalFormatSymbols(Locale.US)); + + protected void textChanged(String text) { + try { + double newValue = Double.parseDouble(text); + if (value != newValue) { + if (setValue(newValue)) { + value = newValue; + } else { + resetValue(); + } + } + } catch (NumberFormatException ex) { + resetValue(); + } + } + + protected void resetValue() { + if (Double.isNaN(value)) { + setText(""); + } else { + setBindableValue(value); + } + } + + @Override + public void setBindableValue(double value) { + this.value = value; + setText(formatter.format(value)); + } + + public void setFormatter(DecimalFormat formatter) { + this.formatter = formatter; + } + + protected abstract boolean setValue(double value); + } + + public static abstract class StringField extends EditorTextField implements StringBindable { + + private String value = null; + + protected void textChanged(String newValue) { + if (!value.equals(newValue)) { + if (setValue(newValue)) { + value = newValue; + } else { + resetValue(); + } + } + } + + protected void resetValue() { + if (value == null) { + setText(""); + } else { + setBindableValue(value); + } + } + + @Override + public void setBindableValue(String value) { + this.value = value; + setText(value); + } + + protected abstract boolean setValue(String value); + } + + public static class UneditableBooleanCheckBox extends BooleanCheckBox { + + { + setEnabled(false); + } + + @Override + protected boolean setValue(boolean value) { + return false; + } + } + + public static class UneditableBooleanField extends BooleanField { + + { + setEditable(false); + } + + @Override + protected boolean setValue(boolean value) { + return false; + } + } + + public static class UneditableNumberField extends NumberField { + + { + setEditable(false); + } + + @Override + protected boolean setValue(double value) { + return false; + } + } + + public static class UneditableStringField extends StringField { + + { + setEditable(false); + } + + @Override + protected boolean setValue(String value) { + return false; + } + } + + public static class TitledBorderStringDisplayer implements StringBindable { + + private final JComponent component; + + public TitledBorderStringDisplayer(JComponent component) { + this.component = component; + } + + @Override + public void setBindableValue(String value) { + component.setBorder(BorderFactory.createTitledBorder(value)); + } + } + + public static class BindableBooleanCheckBox extends BooleanCheckBox { + + private final BooleanBindable bindable; + + public BindableBooleanCheckBox(BooleanBindable bindable) { + this.bindable = bindable; + } + + @Override + protected boolean setValue(boolean value) { + bindable.setBindableValue(value); + return true; + } + } + + public static class BindableBooleanField extends BooleanField { + + private final BooleanBindable bindable; + + public BindableBooleanField(BooleanBindable bindable) { + this.bindable = bindable; + } + + @Override + protected boolean setValue(boolean value) { + bindable.setBindableValue(value); + return true; + } + } + + public static class BindableNumberField extends NumberField { + + private final NumberBindable bindable; + + + public BindableNumberField(NumberBindable bindable) { + this.bindable = bindable; + } + + @Override + protected boolean setValue(double value) { + bindable.setBindableValue(value); + return true; + } + } + + public static class BindableStringField extends StringField { + + private final StringBindable bindable; + + public BindableStringField(StringBindable bindable) { + this.bindable = bindable; + } + + @Override + protected boolean setValue(String value) { + bindable.setBindableValue(value); + return true; + } + } + + public static class BindableTableEntry implements BooleanBindable, NumberBindable, StringBindable { + + private final ITable table; + private final String key; + + public BindableTableEntry(ITable table, String key) { + this.table = table; + this.key = key; + } + + @Override + public void setBindableValue(String value) { + table.putString(key, value); + } + + @Override + public void setBindableValue(double value) { + table.putNumber(key, value); + } + + @Override + public void setBindableValue(boolean value) { + table.putBoolean(key, value); + } + } + + public static class BooleanMultiBindable implements BooleanBindable { + + private final BooleanBindable[] bindables; + + public BooleanMultiBindable(BooleanBindable... bindables) { + this.bindables = bindables; + } + + @Override + public void setBindableValue(boolean value) { + for (BooleanBindable bindable : bindables) { + bindable.setBindableValue(value); + } + } + } + + public static class NumberMultiBindable implements NumberBindable { + + private final NumberBindable[] bindables; + + public NumberMultiBindable(NumberBindable... bindables) { + this.bindables = bindables; + } + + @Override + public void setBindableValue(double value) { + for (NumberBindable bindable : bindables) { + bindable.setBindableValue(value); + } + } + } + + public static class StringMultiBindable implements StringBindable { + + private final StringBindable[] bindables; + + public StringMultiBindable(StringBindable... bindables) { + this.bindables = bindables; + } + + @Override + public void setBindableValue(String value) { + for (StringBindable bindable : bindables) { + bindable.setBindableValue(value); + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/BooleanBox.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/BooleanBox.java new file mode 100644 index 0000000..12b4db4 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/BooleanBox.java @@ -0,0 +1,47 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractValueWidget; +import java.awt.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * Implements a simple text box UI element with a name label. + * @author pmalmsten + */ +public class BooleanBox extends AbstractValueWidget { + + public static final DataType[] TYPES = {DataType.BOOLEAN}; + public final ColorProperty colorOnTrue = new ColorProperty(this, "Color to show when true", Color.GREEN); + public final ColorProperty colorOnFalse = new ColorProperty(this, "Color to show when false", Color.RED); + private JPanel valueField; + private boolean value; + + public void init() { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + JLabel nameLabel = new JLabel(getFieldName()); + valueField = new JPanel(); + valueField.setPreferredSize(new Dimension(10, 10)); + + add(valueField); + add(nameLabel); + revalidate(); + repaint(); + } + + @Override + public void setValue(final boolean value) { + this.value = value; + valueField.setBackground(value ? colorOnTrue.getValue() : colorOnFalse.getValue()); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + setValue(value);//force backgound change + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Button.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Button.java new file mode 100644 index 0000000..f95eba2 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Button.java @@ -0,0 +1,50 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Joe Grinstead + */ +public class Button extends AbstractTableWidget { + + public static final DataType[] TYPES = {ButtonType.get()}; + + @Override + public void init() { + JButton start = new JButton(getFieldName()); + + start.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + table.putBoolean("pressed", true); + } + + @Override + public void mouseReleased(MouseEvent e) { + table.putBoolean("pressed", false); + } + }); + + start.setFocusable(false); + + setLayout(new BorderLayout()); + + add(start, BorderLayout.CENTER); + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CheckBox.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CheckBox.java new file mode 100644 index 0000000..73e93bd --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CheckBox.java @@ -0,0 +1,37 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractValueWidget; +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * Implements a simple text box UI element with a name label. + * @author pmalmsten + */ +public class CheckBox extends AbstractValueWidget { + + public static final DataType[] TYPES = {DataType.BOOLEAN}; + + public final BooleanProperty editable = new BooleanProperty(this, "Editable", true); + + private EditableBooleanValueCheckBox valueField; + + public void init() { + setResizable(false); + + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + valueField = new EditableBooleanValueCheckBox(getFieldName()); + + add(valueField); + } + + @Override + public void propertyChanged(Property property) { + if (property == editable) { + valueField.setEnabled(editable.getValue()); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Chooser.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Chooser.java new file mode 100644 index 0000000..ac098cb --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Chooser.java @@ -0,0 +1,308 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.networktables2.type.StringArray; +import edu.wpi.first.wpilibj.tables.*; +import java.awt.Component; +import java.awt.event.*; +import java.util.*; +import javax.swing.*; + +/** + * + * @author Joe Grinstead + */ +public class Chooser extends AbstractTableWidget implements ITableListener +{ + + private static final String DEFAULT = "default"; + private static final String SELECTED = "selected"; + private static final String OPTIONS = "options"; + public static final DataType[] TYPES = + { + StringChooserType.get() + }; + public final BooleanProperty editable = new BooleanProperty(this, "Editable", true); + public final BooleanProperty useRadioButtons = new BooleanProperty(this, "Use Radio Buttons", true); + private Display display; + private String selection; + private StringArray choices = new StringArray(); + + @Override + public void init() + { + setResizable(false); + + selection = null; + + display = useRadioButtons.getValue() ? new RadioButtons() : new ComboBox(); + display.setChoices(choices); + } + + @Override + public void valueChanged(ITable source, String key, Object value, boolean isNew) + { + if (key.equals(OPTIONS)) + { + table.retrieveValue(OPTIONS, choices); + display.setChoices(choices); + } + //if(key.equals(DEFAULT)) + // display.setDefault(source.getString(DEFAULT)); //TODO handle change in default? + if (key.equals(SELECTED)) + { + display.setSelected(source.getString(SELECTED)); + } + } + + @Override + public void propertyChanged(Property property) + { + if (property == useRadioButtons) + { + changeChoices(); + } else + { + if (property == editable) + { + display.setEditable(editable.getValue()); + } + } + } + + private void changeChoices() + { + remove(display.getComponent()); + display = useRadioButtons.getValue() ? new RadioButtons() : new ComboBox(); + display.setChoices(choices); + } + + private abstract class Display + { + + JPanel panel = new JPanel(); + + public Display() + { + panel.setOpaque(false); + + add(panel); + } + + abstract void setEditable(boolean editable); + + abstract void setChoices(StringArray choices); + + abstract void setSelected(String selected); + + Component getComponent() + { + return panel; + } + } + + private class RadioButtons extends Display implements ActionListener + { + + JPanel groupPanel; + ButtonGroup group; + JRadioButton selected; + Map buttons; + + @Override + void setEditable(boolean editable) + { + for (JRadioButton button : buttons.values()) + { + button.setEnabled(editable); + } + } + + @Override + void setChoices(StringArray choices) + { + if (groupPanel != null) + { + panel.remove(groupPanel); + for (JRadioButton button : buttons.values()) + { + group.remove(button); + } + buttons.clear(); + } + + groupPanel = new JPanel(); + groupPanel.setOpaque(false); + groupPanel.setLayout(new BoxLayout(groupPanel, BoxLayout.Y_AXIS)); + + group = new ButtonGroup(); + + buttons = new HashMap(); + + + boolean hasSelection = false; + + for (int i = 0; i < choices.size(); i++) + { + String choice = choices.get(i); + hasSelection |= choice.equals(selection); + + JRadioButton button = new JRadioButton(choice); + buttons.put(choice, button); + group.add(button); + groupPanel.add(button); + button.setActionCommand(choice); + button.addActionListener(this); + } + + if (!hasSelection) + { + selection = null; + } + + if (table != null && table.containsKey(SELECTED)) + { + selection = table.getString(SELECTED); + } + + if (table != null && selection != null) + { + table.putString(SELECTED, selection); + selected = buttons.get(selection); + selected.setSelected(true); + } else + { + if (table != null && table.containsKey(DEFAULT)) + { + selected = buttons.get(table.getString(DEFAULT)); + selected.setSelected(true); + } else + { + selected = null; + } + } + + setEnabled(editable.getValue()); + + panel.add(groupPanel); + + revalidate(); + repaint(); + + setSize(getPreferredSize()); + } + + @Override + void setSelected(String selected) + { + buttons.get(selected).setSelected(true); + } + + public void actionPerformed(ActionEvent e) + { + String userChoice = e.getActionCommand(); + if (selection == null || !selection.equals(userChoice)) + { + selection = userChoice; + table.putString(SELECTED, selection); + } + } + } + + private class ComboBox extends Display implements ItemListener + { + + JComboBox combo; + + @Override + void setEditable(boolean editable) + { + if (combo != null) + { + combo.setEnabled(editable); + } + } + + @Override + void setChoices(StringArray choices) + { + if (combo != null) + { + panel.remove(combo); + combo.removeItemListener(this); + } + + combo = new JComboBox(); + + synchronized (table) + { + boolean hasSelection = false; + + for (int i = 0; i < choices.size(); i++) + { + String choice = choices.get(i); + hasSelection |= choice.equals(selection); + combo.addItem(choice); + } + + if (!hasSelection) + { + selection = null; + } + + if (table.containsKey(SELECTED)) + { + selection = table.getString(SELECTED); + } + + if (selection != null) + { + combo.setSelectedItem(selection); + table.putString(SELECTED, selection); + } else + { + if (table.containsKey(DEFAULT)) + { + combo.setSelectedItem(table.getString(DEFAULT)); + } + } + } + + panel.add(combo); + + combo.addItemListener(this); + + combo.setEnabled(editable.getValue()); + + revalidate(); + repaint(); + + setSize(getPreferredSize()); + } + + @Override + void setSelected(String selected) + { + if (combo != null) + { + combo.setSelectedItem(selected); + } + } + + public void itemStateChanged(ItemEvent e) + { + if (e.getStateChange() == ItemEvent.SELECTED) + { + String userChoice = (String) e.getItem(); + if (!userChoice.equals(selection)) + { + selection = userChoice; + table.putString(SELECTED, selection); + } + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Command.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Command.java new file mode 100644 index 0000000..941cd99 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Command.java @@ -0,0 +1,91 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Jeff Copeland + */ +public class Command extends AbstractTableWidget { + + public static final DataType[] TYPES = {CommandType.get()}; + private static final String START_CARD = "Start"; + private static final String CANCEL_CARD = "Cancel"; + public final ColorProperty startBackground = new ColorProperty(this, "Start Button Color", new Color(32, 134, 32)); + public final ColorProperty cancelBackground = new ColorProperty(this, "Cancel Button Color", new Color(243, 32, 32)); + private JLabel name; + private JPanel buttonPanel; + private CardLayout layout; + private JButton start; + private JButton cancel; + + @Override + public void init() { + setResizable(false); + + buttonPanel = new JPanel(layout = new CardLayout()); + buttonPanel.setOpaque(false); + + start = new JButton("start"); + start.setOpaque(false); + start.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + table.putBoolean("running", true); + } + }); + start.setForeground(startBackground.getValue()); + + buttonPanel.add(start, START_CARD); + + cancel = new JButton("cancel"); + cancel.setOpaque(false); + cancel.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + table.putBoolean("running", false); + } + }); + cancel.setForeground(cancelBackground.getValue()); + + buttonPanel.add(cancel, CANCEL_CARD); + + name = new JLabel(getFieldName()); + add(name); + add(buttonPanel); + } + + @Override + public void booleanChanged(ITable source, String key, final boolean value, boolean isNew) { + if(key.equals("running")){ + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (value) + layout.show(buttonPanel, CANCEL_CARD); + else + layout.show(buttonPanel, START_CARD); + revalidate(); + repaint(); + } + }); + } + } + + @Override + public void propertyChanged(Property property) { + if (property == startBackground) { + start.setBackground(startBackground.getValue()); + } else if (property == cancelBackground) { + cancel.setBackground(cancelBackground.getValue()); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CommandButton.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CommandButton.java new file mode 100644 index 0000000..5af2c87 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CommandButton.java @@ -0,0 +1,46 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Joe Grinstead + */ +public class CommandButton extends AbstractTableWidget { + + public static final DataType[] TYPES = {CommandType.get()}; + + @Override + public void init() { + JButton start = new JButton(getFieldName()); + start.addActionListener(new ActionListener(){ + @Override + public void actionPerformed(ActionEvent e) { + table.putBoolean("running", true); + } + }); + + start.setFocusable(false); + + setLayout(new BorderLayout()); + + add(start, BorderLayout.CENTER); + + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + //no properties + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Compass.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Compass.java new file mode 100644 index 0000000..af59d27 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Compass.java @@ -0,0 +1,82 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.NumberBindable; +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import java.awt.*; + +import javax.swing.*; + +import org.jfree.chart.*; +import org.jfree.chart.plot.*; +import org.jfree.data.general.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Paul + */ +public class Compass extends AbstractTableWidget implements NumberBindable { + + public static final DataType[] TYPES = {DataType.NUMBER, GyroType.get()}; + public final DoubleProperty circumference = new DoubleProperty(this, "Circumference", 360.0); + public final ColorProperty ringColor = new ColorProperty(this, "Ring Color", Color.YELLOW); + private JPanel chartPanel; + private DefaultValueDataset data = new DefaultValueDataset(0); + private CompassPlot m_compass; + + @Override + public void init() { + setLayout(new BorderLayout()); + + m_compass = new CompassPlot(data); + m_compass.setSeriesNeedle(7); + m_compass.setSeriesPaint(0, Color.RED); + m_compass.setSeriesOutlinePaint(0, Color.RED); + + JFreeChart chart = new JFreeChart(getFieldName(), JFreeChart.DEFAULT_TITLE_FONT, m_compass, false); + chartPanel = new ChartPanel(chart); + chartPanel.setPreferredSize(new Dimension(250, 150)); + + add(chartPanel, BorderLayout.CENTER); + +// System.out.println(m_compass.getPlotType()); + + revalidate(); + repaint(); + } + + public void setValue(double value){ + if(table!=null) + setValue((ITable)null); + updateValue(value); + } + @Override + public void doubleChanged(ITable source, String key, double value, boolean isNew) { + if(key.equals("angle") || key.equals("Value")) + updateValue(value); + } + + public void updateValue(double value){ + data.setValue(value + m_compass.getRevolutionDistance() / 2); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == circumference) { + double compassUncorrectedValue = (data.getValue()).doubleValue() - m_compass.getRevolutionDistance() / 2; + m_compass.setRevolutionDistance(circumference.getValue()); + updateValue(compassUncorrectedValue); + } else if (property == ringColor) { + m_compass.setRosePaint(ringColor.getValue()); + } + } + + public void setBindableValue(double value) { + doubleChanged(table, "Value", value, true); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ConnectionIndicator.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ConnectionIndicator.java new file mode 100644 index 0000000..da18f20 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ConnectionIndicator.java @@ -0,0 +1,113 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import java.awt.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.robot.Robot; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Joe Grinstead + */ +public class ConnectionIndicator extends StaticWidget implements IRemoteConnectionListener { + + public static final String NAME = "Connection Indicator"; + private static final int DRAW_EMBOSSED = 0; + private static final int DRAW_ENGRAVED = 1; + private static final int DRAW_ROUNDED = 2; + private static final int DRAW_PLAIN = 3; + public final ColorProperty positive = new ColorProperty(this, "Connection Color", Color.GREEN); + public final ColorProperty negative = new ColorProperty(this, "No Connection Color", Color.RED); + public final MultiProperty display = new MultiProperty(this, "Graphics"); + private boolean firstRun = true; + private boolean connected = false; + private Runnable repainter = new Runnable() { + + public void run() { + repaint(); + } + }; + + public ConnectionIndicator() { + setPreferredSize(new Dimension(32, 32)); + + display.add("Embossed", DRAW_EMBOSSED); + display.add("Engraved", DRAW_ENGRAVED); + display.add("Rounded", DRAW_ROUNDED); + display.add("Simple", DRAW_PLAIN); + display.setDefault("Embossed"); + } + + @Override + public void init() { + Robot.addConnectionListener(this, true); + } + + @Override + public void disconnect() { + Robot.removeConnectionListener(this); + } + + @Override + public void propertyChanged(Property property) { + if (property == positive && connected) { + repaint(); + } else if (property == negative && !connected) { + repaint(); + } else if (property == display) { + repaint(); + } + } + + @Override + protected void paintComponent(Graphics g) { + Dimension size = getSize(); + + switch ((Integer) display.getValue()) { + case DRAW_EMBOSSED: + g.setColor(connected ? positive.getValue() : negative.getValue()); + g.fill3DRect(0, 0, size.width, size.height, true); + break; + case DRAW_ENGRAVED: + g.setColor(connected ? positive.getValue() : negative.getValue()); + g.fill3DRect(0, 0, size.width, size.height, false); + break; + case DRAW_ROUNDED: + g.setColor(getBackground()); + g.fillRect(0, 0, size.width, size.height); + g.setColor(connected ? positive.getValue() : negative.getValue()); + g.fillRoundRect(0, 0, size.width, size.height, 8, 8); + break; + case DRAW_PLAIN: + default: + g.setColor(connected ? positive.getValue() : negative.getValue()); + g.fillRect(0, 0, size.width, size.height); + } + } + + @Override + public void connected(IRemote remote) { + if (!connected) { + connected = true; + if (!firstRun) { + SwingUtilities.invokeLater(repainter); + } + } + firstRun = false; + } + + @Override + public void disconnected(IRemote remote) { + if (connected) { + connected = false; + if (!firstRun) { + SwingUtilities.invokeLater(repainter); + } + } + firstRun = false; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/DefaultDisplayElementRegistrar.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/DefaultDisplayElementRegistrar.java new file mode 100644 index 0000000..f7662df --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/DefaultDisplayElementRegistrar.java @@ -0,0 +1,35 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.types.DisplayElementRegistry; + +/** + * + * @author Joe Grinstead + */ +public class DefaultDisplayElementRegistrar { + + public static void init() { + DisplayElementRegistry.registerWidget(CommandButton.class); + DisplayElementRegistry.registerWidget(BooleanBox.class); + DisplayElementRegistry.registerWidget(Compass.class); + DisplayElementRegistry.registerWidget(Button.class); + DisplayElementRegistry.registerWidget(FormattedField.class); + DisplayElementRegistry.registerWidget(LinePlot.class); + DisplayElementRegistry.registerWidget(ProgressBar.class); + DisplayElementRegistry.registerWidget(SimpleDial.class); + DisplayElementRegistry.registerWidget(TextBox.class); + DisplayElementRegistry.registerWidget(CheckBox.class); + DisplayElementRegistry.registerWidget(PIDEditor.class); + DisplayElementRegistry.registerWidget(Chooser.class); + DisplayElementRegistry.registerWidget(Subsystem.class); + DisplayElementRegistry.registerWidget(Command.class); + DisplayElementRegistry.registerWidget(Scheduler.class); + + DisplayElementRegistry.registerStaticWidget(Image.class); + DisplayElementRegistry.registerStaticWidget(ConnectionIndicator.class); + DisplayElementRegistry.registerStaticWidget(Label.class); + DisplayElementRegistry.registerStaticWidget(RobotPreferences.class); + DisplayElementRegistry.registerStaticWidget(VideoStreamViewerExtension.class); + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/FormattedField.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/FormattedField.java new file mode 100644 index 0000000..ce1fc62 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/FormattedField.java @@ -0,0 +1,63 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import java.awt.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * Implements a simple text box UI element with a name label. + * @author pmalmsten + */ +public class FormattedField extends Widget { + + public static final DataType[] TYPES = {DataType.BASIC}; + + // All three defaults will change once the init method is called + public final ColorProperty foreground = new ColorProperty(this, "Foreground"); + public final ColorProperty background = new ColorProperty(this, "Background"); + public final IntegerProperty fontSize = new IntegerProperty(this, "Font Size", 12); + + + protected JFormattedTextField valueField; + + public void init() { + setLayout(new BorderLayout()); + + JLabel nameLabel = new JLabel(getFieldName()); + valueField = new JFormattedTextField(); + + update(foreground, valueField.getForeground()); + update(background, valueField.getBackground()); + update(fontSize, valueField.getFont().getSize()); + + valueField.setEditable(false); + valueField.setColumns(10); + + add(nameLabel, BorderLayout.LINE_START); + add(valueField, BorderLayout.CENTER); + revalidate(); + repaint(); + } + + @Override + public void setValue(Object value) { + valueField.setValue(value); + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == foreground) { + valueField.setForeground(foreground.getValue()); + } else if (property == background) { + valueField.setBackground(background.getValue()); + } else if (property == fontSize) { + valueField.setFont(new Font(valueField.getFont().getName(), valueField.getFont().getStyle(), fontSize.getValue())); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Image.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Image.java new file mode 100644 index 0000000..7e6632b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Image.java @@ -0,0 +1,73 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import java.awt.*; +import java.awt.image.*; +import java.io.*; + +import javax.imageio.*; +import javax.swing.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; + +/** + * + * @author Jeff Copeland + */ +public class Image extends StaticWidget { + + public final FileProperty img = new FileProperty(this, "Image File"); + public final BooleanProperty aspectKept = new BooleanProperty(this, "Maintain Aspect Ratio", false); + private BufferedImage image; + + public Image() { + setObstruction(false); + } + + @Override + public void init() { + update(img, "."); + img.addExtensionFilter("JPEG", ".jpg"); + img.addExtensionFilter("GIF", ".gif"); + img.addExtensionFilter("PNG", ".png"); + img.addExtensionFilter("Bitmap", ".bmp"); + img.addExtensionFilter("JPEG", ".jpeg"); + if (image == null) { + setPreferredSize(new Dimension(100, 100)); + } + } + + @Override + public void propertyChanged(Property property) { + if (property == img) { + try { + image = ImageIO.read(new File(img.getValue())); + setPreferredSize(new Dimension(image.getWidth(), image.getHeight())); + } catch (IOException e) { + image = null; + JOptionPane.showMessageDialog(this, "Invalid File Type.", "Input Error", JOptionPane.WARNING_MESSAGE); + setPreferredSize(new Dimension(100, 100)); + } + revalidate(); + repaint(); + } + } + + @Override + protected void paintComponent(Graphics g) { + if (image == null) { + g.fillRect(0, 0, getWidth(), getHeight()); + } else { + if (aspectKept.getValue()) { + int width = getBounds().width; + int height = getBounds().height; + double scale = Math.min((double) width / (double) image.getWidth(), (double) height / (double) image.getHeight()); + g.drawImage(image, (int) (width - (scale * image.getWidth())) / 2, (int) (height - (scale * image.getHeight())) / 2, + (int) ((width + scale * image.getWidth()) / 2), (int) (height + scale * image.getHeight()) / 2, + 0, 0, image.getWidth(), image.getHeight(), null); + } else { + g.drawImage(image, 0, 0, getWidth(), getHeight(), 0, 0, image.getWidth(), image.getHeight(), null); + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Label.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Label.java new file mode 100644 index 0000000..0d91cf1 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Label.java @@ -0,0 +1,56 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import java.awt.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; + +/** + * + * @author Joe Grinstead + */ +public class Label extends StaticWidget { + + public final StringProperty text = new StringProperty(this, "Text", "Label"); + public final MultiProperty horizontal = new MultiProperty(this, "Horizontal Alignment"); + public final MultiProperty vertical = new MultiProperty(this, "Vertical Alignment"); + + private JLabel label; + + public Label() { + horizontal.add("Left", SwingConstants.LEFT); + horizontal.add("Center", SwingConstants.CENTER); + horizontal.add("Right", SwingConstants.RIGHT); + horizontal.setDefault("Center"); + + vertical.add("Up", SwingConstants.TOP); + vertical.add("Center", SwingConstants.CENTER); + vertical.add("Down", SwingConstants.BOTTOM); + vertical.setDefault("Center"); + } + + @Override + public void init() { + setLayout(new BorderLayout()); + + label = new JLabel(text.getValue()); + + label.setHorizontalAlignment((Integer) horizontal.getValue()); + label.setVerticalAlignment((Integer) vertical.getValue()); + + add(label, BorderLayout.CENTER); + } + + @Override + public void propertyChanged(Property property) { + if (property == text) { + label.setText(text.getValue()); + } else if (property == horizontal) { + label.setHorizontalAlignment((Integer) horizontal.getValue()); + } else if (property == vertical) { + label.setVerticalAlignment((Integer) vertical.getValue()); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/LinePlot.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/LinePlot.java new file mode 100644 index 0000000..0fe49d5 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/LinePlot.java @@ -0,0 +1,76 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractValueWidget; +import java.awt.*; + +import javax.swing.*; + +import org.jfree.chart.*; +import org.jfree.chart.plot.*; +import org.jfree.data.xy.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author pmalmsten + */ +public class LinePlot extends AbstractValueWidget { + + public static final DataType[] TYPES = {DataType.NUMBER}; + public final IntegerProperty bufferSize = new IntegerProperty(this, "Buffer Size (samples)", 5000); + JPanel m_chartPanel; + XYSeries m_data; + XYDataset m_dataset; + JFreeChart m_chart; + int m_timeUnit = 0; + + @Override + public void init() { + setLayout(new BorderLayout()); + + m_data = new XYSeries(getFieldName()); + m_dataset = new XYSeriesCollection(m_data); + + JFreeChart chart = ChartFactory.createXYLineChart( + getFieldName(), + "Time (units)", + "Data", + m_dataset, + PlotOrientation.VERTICAL, + false, + true, + false); + + m_chartPanel = new ChartPanel(chart); + m_chartPanel.setPreferredSize(new Dimension(400, 300)); + m_chartPanel.setBackground(getBackground()); + + add(m_chartPanel, BorderLayout.CENTER); + revalidate(); + repaint(); + } + + @Override + public void setValue(double value) {//TODO make sample in thread instead of relying on set value (so that the widget has even time scale) + m_data.add(m_timeUnit++, value); + + if (m_data.getItemCount() > bufferSize.getValue()) { + m_data.remove(0); + } + + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == bufferSize) { + + while (m_data.getItemCount() > bufferSize.getValue()) { + m_data.remove(0); + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/PIDEditor.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/PIDEditor.java new file mode 100644 index 0000000..f7a0cac --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/PIDEditor.java @@ -0,0 +1,114 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.livewindow.elements.NameTag; +import java.awt.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; + +/** + * + * @author Joe Grinstead + */ +public class PIDEditor extends AbstractTableWidget { + + public static final DataType[] TYPES = {PIDType.get()}; + + private NumberTableField pField; + private NumberTableField iField; + private NumberTableField dField; + private NumberTableField fField; + private NumberTableField sField; + private BooleanTableCheckBox eBox; + private JLabel pLabel; + private JLabel iLabel; + private JLabel dLabel; + private JLabel fLabel; + private JLabel sLabel; + private JLabel eLabel; + + public PIDEditor() {//TODO alert user when the robot is about reset modified PID values + setLayout(new GridBagLayout()); + + pLabel = new JLabel("P:"); + iLabel = new JLabel("I:"); + dLabel = new JLabel("D:"); + fLabel = new JLabel("F:"); + sLabel = new JLabel("Setpoint:"); + eLabel = new JLabel("Enabled:"); + pLabel.setHorizontalAlignment(JLabel.RIGHT); + iLabel.setHorizontalAlignment(JLabel.RIGHT); + dLabel.setHorizontalAlignment(JLabel.RIGHT); + fLabel.setHorizontalAlignment(JLabel.RIGHT); + sLabel.setHorizontalAlignment(JLabel.RIGHT); + eLabel.setHorizontalAlignment(JLabel.RIGHT); + + pField = new NumberTableField("p"); + iField = new NumberTableField("i"); + dField = new NumberTableField("d"); + fField = new NumberTableField("f"); + sField = new NumberTableField("setpoint"); + eBox = new BooleanTableCheckBox("enabled"); + + int columns = 10; + pField.setColumns(columns); + iField.setColumns(columns); + dField.setColumns(columns); + fField.setColumns(columns); + sField.setColumns(columns); + + + GridBagConstraints c = new GridBagConstraints(); + + + c.gridy = 1; + add(pLabel, c); + c.gridy = 2; + add(iLabel, c); + c.gridy = 3; + add(dLabel, c); + c.gridy = 4; + add(fLabel, c); + c.gridy = 5; + add(sLabel, c); + c.gridy = 6; + add(eLabel, c); + + c.gridx = 1; + c.weightx = 1.0; + c.gridy = 0; + c.fill = GridBagConstraints.HORIZONTAL; + add(nameTag = new NameTag(""), c); + nameTag.setHorizontalAlignment(JLabel.LEFT); + c.gridy = 1; + add(pField, c); + c.gridy = 2; + add(iField, c); + c.gridy = 3; + add(dField, c); + c.gridy = 4; + add(fField, c); + c.gridy = 5; + add(sField, c); + c.gridy = 6; + add(eBox, c); + + setMaximumSize(new Dimension(Integer.MAX_VALUE, getPreferredSize().height)); + + revalidate(); + repaint(); + } + + @Override + public void init(){ + nameTag.setText(getFieldName()); + } + + @Override + public void propertyChanged(Property property) { + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ProgressBar.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ProgressBar.java new file mode 100644 index 0000000..29480c6 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ProgressBar.java @@ -0,0 +1,59 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractValueWidget; +import java.awt.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author brad (Heavily modified by Alex Henning) + */ +public class ProgressBar extends AbstractValueWidget { + public static final DataType[] TYPES = {DataType.NUMBER}; + + public final ColorProperty foreground = new ColorProperty(this, "Foreground"); + public final ColorProperty background = new ColorProperty(this, "Background"); + public final DoubleProperty max = new DoubleProperty(this, "Maximum", 100); + public final DoubleProperty min = new DoubleProperty(this, "Minimum", 0); + + private NumberProgressBar progressBar; + + @Override + public void init() { + progressBar = new NumberProgressBar(); + progressBar.setMin(min.getValue()); + progressBar.setMax(max.getValue()); + progressBar.setBorderPainted(false); + progressBar.setBounds(progressBar.getX(), progressBar.getY(), + progressBar.getX() + 200, progressBar.getY() + 40); + setNumberBinding(progressBar); + + setLayout(new BorderLayout()); + add(new JLabel(getFieldName()), BorderLayout.PAGE_START); + add(progressBar, BorderLayout.CENTER); + + + update(foreground, progressBar.getForeground()); + update(background, progressBar.getBackground()); + + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == foreground) { + progressBar.setForeground(foreground.getValue()); + } else if (property == background) { + progressBar.setBackground(background.getValue()); + } else if (property == min) { + progressBar.setMin(min.getValue()); + } else if (property == max) { + progressBar.setMax(max.getValue()); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/RobotPreferences.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/RobotPreferences.java new file mode 100644 index 0000000..9cb35b2 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/RobotPreferences.java @@ -0,0 +1,302 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.table.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.robot.Robot; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Joe Grinstead + */ +public class RobotPreferences extends StaticWidget implements ITableListener { + + public static final String DELETED_VALUE = "\""; + public static final String NAME = "Robot Preferences"; + private JTable table; + private PreferenceTableModel model; + private Map values; + private JButton save; + private JButton add; + private JButton remove; + + @Override + public void init() { + add = new JButton("Add"); + add.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + NewPreferenceEntryDialog dialog = new NewPreferenceEntryDialog(); + dialog.show(remove.getLocationOnScreen()); + if (!dialog.isCanceled()) { + model.put(dialog.getKey(), dialog.getValue()); + } + } + }); + + remove = new JButton("Remove"); + remove.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + if (table.isEditing()) { + table.getCellEditor().cancelCellEditing(); + } + Map.Entry entry = model.getRow(table.getSelectedRow()); + if (entry != null) { + model.delete(entry.getKey()); + } + } + }); + + save = new JButton("Save"); + save.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + Robot.getPreferences().putBoolean(Robot.PREF_SAVE_FIELD, true); + } + }); + + values = new LinkedHashMap(); + + Robot.getPreferences().addTableListener(this, true); + + model = new PreferenceTableModel(); + + table = new JTable(model); + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + table.getTableHeader().setReorderingAllowed(false); + + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new GridLayout(0, 2)); + buttonPanel.add(add); + buttonPanel.add(remove); + + JPanel controlPanel = new JPanel(); + controlPanel.setLayout(new BorderLayout()); + controlPanel.add(buttonPanel, BorderLayout.NORTH); + controlPanel.add(save, BorderLayout.SOUTH); + + setLayout(new BorderLayout()); + JScrollPane tableScrollPane = new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + add(tableScrollPane, BorderLayout.CENTER); + add(controlPanel, BorderLayout.SOUTH); + + setPreferredSize(new Dimension(300, 200)); + } + + @Override + public void disconnect() { + Robot.getPreferences().removeTableListener(this); + } + + @Override + public void propertyChanged(Property property) { + } + + @Override + public void valueChanged(ITable source, String key, Object value, boolean isNew) { + if (key.equals(Robot.PREF_SAVE_FIELD)) { + save.setEnabled(!(Boolean) value); + } else { + if (DELETED_VALUE.equals(value.toString())) { + values.remove(key); + } else { + values.put(key, value.toString()); + } + } + + if (model != null) { + model.fireTableDataChanged(); + } + } + + + private class PreferenceTableModel extends AbstractTableModel { + + public int getRowCount() { + return values.size(); + } + + public int getColumnCount() { + return 2; + } + + @Override + public String getColumnName(int i) { + if (i == 0) { + return "Key"; + } else if (i == 1) { + return "Value"; + } else { + return "ERROR"; + } + } + + public Map.Entry getRow(int rowIndex) { + int row = 0; + for (Map.Entry entry : values.entrySet()) { + if (row++ == rowIndex) { + return entry; + } + } + return null; + } + + public boolean put(String key, String value) { + if (validateKey(key) && validateValue(value)) { + Robot.getPreferences().putString(key, value); + return true; + } + return false; + } + + public void delete(String key) { + Robot.getPreferences().putString(key, DELETED_VALUE); + } + + public boolean validateKey(String key) { + if (key.isEmpty()) { + JOptionPane.showMessageDialog(RobotPreferences.this, "The key cannot be empty", "Bad Key", JOptionPane.ERROR_MESSAGE); + return false; + } + if (key.contains(" ") || key.contains("=") || key.contains("\t") || key.contains("\r") || key.contains("\n")) { + JOptionPane.showMessageDialog(RobotPreferences.this, "The key cannot containt ' ', '=', tabs or newlines", "Bad Key", JOptionPane.ERROR_MESSAGE); + return false; + } + return true; + } + + public boolean validateValue(String value) { + if (value.contains("\"")) { + JOptionPane.showMessageDialog(RobotPreferences.this, "The value cannot contain '\"'", "Bad Value", JOptionPane.ERROR_MESSAGE); + return false; + } + return true; + } + + @Override + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + if (columnIndex == 0) {//Key + Map.Entry entry = getRow(rowIndex); + if (entry != null) { + String oldName = entry.getKey(); + String value = entry.getValue(); + if (!oldName.equals(aValue.toString())) { + if (!values.containsKey(aValue.toString())) { + if(put(aValue.toString(), value)) + delete(oldName); + } else { + JOptionPane.showMessageDialog(RobotPreferences.this, "An entry with the key " + aValue + " already exists", "Duplicate Key", JOptionPane.ERROR_MESSAGE); + } + } + } + } else {//Value + Map.Entry entry = getRow(rowIndex); + if (entry != null) { + put(entry.getKey(), aValue.toString()); + } + } + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return rowIndex >= 0; + } + + public Object getValueAt(int rowIndex, int columnIndex) { + Map.Entry entry = getRow(rowIndex); + if (entry != null) { + return columnIndex == 0 ? entry.getKey() : entry.getValue(); + } + return "ERROR"; + } + } + + private class NewPreferenceEntryDialog extends JDialog { + + private JTextField keyField; + private JTextField valueField; + private JButton addButton; + private JButton cancelButton; + boolean canceled = true; + + public NewPreferenceEntryDialog() { + setTitle("New Preference Entry"); + setModal(true); + setResizable(false); + ((JComponent) getContentPane()).setBorder(BorderFactory.createEmptyBorder(7, 7, 7, 7)); + + setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + + add(new JLabel("Key: "), c); + c.gridx = 1; + add(keyField = new JTextField(10), c); + + c.gridx = 0; + c.gridy = 1; + add(new JLabel("Value: "), c); + c.gridx = 1; + add(valueField = new JTextField(10), c); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new GridLayout(0, 2)); + buttonPanel.add(addButton = new JButton("Add"), c); + addButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + if (!values.containsKey(getKey())) { + if (model.validateKey(getKey()) && model.validateValue(getValue())) { + canceled = false; + dispose(); + } + } else { + JOptionPane.showMessageDialog(RobotPreferences.this, "An entry with the key " + getKey() + " already exists", "Duplicate Key", JOptionPane.ERROR_MESSAGE); + } + } + }); + getRootPane().setDefaultButton(addButton); + + buttonPanel.add(cancelButton = new JButton("Cancel"), c); + cancelButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + dispose(); + } + }); + + c.gridx = 0; + c.gridy = 2; + c.gridwidth = 2; + add(buttonPanel, c); + + pack(); + } + + public void show(Point center) { + setLocation((int) (center.getX() - getWidth() / 2), (int) (center.getY() - getHeight() / 2)); + setVisible(true); + } + + public boolean isCanceled() { + return canceled; + } + + public String getKey() { + return keyField.getText(); + } + + public String getValue() { + return valueField.getText(); + } + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Scheduler.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Scheduler.java new file mode 100644 index 0000000..d61e052 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Scheduler.java @@ -0,0 +1,157 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.List; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.networktables2.type.NumberArray; +import edu.wpi.first.wpilibj.networktables2.type.StringArray; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Jeff Copeland + */ +public class Scheduler extends Widget { + + public static final DataType[] TYPES = {SchedulerType.get()}; + private static final String NO_COMMAND_CARD = "No Command"; + private static final String COMMAND_CARD = "Commands"; + private int count = 0; + private JLabel noCommands; + private JPanel commandLabels; + private JPanel cancelButtons; + private JPanel commandPanel; + private List labels; + private List buttons; + private ITable table; + private GridLayout commandLayout; + private GridLayout cancelLayout; + private CardLayout cardLayout; + + private StringArray commands = new StringArray(); + private NumberArray ids = new NumberArray(); + private NumberArray toCancel = new NumberArray(); + + private ITableListener listener = new ITableListener() { + + boolean running = false; + + @Override + public void valueChanged(ITable source, String key, Object value, boolean isNew) { + if (running) { + return; + } + running = true; + SwingUtilities.invokeLater(new Runnable() { + + public void run() { + synchronized (table) { + table.retrieveValue("Names", commands); + table.retrieveValue("Ids", ids); + assert commands.size() == ids.size(); + + // Update displayed commands + for (int i = 0; i < commands.size(); i++) { + if (i >= labels.size()) { + labels.add(new JLabel()); + } + JLabel label = labels.get(i); + label.setText(commands.get(i)); + + if (i >= buttons.size()) { + JButton button = new JButton("cancel"); + final int index = i; + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + // Cancel commands + table.retrieveValue("Cancel", toCancel); + toCancel.add(ids.get(index)); + table.putValue("Cancel", toCancel); + } + }); + buttons.add(button); + } + JButton button = buttons.get(i); + + if (i > count - 1) { + commandLabels.add(label); + cancelButtons.add(button); + } + } + } + + // Remove leftover widgets + if (count > commands.size()) { + for (int i = commands.size(); i < count; i++) { + commandLabels.remove(labels.get(i)); + cancelButtons.remove(buttons.get(i)); + } + } + + count = commands.size(); + + cardLayout.show(Scheduler.this, count == 0 ? NO_COMMAND_CARD : COMMAND_CARD); + + running = false; + } + }); + } + }; + + @Override + public void setValue(Object value) { + if (table != null) { + table.removeTableListener(listener); + } + table = (ITable) value; + table.addTableListener(listener, true); + + revalidate(); + repaint(); + } + + @Override + public void init() { + setLayout(cardLayout = new CardLayout()); + + labels = new ArrayList(); + buttons = new ArrayList(); + + commandPanel = new JPanel(); + commandPanel.setLayout(new GridLayout(0, 2)); + + commandLabels = new JPanel(); + cancelButtons = new JPanel(); + + commandPanel.add(commandLabels, BorderLayout.WEST); + commandPanel.add(cancelButtons, BorderLayout.CENTER); + + commandLayout = new GridLayout(0, 1); + cancelLayout = new GridLayout(0, 1); + + commandLabels.setLayout(commandLayout); + cancelButtons.setLayout(cancelLayout); + + add(commandPanel, COMMAND_CARD); + + noCommands = new JLabel("No commands running."); + noCommands.setHorizontalAlignment(JLabel.CENTER); + add(noCommands, NO_COMMAND_CARD); + + cardLayout.show(this, NO_COMMAND_CARD); + + repaint(); + } + + @Override + public void propertyChanged(Property property) { + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/SimpleDial.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/SimpleDial.java new file mode 100644 index 0000000..e30b8aa --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/SimpleDial.java @@ -0,0 +1,67 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractNumberDatasetWidget; +import java.awt.*; + +import javax.swing.*; + +import org.jfree.chart.*; +import org.jfree.chart.plot.*; +import org.jfree.data.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author Paul + */ +public class SimpleDial extends AbstractNumberDatasetWidget { + + public static final DataType[] TYPES = { DataType.NUMBER }; + + public final DoubleProperty max = new DoubleProperty(this, "Upper Limit", 100); + public final DoubleProperty min = new DoubleProperty(this, "Lower Limit", 0); + public final DoubleProperty tickInterval = new DoubleProperty(this, "Tick Interval", 10); + + private final JPanel chartPanel; + private final MeterPlot m_meter; + private Range m_plotRange; + + public SimpleDial() { + super(0); + + setLayout(new BorderLayout()); + + m_meter = new MeterPlot(getDataset()); + m_plotRange = new Range(min.getValue(), max.getValue()); + m_meter.setRange(m_plotRange); + //plot.addInterval(new MeterInterval("High", new Range(80.0, 100.0))); + JFreeChart chart = new JFreeChart(getFieldName(), JFreeChart.DEFAULT_TITLE_FONT, m_meter, false); + chartPanel = new ChartPanel(chart); + chartPanel.setPreferredSize(new Dimension(250, 150)); + + propertyChanged(tickInterval); + + add(chartPanel, BorderLayout.CENTER); + } + + @Override + public void init() { + m_plotRange= new Range(min.getValue(), max.getValue()); + m_meter.setRange(m_plotRange); + m_meter.setTickSize(tickInterval.getValue()); + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == min || property == max) { + m_plotRange = new Range(min.getValue(), max.getValue()); + m_meter.setRange(m_plotRange); + } else if (property == tickInterval) { + m_meter.setTickSize(tickInterval.getValue()); + } + } +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Subsystem.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Subsystem.java new file mode 100644 index 0000000..9ca3b4b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Subsystem.java @@ -0,0 +1,74 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.gui.elements.bindings.BooleanBindable; +import edu.wpi.first.smartdashboard.gui.elements.bindings.StringBindable; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.tables.*; +import javax.swing.*; + +/** + * @author Joe Grinstead + */ +public class Subsystem extends AbstractTableWidget implements ITableListener { + + public static final DataType[] TYPES = {SubsystemType.get()}; + + public final ColorProperty background = new ColorProperty(this, "Background"); + + private SubsystemCommandField valueField; + + public void init() { + JLabel nameLabel = new JLabel(getFieldName()); + valueField = new SubsystemCommandField(); + + update(background, valueField.getBackground()); + + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + valueField.setEditable(false); + valueField.setColumns(10); + + add(nameLabel); + add(valueField); + revalidate(); + repaint(); + } + + private class SubsystemCommandField extends BindableStringField{ + private boolean hasCommand = false; + private String commandName = ""; + public SubsystemCommandField(){ + super(StringBindable.NULL); + setStringBinding("command", new StringBindable() { + + public void setBindableValue(String value) { + commandName = value; + if(hasCommand) + SubsystemCommandField.this.setBindableValue(commandName); + else + SubsystemCommandField.this.setBindableValue("---"); + } + }, "---"); + setBooleanBinding("hasCommand", new BooleanBindable() { + + public void setBindableValue(boolean value) { + hasCommand = value; + if(hasCommand) + SubsystemCommandField.this.setBindableValue(commandName); + else + SubsystemCommandField.this.setBindableValue("---"); + } + }); + } + } + + @Override + public void propertyChanged(Property property) { + if (property == background) { + valueField.setBackground(background.getValue()); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/TextBox.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/TextBox.java new file mode 100644 index 0000000..ea259f3 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/TextBox.java @@ -0,0 +1,59 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractValueWidget; +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * Implements a simple text box UI element with a name label. + * @author pmalmsten + * @author Joe Grinstead + */ +public class TextBox extends AbstractValueWidget { + + public static final DataType[] TYPES = {DataType.BASIC}; + public static final String NAME = "Text Box"; + + public final BooleanProperty editable = new BooleanProperty(this, "Editable", true); + public final ColorProperty background = new ColorProperty(this, "Background"); + + private JTextField valueField; + + public void init() { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + JLabel nameLabel = new JLabel(getFieldName()); + + if (getType().isChildOf(DataType.BOOLEAN)) + valueField = new EditableBooleanValueField(getFieldName()); + else if (getType().isChildOf(DataType.NUMBER)) + valueField = new EditableNumberValueField(getFieldName()); + else if (getType().isChildOf(DataType.STRING)) + valueField = new EditableStringValueField(getFieldName()); + else{ + valueField = new JTextField(); + valueField.setText("Unupported basic data type: "+getType()); + valueField.setEditable(false); + } + + update(background, valueField.getBackground()); + + valueField.setEditable(editable.getValue()); + valueField.setColumns(10); + + add(nameLabel); + add(valueField); + } + + @Override + public void propertyChanged(Property property) { + if (property == background) { + valueField.setBackground(background.getValue()); + } else if (property == editable) { + valueField.setEditable(editable.getValue()); + } + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/VideoStreamViewerExtension.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/VideoStreamViewerExtension.java new file mode 100644 index 0000000..c40f4c0 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/VideoStreamViewerExtension.java @@ -0,0 +1,175 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.DashboardPrefs; +import edu.wpi.first.smartdashboard.gui.StaticWidget; +import edu.wpi.first.smartdashboard.properties.IPAddressProperty; +import edu.wpi.first.smartdashboard.properties.Property; +import edu.wpi.first.smartdashboard.properties.StringProperty; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.Iterator; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; + +/** + * + * @author Greg Granito + */ +public class VideoStreamViewerExtension extends StaticWidget { + + public static final String NAME = "Simple Camera Viewer"; + + + private static final int[] START_BYTES = new int[]{0xFF, 0xD8}; + private static final int[] END_BYTES = new int[]{0xFF, 0xD9}; + + private boolean ipChanged = true; + private String ipString = null; + private long lastFPSCheck = 0; + private int lastFPS = 0; + private int fpsCounter = 0; + public class BGThread extends Thread { + + boolean destroyed = false; + + public BGThread() { + super("Camera Viewer Background"); + } + + long lastRepaint = 0; + @Override + public void run() { + URLConnection connection = null; + InputStream stream = null; + ByteArrayOutputStream imageBuffer = new ByteArrayOutputStream(); + while (!destroyed) { + try{ + System.out.println("Connecting to camera"); + ipChanged = false; + URL url = new URL("http://"+ipString+"/mjpg/video.mjpg"); + connection = url.openConnection(); + connection.setReadTimeout(250); + stream = connection.getInputStream(); + + while(!destroyed && !ipChanged){ + while(System.currentTimeMillis()-lastRepaint<10){ + stream.skip(stream.available()); + Thread.sleep(1); + } + stream.skip(stream.available()); + + imageBuffer.reset(); + for(int i = 0; i500){ + lastFPSCheck = System.currentTimeMillis(); + lastFPS = fpsCounter*2; + fpsCounter = 0; + } + + lastRepaint = System.currentTimeMillis(); + ByteArrayInputStream tmpStream = new ByteArrayInputStream(imageBuffer.toByteArray()); + imageToDraw = ImageIO.read(tmpStream); + System.out.println(System.currentTimeMillis()-lastRepaint); + repaint(); + } + + } catch(Exception e){ + imageToDraw = null; + repaint(); + e.printStackTrace(); + } + + if(!ipChanged){ + try { + Thread.sleep(500); + } catch (InterruptedException ex) {} + } + } + + } + + @Override + public void destroy() { + destroyed = true; + } + } + private BufferedImage imageToDraw; + private BGThread bgThread = new BGThread(); + private final int team = DashboardPrefs.getInstance().team.getValue(); + public final IPAddressProperty ipProperty = new IPAddressProperty(this, "Camera IP Address", new int[]{10, (DashboardPrefs.getInstance().team.getValue() / 100), (DashboardPrefs.getInstance().team.getValue() % 100), 11}); + + @Override + public void init() { + setPreferredSize(new Dimension(100, 100)); + ipString = ipProperty.getSaveValue(); + bgThread.start(); + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == ipProperty) { + ipString = ipProperty.getSaveValue(); + ipChanged = true; + } + + } + + @Override + public void disconnect() { + bgThread.destroy(); + super.disconnect(); + } + + @Override + protected void paintComponent(Graphics g) { + BufferedImage drawnImage = imageToDraw; + if (drawnImage != null) { + int width = getBounds().width; + int height = getBounds().height; + double scale = Math.min((double) width / (double) drawnImage.getWidth(), (double) height / (double) drawnImage.getHeight()); + g.drawImage(drawnImage, (int) (width - (scale * drawnImage.getWidth())) / 2, (int) (height - (scale * drawnImage.getHeight())) / 2, + (int) ((width + scale * drawnImage.getWidth()) / 2), (int) (height + scale * drawnImage.getHeight()) / 2, + 0, 0, drawnImage.getWidth(), drawnImage.getHeight(), null); + g.setColor(Color.PINK); + g.drawString("FPS: "+lastFPS, 10, 10); + } else { + g.setColor(Color.PINK); + g.fillRect(0, 0, getBounds().width, getBounds().height); + g.setColor(Color.BLACK); + g.drawString("NO CONNECTION", 10, 10); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractNumberDatasetWidget.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractNumberDatasetWidget.java new file mode 100644 index 0000000..8cf37aa --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractNumberDatasetWidget.java @@ -0,0 +1,17 @@ +package edu.wpi.first.smartdashboard.gui.elements.bindings; + +import org.jfree.data.general.*; + + +public abstract class AbstractNumberDatasetWidget extends AbstractValueWidget{ + + private final NumberDatasetDisplayer data; + protected AbstractNumberDatasetWidget(double defaultValue){ + setNumberBinding(data = new NumberDatasetDisplayer(defaultValue)); + } + + protected DefaultValueDataset getDataset(){ + return data; + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractTableWidget.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractTableWidget.java new file mode 100644 index 0000000..e20b5bb --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractTableWidget.java @@ -0,0 +1,160 @@ +package edu.wpi.first.smartdashboard.gui.elements.bindings; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.livewindow.elements.LWSubsystem; +import edu.wpi.first.smartdashboard.livewindow.elements.NameTag; +import edu.wpi.first.smartdashboard.types.DataType; +import edu.wpi.first.wpilibj.tables.*; +import java.awt.Point; +import java.util.*; + +/** + * An abstraction for creating a widget that wraps a network table + * + * + * @author Mitchell + * + */ +public abstract class AbstractTableWidget extends Widget implements ITableListener{ + protected ITable table; + + /** A NameTag for this widget. */ + public NameTag nameTag; + + private final boolean listenSubtables; + + public AbstractTableWidget(){ + this(false); + } + protected AbstractTableWidget(boolean listenSubtables){ + this.listenSubtables = listenSubtables; + } + + @Override + public void setValue(Object value) { + if(value instanceof ITable){ + ITable table = (ITable)value; + + if (table != null) + table.removeTableListener(this); + + this.table = table; + if(table!=null){ + table.addTableListener(this, true); + if(listenSubtables) + table.addSubTableListener(this); + } + } + } + + public void setField(String name, Widget element, DataType type, Object value, LWSubsystem subsystem, Point point) { + element.setFieldName(name); + element.setType(type); + element.init(); + if(value != null) { + element.setValue(value); + } + subsystem.add(element); + } + + private Map booleanFields = new HashMap(); + private Map numberFields = new HashMap(); + private Map stringFields = new HashMap(); + @Override + public void valueChanged(ITable source, String key, Object value, boolean isNew) { + if(value instanceof Boolean) + booleanChanged(source, key, ((Boolean)value).booleanValue(), isNew); + if(value instanceof Double) + doubleChanged(source, key, ((Double)value).doubleValue(), isNew); + if(value instanceof String) + stringChanged(source, key, (String)value, isNew); + if(value instanceof ITable) + tableChanged(source, key, (ITable)value, isNew); + } + + + public void booleanChanged(ITable source, String key, boolean value, boolean isNew) { + BooleanBindable field = booleanFields.get(key); + if(field!=null) + field.setBindableValue(value); + } + public void doubleChanged(ITable source, String key, double value, boolean isNew) { + NumberBindable field = numberFields.get(key); + if(field!=null) + field.setBindableValue(value); + } + public void stringChanged(ITable source, String key, String value, boolean isNew) { + StringBindable field = stringFields.get(key); + if(field!=null) + field.setBindableValue(value); + } + public void tableChanged(ITable source, String key, ITable value, boolean isNew) { + } + + + public MultiTypeBindable getTableEntryBindable(final String key){ + return new MultiTypeBindable(){ + @Override + public void setBindableValue(boolean value) { + if(table!=null) + table.putBoolean(key, value); + } + @Override + public void setBindableValue(double value) { + if(table!=null) + table.putNumber(key, value); + } + @Override + public void setBindableValue(String value) { + if(table!=null) + table.putString(key, value); + } + }; + } + + + protected void setBooleanBinding(String key, BooleanBindable displayer){ + if(booleanFields.containsKey(key))//TODO maybe remove and just let them overwrite??? + throw new RuntimeException("Cannot have multiple boolean fields for the same key: "+key); + booleanFields.put(key, displayer); + } + protected void setNumberBinding(String key, NumberBindable displayer){ + if(numberFields.containsKey(key)) + throw new RuntimeException("Cannot have multiple number fields for the same key: "+key); + numberFields.put(key, displayer); + } + protected void setStringBinding(String key, StringBindable displayer, String defaultValue){ + if(stringFields.containsKey(key)) + throw new RuntimeException("Cannot have multiple string fields for the same key: "+key); + displayer.setBindableValue(defaultValue); + stringFields.put(key, displayer); + } + + + + + public class BooleanTableCheckBox extends BindableBooleanCheckBox{ + public BooleanTableCheckBox(final String key){ + super(getTableEntryBindable(key)); + setBooleanBinding(key, this); + } + } + public class BooleanTableField extends BindableBooleanField{ + public BooleanTableField(final String key){ + super(getTableEntryBindable(key)); + setBooleanBinding(key, this); + } + } + public class NumberTableField extends BindableNumberField{ + public NumberTableField(final String key){ + super(getTableEntryBindable(key)); + setNumberBinding(key, this); + } + } + public class StringTableField extends BindableStringField{ + public StringTableField(final String key){ + super(getTableEntryBindable(key)); + setStringBinding(key, this, ""); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractValueWidget.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractValueWidget.java new file mode 100644 index 0000000..6eaf6bd --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractValueWidget.java @@ -0,0 +1,81 @@ +package edu.wpi.first.smartdashboard.gui.elements.bindings; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.robot.*; + + + + +public abstract class AbstractValueWidget extends Widget{ + + private BooleanBindable booleanDisplayer = BooleanBindable.NULL; + private NumberBindable numberDisplayer = NumberBindable.NULL; + private StringBindable stringDisplayer = StringBindable.NULL; + + @Override + public void setValue(Object value) { + if(value instanceof Boolean) + setValue(((Boolean)value).booleanValue()); + else if(value instanceof Double) + setValue(((Double)value).doubleValue()); + else if(value instanceof String) + setValue(((String)value)); + } + + + public void setBooleanBinding(BooleanBindable booleanDisplayer) { + this.booleanDisplayer = booleanDisplayer; + } + + public void setNumberBinding(NumberBindable numberDisplayer) { + this.numberDisplayer = numberDisplayer; + } + + public void setStringBinding(StringBindable stringDisplayer) { + this.stringDisplayer = stringDisplayer; + } + + + public void setValue(boolean value){ + booleanDisplayer.setBindableValue(value); + } + public void setValue(double value){ + numberDisplayer.setBindableValue(value); + } + public void setValue(String value){ + stringDisplayer.setBindableValue(value); + } + + + + + + + public class EditableBooleanValueCheckBox extends BindableBooleanCheckBox{ + public EditableBooleanValueCheckBox(final String key){ + super(new BindableTableEntry(Robot.getTable(), key)); + setBooleanBinding(this); + } + } + public class EditableBooleanValueField extends BindableBooleanField{ + public EditableBooleanValueField(final String key){ + super(new BindableTableEntry(Robot.getTable(), key)); + setBooleanBinding(this); + } + } + public class EditableNumberValueField extends BindableNumberField{ + public EditableNumberValueField(final String key){ + super(new BindableTableEntry(Robot.getTable(), key)); + setNumberBinding(this); + } + } + public class EditableStringValueField extends BindableStringField{ + public EditableStringValueField(final String key){ + super(new BindableTableEntry(Robot.getTable(), key)); + setStringBinding(this); + } + } + + + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/BooleanBindable.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/BooleanBindable.java new file mode 100644 index 0000000..f654283 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/BooleanBindable.java @@ -0,0 +1,10 @@ +package edu.wpi.first.smartdashboard.gui.elements.bindings; + +public interface BooleanBindable { + BooleanBindable NULL = new BooleanBindable(){ + @Override + public void setBindableValue(boolean value) { + } + }; + public void setBindableValue(boolean value); +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/NumberBindable.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/NumberBindable.java new file mode 100644 index 0000000..7c0eacf --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/NumberBindable.java @@ -0,0 +1,11 @@ +package edu.wpi.first.smartdashboard.gui.elements.bindings; + +public interface NumberBindable { + NumberBindable NULL = new NumberBindable(){ + @Override + public void setBindableValue(double value) { + } + }; + + public void setBindableValue(double value); +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/StringBindable.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/StringBindable.java new file mode 100644 index 0000000..770060c --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/StringBindable.java @@ -0,0 +1,10 @@ +package edu.wpi.first.smartdashboard.gui.elements.bindings; + +public interface StringBindable { + StringBindable NULL = new StringBindable(){ + @Override + public void setBindableValue(String value) { + } + }; + public void setBindableValue(String value); +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/Controller.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/Controller.java new file mode 100644 index 0000000..c8e6852 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/Controller.java @@ -0,0 +1,20 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.livewindow.elements; + +/** + * Used for all controllers. + * @author Sam + */ +public interface Controller { + + /** + * Resets this Controller. This is called when exiting LiveWindow mode or + * when the "Reset" button is pressed. It's important to reset Controllers + * when exiting the LiveWindow because we don't want to keep motors etc. + * running when we can't control them. + */ + public void reset(); +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalInputDisplay.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalInputDisplay.java new file mode 100644 index 0000000..2efba99 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalInputDisplay.java @@ -0,0 +1,41 @@ +package edu.wpi.first.smartdashboard.livewindow.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.properties.Property; +import edu.wpi.first.smartdashboard.types.DataType; +import edu.wpi.first.smartdashboard.types.named.DigitalInputType; +import edu.wpi.first.wpilibj.tables.ITableListener; +import javax.swing.BoxLayout; + +/** + * Displays a digital value (on/off). Useful for limit switches. + * @author Sam + */ +public class DigitalInputDisplay extends AbstractTableWidget implements ITableListener { + + public static final DataType[] TYPES = {DigitalInputType.get()}; + + protected final String defaultText = " ---- "; + private final UneditableBooleanField display = new UneditableBooleanField(); + + public void init() { + + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + nameTag = new NameTag(getFieldName()); + add(nameTag); + + display.setText(defaultText); + setBooleanBinding("Value", display); + add(display); + + revalidate(); + repaint(); + + } + + public void propertyChanged(Property property) { + + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalOutputController.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalOutputController.java new file mode 100644 index 0000000..5a71a6e --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalOutputController.java @@ -0,0 +1,67 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.livewindow.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.properties.Property; +import edu.wpi.first.smartdashboard.types.DataType; +import edu.wpi.first.smartdashboard.types.named.CompressorType; +import edu.wpi.first.smartdashboard.types.named.DigitalOutputType; +import edu.wpi.first.smartdashboard.types.named.SolenoidType; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.BoxLayout; +import javax.swing.JToggleButton; + +/** + * Controls a controller that is either on or off. Useful for solenoids. + * @author Sam + */ +public class DigitalOutputController extends AbstractTableWidget implements Controller { + + public static final DataType[] TYPES = {DigitalOutputType.get(), + CompressorType.get(), + SolenoidType.get()}; + + private final JToggleButton controller = new JToggleButton("Off"); + + @Override + public void init() { + + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + nameTag = new NameTag(getFieldName()); + add(nameTag); + + controller.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + controller.setText(controller.isSelected() ? "On" : "Off"); + table.putBoolean("Value", controller.getText().equals("On")); + } + }); + add(controller); + + revalidate(); + repaint(); + + } + + /** + * @inheritdoc + */ + public void propertyChanged(Property property) { + + } + + /** + * @inheritdoc + */ + public void reset() { + if(controller.isSelected()) controller.doClick(); + } + + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/EncoderDisplay.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/EncoderDisplay.java new file mode 100644 index 0000000..5d57f0d --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/EncoderDisplay.java @@ -0,0 +1,100 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.livewindow.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.gui.elements.bindings.BooleanBindable; +import edu.wpi.first.smartdashboard.properties.Property; +import edu.wpi.first.smartdashboard.types.DataType; +import edu.wpi.first.smartdashboard.types.named.EncoderType; +import edu.wpi.first.smartdashboard.types.named.GearToothSensorType; +import edu.wpi.first.smartdashboard.types.named.QuadratureEncoderType; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; + +/** + * Displays data from an Encoder. This displays + * @author Sam + */ +public class EncoderDisplay extends AbstractTableWidget { + + public static final DataType[] TYPES = {QuadratureEncoderType.get(), EncoderType.get(), GearToothSensorType.get()}; + + private final JLabel speedTag = new JLabel("Speed"); + private final JLabel distanceTag = new JLabel("Distance"); + private final JLabel DPTTag = new JLabel("Distance per Tick"); + + /** Displays the speed of the Encoder. */ + private final UneditableNumberField speed = new UneditableNumberField(); + /** Displays how far the Encoder has traveled. */ + private final UneditableNumberField distance = new UneditableNumberField(); + /** A field to set the distance traveled by the Encoder per tick. */ + private final BindableNumberField DPT = new BindableNumberField(null); + /** Displays whether or not the Encoder is reversed. */ + private BindableBooleanCheckBox reversed; + /** Used to zero the distance traveled by the Encoder. */ + private JButton zero = new JButton("Zero Distance"); + + @Override + public void init() { + + reversed = new BindableBooleanCheckBox(BooleanBindable.NULL); + + nameTag = new NameTag(getFieldName()); + + setBooleanBinding("Reversed", reversed); + zero.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + distance.setBindableValue(0.0); + } + }); + + setNumberBinding("Speed", speed); + setNumberBinding("Distance", distance); + setNumberBinding("Distance per Tick", DPT); + + setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + c.gridx = 0; + c.weightx = 1; + add(nameTag, c); + c.weightx = 0; + add(speedTag, c); + c.weightx = 1; + add(distanceTag, c); + c.weightx = 2; + add(DPTTag, c); + c.weightx = 2; + c.gridx = 2; + c.gridy = 1; + add(speed, c); + c.gridy = 2; + add(distance, c); + c.gridy = 3; + add(DPT, c); + c.gridy = 4; + add(zero, c); + c.gridx = 0; + c.weightx = 0; + add(new JLabel("Reversed"), c); + c.gridx = 1; + c.weightx = 10000; + reversed.setAlignmentX(JLabel.LEFT_ALIGNMENT); + add(reversed, c); + + } + + public void propertyChanged(Property property) { + + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/GyroDisplay.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/GyroDisplay.java new file mode 100644 index 0000000..674a210 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/GyroDisplay.java @@ -0,0 +1,90 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.livewindow.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.gui.elements.Compass; +import edu.wpi.first.smartdashboard.properties.Property; +import edu.wpi.first.smartdashboard.types.DataType; +import edu.wpi.first.smartdashboard.types.named.CompassType; +import edu.wpi.first.smartdashboard.types.named.LWGyroType; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JComboBox; + + +/** + * Displays data from a Gyro or Compass. + * @author Sam + */ +public class GyroDisplay extends AbstractTableWidget { // TODO: get the compass working correctly + + public static final DataType[] TYPES = {LWGyroType.get(), + CompassType.get()}; + + /* A Compass display that graphically shows which direction the sensor is facing */ + private final Compass compass = new Compass(); + /** A text display that shows the value of the sensor. */ + private final UneditableNumberField feedback = new UneditableNumberField(); + /** Options in a ComboBox for which display to show. */ + private final String[] names = {"Display as Text", "Display as Compass"}; + private final JComboBox menu = new JComboBox(names); + + @Override + public void init() { + + setLayout(new GridBagLayout()); + final GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + + final GyroDisplay self = this; + nameTag = new NameTag(getFieldName()); + + setNumberBinding("Value", new NumberMultiBindable(feedback, compass)); + + compass.init(); + + menu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JComboBox box = (JComboBox) e.getSource(); + String name = box.getSelectedItem().toString(); + if(name.equals(names[1])) { + self.remove(feedback); + c.gridx = 0; + c.gridy = 2; + c.weightx = 2; + self.add(compass, c); + revalidate(); + repaint(); + } else if(name.equals(names[0])) { + self.remove(compass); + c.gridx = 0; + c.gridy = 2; + c.weightx = 2; + self.add(feedback, c); + revalidate(); + repaint(); + } + } + }); + + + c.gridx = 0; + add(nameTag, c); + c.gridy = 1; + add(menu, c); + c.gridy = 2; + c.weightx = 2; + add(feedback, c); + + } + + public void propertyChanged(Property property) { + + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LWSubsystem.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LWSubsystem.java new file mode 100644 index 0000000..a09a60d --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LWSubsystem.java @@ -0,0 +1,251 @@ +package edu.wpi.first.smartdashboard.livewindow.elements; + +import edu.wpi.first.smartdashboard.gui.MainPanel; +import edu.wpi.first.smartdashboard.gui.Widget; +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.properties.Property; +import edu.wpi.first.smartdashboard.types.DataType; +import edu.wpi.first.smartdashboard.types.named.LWSubsystemType; +import edu.wpi.first.smartdashboard.xml.SmartDashboardXMLReader; +import edu.wpi.first.wpilibj.tables.ITable; +import edu.wpi.first.wpilibj.tables.ITableListener; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.SwingUtilities; + +/** + * The main player in the Live Window. Subsystems hold every component that + * corresponds to that physical subsystem on the robot (e.g. a drive train would + * contain speed controllers and encoders). + * + * @author Sam Carlberg + */ +public class LWSubsystem extends AbstractTableWidget { + + public static final DataType[] TYPES = {LWSubsystemType.get()}; + private BoxLayout layout; + private Dimension preferredSize = new Dimension(100, 100); + /** + * An implementation of {@link MouseAdapter} that's responsible for dragging + * widgets. + */ + private Mouse mouse; + /** + * Whether or not it is possible to move widgets within a subsystem. + */ + private static boolean editable = false; + /** + * A widget being dragged. + */ + private Widget selected = null; + /** + * A list of components within this subsystem. + */ + private final ArrayList widgets = new ArrayList(20); + /** + * Responsible for reading data from a save file. + */ + private static SmartDashboardXMLReader reader; + + public LWSubsystem() { + super(true);//listen for sub tables + MainPanel.getPanel("LiveWindow").addSubsystem(this); + } + + /** + * Initializes this subsystem. + */ + public void init() { + layout = new BoxLayout(this, BoxLayout.Y_AXIS); + setLayout(layout); + setObstruction(true); + setOpaque(true); + setVisible(true); + setBorder(BorderFactory.createTitledBorder(getFieldName())); + mouse = new Mouse(this); + addMouseListener(mouse); + addMouseMotionListener(mouse); + } + + private void addSubsystemElement(String key, ITable value) { + try { + System.out.println("\nSubsystem \"" + getFieldName() + "\" does not contain widget \"" + key + "\""); + System.out.println("Table: " + value); + System.out.println("Type: " + value.getString("~TYPE~")); + System.out.println("Trying to add a widget of type \"" + DataType.getType(value) + "\" and key " + key); + Class widgetClass = DataType.getType(value).getDefault(); + Widget widget = widgetClass.newInstance(); + widget.setFieldName(key); + widget.setType(DataType.getType(value)); + widget.init(); + widget.setValue(value); + widgets.add(widget); + add(widget); + preferredSize = layout.preferredLayoutSize(this); + setPreferredSize(preferredSize); + setMinimumSize(preferredSize); + setSavedSize(preferredSize); + setSize(preferredSize); + System.out.println("New size [" + preferredSize.width + ", " + preferredSize.height + "]"); + revalidate(); + repaint(); + System.out.println(); + } catch (InstantiationException ex) { + Logger.getLogger(LWSubsystem.class.getName()).log(Level.SEVERE, null, ex); + } catch (IllegalAccessException ex) { + Logger.getLogger(LWSubsystem.class.getName()).log(Level.SEVERE, null, ex); + } + } + + /** + * @param source Required by ITableListener. Not used. + * @param key The name of the changed table. + * @param value The table that has been changed. + * @param isNew Required by ITableListener. Not used. + */ + @Override + public void tableChanged(ITable source, final String key, final ITable table, boolean isNew) { + boolean alreadyHasWidget = false; + if (reader != null) { + alreadyHasWidget = reader.containsWidgetOfName(this, key); + } + + if (!alreadyHasWidget) { + table.addTableListener("~TYPE~", new ITableListener() { + public void valueChanged(final ITable typeSource, final String typeKey, final Object typeValue, final boolean typeIsNew) { + table.removeTableListener(this); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + addSubsystemElement(key, table); + } + }); + } + }, true); + } + } + + /** + * Sets the reader responsible for loading a save file. + * + * @param XMLreader + */ + public static void setLoaded(SmartDashboardXMLReader XMLreader) { + reader = XMLreader; + } + + @Override + public void propertyChanged(Property property) { + } + + /** + * Gets a widget at a specific point. Used for dragging. + */ + public Widget getWidgetAt(Point p) { + if (getComponentAt(p) instanceof Widget) { + Widget w = (Widget) getComponentAt(p); + return w; + } else { + return null; + } + } + + /** + * Gets all the widgets contained within this subsystem. + */ + public ArrayList getWidgets() { + return widgets; + } + + /** + * Adds the specified widget to this subsystem. + * + * @param widget The widget to add. + */ + public void addWidget(Widget widget) { + widgets.add(widget); + } + + /** + * Sets whether or not it is possible to edit subsystems. + */ + public static void setEditable(boolean editable) { + LWSubsystem.editable = editable; + } + + /** + * @return whether or not it is possible to edit subsystems. + */ + public static boolean isEditable() { + return editable; + } + + /** + * Used for DnD for widgets within a subsystem. + */ + private class Mouse extends MouseAdapter { + + int yclick = 0; + private final LWSubsystem subsystem; + + public Mouse(LWSubsystem subsystem) { + this.subsystem = subsystem; + } + + @Override + public void mouseDragged(MouseEvent e) { + if (editable) { + if (selected == null) { + selected = getWidgetAt(e.getPoint()); + } + if (selected != null) { + + final int mouseY = e.getY(); // Mouse's y-coordinate. We don't care about its x-coord + int index = subsystem.getComponentZOrder(selected); // The index of the dragged widget + int newIndex = index + (mouseY < yclick ? -1 : 1); // The index to insert the dragged widget + + boolean goingUp = false, // Flags for which direction the widget is being dragged + goingDown = false; + + // Check to see if the new index is within acceptable bounds + if (newIndex >= 0 && newIndex < (subsystem.getComponentCount())) { + goingUp = newIndex < index; + goingDown = newIndex > index; + } + + if (goingUp || goingDown) { + Component next = subsystem.getComponent(newIndex); + + // If the stars align... move the widget + if ((goingDown && mouseY > next.getY() + next.getHeight()) + || (goingUp && mouseY < next.getY())) { + subsystem.remove(selected); + subsystem.add(selected, newIndex); + } + + subsystem.revalidate(); + subsystem.repaint(); + } + } + } + } + + @Override + public void mousePressed(MouseEvent e) { + yclick = e.getY(); + } + + @Override + public void mouseReleased(MouseEvent e) { + selected = null; + yclick = 0; + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LiveWindowWidgetRegistrar.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LiveWindowWidgetRegistrar.java new file mode 100644 index 0000000..261360b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LiveWindowWidgetRegistrar.java @@ -0,0 +1,26 @@ +package edu.wpi.first.smartdashboard.livewindow.elements; + +import edu.wpi.first.smartdashboard.types.DisplayElementRegistry; + +/** + * + * @author Sam + */ +public class LiveWindowWidgetRegistrar { + + /** + * Initializes all the widgets in the Live Window. + */ + public static void init() { + DisplayElementRegistry.registerWidget(LWSubsystem.class); + DisplayElementRegistry.registerWidget(SpeedController.class); + DisplayElementRegistry.registerWidget(RelayController.class); + DisplayElementRegistry.registerWidget(DigitalOutputController.class); + DisplayElementRegistry.registerWidget(SingleNumberDisplay.class); + DisplayElementRegistry.registerWidget(DigitalInputDisplay.class); + DisplayElementRegistry.registerWidget(GyroDisplay.class); + DisplayElementRegistry.registerWidget(EncoderDisplay.class); + DisplayElementRegistry.registerWidget(ServoController.class); + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/NameTag.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/NameTag.java new file mode 100644 index 0000000..bcc400b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/NameTag.java @@ -0,0 +1,26 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.livewindow.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import java.awt.Color; +import java.awt.Font; +import javax.swing.JLabel; + +/** + * A custom JLabel implementation that's used for titles + * of individual components within a subsystem. + * @author Sam + * @see AbstractTableWidget#nameTag + */ +public class NameTag extends JLabel { + + public NameTag(String text) { + super(text); + setFont(Font.decode("Arial-BOLD-12")); + setForeground(new Color(0, 0, 125)); + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/RelayController.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/RelayController.java new file mode 100644 index 0000000..2fca7d0 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/RelayController.java @@ -0,0 +1,60 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.livewindow.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.properties.Property; +import edu.wpi.first.smartdashboard.types.DataType; +import edu.wpi.first.smartdashboard.types.named.DoubleSolenoidType; +import edu.wpi.first.smartdashboard.types.named.RelayType; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.BoxLayout; +import javax.swing.JComboBox; + +/** + * Used to control relays, which give out positive, negative, or no voltage + * ("Forward," "Off," and "Reverse") without control over how much is given + * out. Useful for Spikes. + * @author Sam + */ +public class RelayController extends AbstractTableWidget implements Controller { + + public static final DataType[] TYPES = {RelayType.get(), DoubleSolenoidType.get()}; + + private final String[] options = {"Forward", "Off", "Reverse"}; + private final JComboBox controller = new JComboBox(options); + + @Override + public void init() { + + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + nameTag = new NameTag(getFieldName()); + add(nameTag); + controller.setSelectedIndex(1); + controller.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + table.putString("Value", controller.getSelectedItem().toString()); + } + }); + add(controller); + + revalidate(); + repaint(); + + } + + public void propertyChanged(Property property) { + + } + + public void reset() { + table.putString("Value", "Off"); + controller.setSelectedIndex(1); + } + + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/ServoController.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/ServoController.java new file mode 100644 index 0000000..6a72ddb --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/ServoController.java @@ -0,0 +1,100 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.livewindow.elements; + +import edu.wpi.first.smartdashboard.gui.Widget; +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.gui.elements.bindings.NumberBindable; +import edu.wpi.first.smartdashboard.properties.Property; +import edu.wpi.first.smartdashboard.types.DataType; +import edu.wpi.first.smartdashboard.types.named.ServoType; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import javax.swing.JButton; + +/** + * Controls a Servo. Very similar to a {@link SpeedController}, but + * sends values from 0 to +180 degrees to the servo instead of -1 to +1 + * for SpeedControllers such as Victors. + * @author Sam + */ +public class ServoController extends AbstractTableWidget implements Controller { + + public static final DataType[] TYPES = {ServoType.get()}; + + private NumberSlider controller; + /** Used to turn off the servo */ + private final JButton zeroButton = new JButton("Zero"); + + private final String defaultText = "0.0"; + private final Widget.UneditableNumberField feedback = new Widget.UneditableNumberField(); + private final NumberBindable valueEntry = getTableEntryBindable("Value"); + + @Override + public void init() { + nameTag = new NameTag(getFieldName()); + + controller = new Widget.NumberSlider(valueEntry); + controller.setMin(0); // The minimum value able to be sent to the servo + controller.setMax(1); // The maximum value able to be sent to the servo + controller.setBindableValue(0.0); + controller.setSnapToTicks(false); + controller.setMajorTickSpacing(50); + controller.setPaintTicks(true); + + feedback.setText(defaultText); + feedback.setColumns(4); + + setNumberBinding("Value", new Widget.NumberMultiBindable(feedback)); + + controller.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) { + zeroButton.doClick(); + } + } + }); + + feedback.setEditable(false); + zeroButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + reset(); + } + }); + + setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + c.gridx = 0; + add(nameTag, c); + c.gridy = 1; + c.weightx = 1.0; + add(controller, c); + c.weightx = 0.0; + c.gridx = 2; + add(feedback, c); + c.gridx = 3; + add(zeroButton, c); + c.gridx = 4; + + } + + /** + * Resets the slider value to zero and tells the robot + * to turn the motor off. + */ + public void reset() { + controller.setBindableValue(0.0); + valueEntry.setBindableValue(0.0); + } + + public void propertyChanged(Property property) { } + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SingleNumberDisplay.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SingleNumberDisplay.java new file mode 100644 index 0000000..ec35ed4 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SingleNumberDisplay.java @@ -0,0 +1,53 @@ +package edu.wpi.first.smartdashboard.livewindow.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.properties.Property; +import edu.wpi.first.smartdashboard.types.DataType; +import edu.wpi.first.smartdashboard.types.named.AccelerometerType; +import edu.wpi.first.smartdashboard.types.named.AnalogInputType; +import edu.wpi.first.smartdashboard.types.named.CounterType; +import edu.wpi.first.smartdashboard.types.named.GearToothSensorType; +import edu.wpi.first.smartdashboard.types.named.UltrasonicType; +import edu.wpi.first.wpilibj.tables.ITableListener; +import javax.swing.BoxLayout; + + +/** + * Displays a single number (e.g. 10.43). This should be used + * by potentiometers, accelerometers, and other sensors that send + * a single number to the robot. + * @author Sam + */ +public class SingleNumberDisplay extends AbstractTableWidget implements ITableListener { + + public static final DataType[] TYPES = {AnalogInputType.get(), + UltrasonicType.get(), + AccelerometerType.get(), + GearToothSensorType.get(), + CounterType.get()}; + + protected final String defaultText = " ---- "; + private final UneditableNumberField display = new UneditableNumberField(); + + /** + * @inheritdoc + */ + public void init() { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + nameTag = new NameTag(getFieldName()); + add(nameTag); + + display.setFocusable(false); + display.setText(defaultText); + setNumberBinding("Value", display); + add(display); + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SpeedController.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SpeedController.java new file mode 100644 index 0000000..bd0a779 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SpeedController.java @@ -0,0 +1,94 @@ +package edu.wpi.first.smartdashboard.livewindow.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.NumberBindable; +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; + +/** + * Used to control SpeedControllers such as Victors. + * @author Sam + */ +public class SpeedController extends AbstractTableWidget implements Controller { + + public static final DataType[] TYPES = {SpeedControllerType.get()}; + + private NumberSlider controller; + /** Used to turn off the speed controller */ + private final JButton zeroButton = new JButton("Zero"); + + private final String defaultText = "0.0"; + private final UneditableNumberField feedback = new UneditableNumberField(); + private final NumberBindable valueEntry = getTableEntryBindable("Value"); + + @Override + public void init() { + nameTag = new NameTag(getFieldName()); + + controller = new NumberSlider(valueEntry); + controller.setMin(-1.0); + controller.setMax(1.0); + controller.setBindableValue(0.0); + controller.setSnapToTicks(false); + controller.setMajorTickSpacing(50); + controller.setPaintTicks(true); + + feedback.setText(defaultText); + feedback.setColumns(4); + + setNumberBinding("Value", new NumberMultiBindable(feedback)); + + controller.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) { + zeroButton.doClick(); + } + } + }); + + feedback.setEditable(false); + zeroButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + reset(); + } + }); + + setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + c.gridx = 0; + add(nameTag, c); + c.gridy = 1; + c.weightx = 1.0; + add(controller, c); + c.weightx = 0.0; + c.gridx = 2; + add(feedback, c); + c.gridx = 3; + add(zeroButton, c); + c.gridx = 4; + + } + + /** + * Resets the slider value to zero and tells the robot + * to turn the motor off. + */ + public void reset() { + controller.setBindableValue(0.0); + valueEntry.setBindableValue(0.0); + } + + public void propertyChanged(Property property) { + + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/main.java b/smartdashboard/src/edu/wpi/first/smartdashboard/main.java new file mode 100644 index 0000000..1bf0b8e --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/main.java @@ -0,0 +1,149 @@ +package edu.wpi.first.smartdashboard; + +import java.io.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.extensions.*; +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.robot.*; + +/** + * main SmartDashboard logic + * + * @author Joe Grinstead + * @author pmalmsten + */ +public class main { + + /** Variable used in the {@link main#inCompetition() inCompetition()} method */ + private static boolean inCompetition = false; + + /** + * Returns whether or not this is in "competition" mode. Competition mode + * should be used on the netbook provided for teams to use the dashboard. If + * the SmartDashboard is in competition mode, then it automatically sizes + * itself to be the standard dashboard size and to remove the frame around + * it. It can be set to be in competition if "competition" is one of the + * words passed in through the command line. + * + * @return whether or not this is in "competition" mode + */ + public static boolean inCompetition() { + return inCompetition; + } + + + private static DashboardFrame frame; + /** + * Starts the program + * + * @param args + * the standard arguments. If "competition" is one of them, then + * the SmartDashboard will be in competition mode + * @see main#inCompetition() inCompetition() + */ + public static void main(final String[] args) { + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) {} + }}); + } catch (Exception ex) { + ex.printStackTrace(); + System.exit(2); + } + + // Present a loading bar (it will only show up if this is going slowly) + final ProgressMonitor monitor = new ProgressMonitor(null, "Loading SmartDashboard", "Initializing internal code...", 0, 1000); + + // Search the filesystem for extensions (49%) + FileSniffer.findExtensions(monitor, 0, 490); + + + + ArgParser argParser = new ArgParser(args, true, true, new String[] { "ip" }); + inCompetition = argParser.hasFlag("competition"); + + + + + + // Initialize GUI + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + frame = new DashboardFrame(inCompetition); + }}); + } catch (Exception ex) { + ex.printStackTrace(); + System.exit(2); + } + + + + + IntegerProperty teamProp = frame.getPrefs().team; + if (argParser.hasValue("ip")) { + monitor.setProgress(650); + monitor.setNote("Connecting to robot at: "+argParser.getValue("ip")); + Robot.setHost(argParser.getValue("ip")); + } + else{ + monitor.setProgress(600); + monitor.setNote("Getting Team Number"); + int teamNumber = teamProp.getValue(); + teamNumberLoop: while (teamNumber <= 0) { + try{ + String input = JOptionPane.showInputDialog("Input Team Number"); + if(input==null){ + teamNumber = 0; + break teamNumberLoop; + } + teamNumber = Integer.parseInt(input); + } catch(Exception e){} + } + monitor.setProgress(650); + monitor.setNote("Connecting to robot of team: "+teamNumber); + teamProp.setValue(teamNumber); + } + + + + + + try { + SwingUtilities.invokeAndWait(new Runnable() { + + public void run() { + try { + frame.pack(); + frame.setVisible(true); + + monitor.setProgress(750); + monitor.setNote("Loading From Save"); + + // Load + File file = new File(frame.getPrefs().saveFile.getValue()); + if (file.exists()) { + frame.load(file.getPath()); + } + + monitor.setProgress(1000); + + } catch (Exception e) { + e.printStackTrace(); + + System.exit(1); + } + } + }); + } catch (Exception ex) { + ex.printStackTrace(); + System.exit(2); + } + } +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/net/TCPImageFetcher.java b/smartdashboard/src/edu/wpi/first/smartdashboard/net/TCPImageFetcher.java new file mode 100644 index 0000000..81ceb1a --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/net/TCPImageFetcher.java @@ -0,0 +1,137 @@ +package edu.wpi.first.smartdashboard.net; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import javax.imageio.ImageIO; + +/** + * Fetches images from the robot's PCVideoServer + * + * @author pmalmsten + */ +public class TCPImageFetcher { + public static final int MAX_IMG_SIZE_BYTES = 500000; + public static final int READ_TIMEOUT_MS = 3000; + public static final int VIDEO_TO_PC_PORT = 1180; + private Socket m_sock = null; + private InputStream m_sockistream = null; + private byte[] m_imgBuffer = null; + private int m_maxImgBufferSize = 0; + private ByteArrayInputStream m_baistream = null; + private DataInputStream m_daistream = null; + private boolean m_initialized = false; + private byte[] m_address = null; + + /** + * Creates a new TCPImageFetcher which will attempt to read from the + * given team's robot + * + * @param teamNumber The team number to use + * @throws UnknownHostException + * @throws IOException + */ + public TCPImageFetcher(int teamNumber) { + byte high = (byte) (teamNumber / 100); + byte low = (byte) (teamNumber % 100); + m_address = new byte[] {10, high, low, 2}; + } + + /** + * Initializes a TCP connection + * + * @param addr The address of the remote device + * @param port The port to connect to + * @throws IOException + */ + private void init() throws IOException { + m_sock = new Socket(InetAddress.getByAddress(m_address), VIDEO_TO_PC_PORT); + m_sock.setSoTimeout(READ_TIMEOUT_MS); + m_sockistream = m_sock.getInputStream(); + m_daistream = new DataInputStream(m_sockistream); + m_initialized = true; + } + + /** + * Reads and returns an image from the associated socket connection. Blocks + * until a valid image arrives. + * @return The image received + */ + public BufferedImage fetch() throws IOException { + if(!m_initialized) + init(); + + try { + byte[] header = new byte[4]; + + while(true) { + blockingRead(m_sockistream, header, 4); + + // Look for header 1,0,0,0 + if(!((header[0] == 1) && ((header[1] + header[2] + header[3]) == 0))) { + continue; + } + + // wait for length integer (4 bytes) + while(m_sockistream.available() < 4) {} + + // Read int length of data to follow + int imgDataLen = m_daistream.readInt(); + //System.out.println(" Data Len: " + imgDataLen + "Hex:" + Integer.toHexString(imgDataLen)); + + // Read in the expected number of bytes + resizeBuffer(imgDataLen); + blockingRead(m_sockistream, m_imgBuffer, imgDataLen); + m_baistream.reset(); + + // Read the image + return ImageIO.read(m_baistream); + } + } catch(IOException ex) { + m_sock.close(); + m_initialized = false; + throw ex; + } + } + + /** + * Ensures that the image buffer byte array is always an appropriate size + * @param size Requested size for the image buffer + */ + private void resizeBuffer(int size) { + if(size > m_maxImgBufferSize) { + if(size > MAX_IMG_SIZE_BYTES) + size = MAX_IMG_SIZE_BYTES; + + m_maxImgBufferSize = size + 100; + m_imgBuffer = new byte[m_maxImgBufferSize]; + m_baistream = new ByteArrayInputStream(m_imgBuffer); + } + } + + /** + * Guarantees that the requested number of bytes are read from the given + * input stream and are written to the given buffer before returning. + * @param istream Stream to read from. + * @param buf Array to write to. + * @param requestedBytes Requested number of bytes to read and store. + * @throws IOException + */ + private void blockingRead(InputStream istream, byte[] buf, int requestedBytes) throws IOException { + int offset = 0; + while(offset < requestedBytes) { + int read = istream.read(buf, offset, requestedBytes - offset); + + if(read < 0) { + throw new IOException("Connection interrupted"); + } + + offset += read; + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/BooleanProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/BooleanProperty.java new file mode 100644 index 0000000..64e735d --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/BooleanProperty.java @@ -0,0 +1,61 @@ +package edu.wpi.first.smartdashboard.properties; + +import java.awt.Component; +import javax.swing.DefaultCellEditor; +import javax.swing.JCheckBox; +import javax.swing.JTable; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; + +/** + * + * @author Joe Grinstead + * @author Jeff Copeland + */ +public class BooleanProperty extends TextInputProperty { + + private JCheckBox box = new JCheckBox(); + private DefaultCellEditor checkbox = new DefaultCellEditor(box); + private TableCellRenderer renderer = new TableCellRenderer() { + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + return box; + } + }; + + + public BooleanProperty(PropertyHolder element, String name) { + super(Boolean.class, element, name); + } + + public BooleanProperty(PropertyHolder element, String name, boolean defaultValue) { + super(Boolean.class, element, name, defaultValue); + } + + @Override + protected Boolean transformValue(Object value) { + if (value instanceof String) { + if ("true".equalsIgnoreCase((String) value)) { + return true; + } else if ("false".equalsIgnoreCase((String) value)) { + return false; + } else { + return null; + } + } else { + return super.transformValue(value); + } + } + + @Override + public TableCellRenderer getRenderer() { + box.setSelected(getValue()); + return renderer; + } + + @Override + public TableCellEditor getEditor(Component c) { + box.setSelected(getValue()); + return checkbox; + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/ColorProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/ColorProperty.java new file mode 100644 index 0000000..f794a88 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/ColorProperty.java @@ -0,0 +1,152 @@ +package edu.wpi.first.smartdashboard.properties; + +import java.awt.Color; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.EventObject; +import javax.swing.AbstractCellEditor; +import javax.swing.JColorChooser; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.UIManager; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; + +/** + * + * @author Joe Grinstead + */ +public class ColorProperty extends GenericProperty { + + public ColorProperty(PropertyHolder parent, String name) { + super(Color.class, parent, name); + } + + public ColorProperty(PropertyHolder parent, String name, Color defaultValue) { + super(Color.class, parent, name, defaultValue); + } + + @Override + protected Color transformValue(Object value) { + if (value instanceof String) { + try { + String string = (String) value; + int index = string.indexOf('.'); + int red = Integer.parseInt(string.substring(0, index)); + string = string.substring(index + 1); + index = string.indexOf('.'); + int green = Integer.parseInt(string.substring(0, index)); + string = string.substring(index + 1); + index = string.indexOf('.'); + int blue = Integer.parseInt(string.substring(0, index)); + int alpha = Integer.parseInt(string.substring(index + 1)); + return new Color(red, green, blue, alpha); + } catch (Exception e) { + return null; + } + } else { + return super.transformValue(value); + } + } + + @Override + public String getSaveValue() { + Color color = (Color) getValue(); + return color.getRed() + "." + color.getGreen() + "." + color.getBlue() + "." + color.getAlpha(); + } + + @Override + public TableCellEditor getEditor(Component c) { + return new ColorTableCellEditor(c); + } + private ColorTableCellRenderer renderer; + + @Override + public TableCellRenderer getRenderer() { + if (renderer == null) { + renderer = new ColorTableCellRenderer(); + } + return renderer; + } + + class ColorTableCellEditor extends AbstractCellEditor implements TableCellEditor { + + private JColorChooser colorChooser; + private JDialog colorDialog; + private JPanel panel; + + public ColorTableCellEditor(Component c) { + panel = new JPanel(); + colorChooser = new JColorChooser(); + colorDialog = JColorChooser.createDialog(c, "Color editor", true, colorChooser, + new ActionListener() { // OK Button listener + + public void actionPerformed(ActionEvent ev) { + stopCellEditing(); + } + }, new ActionListener() { // Cancel button listener + + public void actionPerformed(ActionEvent ev) { + cancelCellEditing(); + } + }); + colorDialog.addWindowListener(new WindowAdapter() { + + @Override + public void windowClosing(WindowEvent event) { + cancelCellEditing(); + } + }); + } + + @Override + public boolean shouldSelectCell(EventObject av) { + // start editing and tell caller it's OK to edit this cell + colorDialog.setVisible(true); + return true; + } + + @Override + public void cancelCellEditing() { + // editing is calceled -- hide dialog + colorDialog.setVisible(false); + super.cancelCellEditing(); + } + + @Override + public boolean stopCellEditing() { + // editing is complete -- hide dialog + colorDialog.setVisible(false); + super.stopCellEditing(); + return true; + } + + public Object getCellEditorValue() { + return colorChooser.getColor(); + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int col) { + // get the current color and store in the dialog in case the user starts + // editing it + colorChooser.setColor((Color) value); + return panel; + } + } + + class ColorTableCellRenderer extends JPanel implements TableCellRenderer { + + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { + setBackground((Color) value); + if (hasFocus) { + setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); + } else { + setBorder(null); + } + return this; + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/DoubleProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/DoubleProperty.java new file mode 100644 index 0000000..780536a --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/DoubleProperty.java @@ -0,0 +1,32 @@ +package edu.wpi.first.smartdashboard.properties; + +/** + * + * @author Joe Grinstead + */ +public class DoubleProperty extends NumberProperty { + + public DoubleProperty(PropertyHolder element, String name) { + super(element, name); + } + + public DoubleProperty(PropertyHolder element, String name, double defaultValue) { + super(element, name, defaultValue); + } + + @Override + protected Double transformValue(Object value) { + Number number = super.transformValue(value); + return number == null ? null : number.doubleValue(); + } + + @Override + public Double getValue() { + return (Double) super.getValue(); + } + + @Override + public Double getDefault() { + return (Double) super.getDefault(); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/FileProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/FileProperty.java new file mode 100644 index 0000000..a591586 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/FileProperty.java @@ -0,0 +1,84 @@ +package edu.wpi.first.smartdashboard.properties; + +import java.awt.*; +import java.io.*; + +import javax.swing.*; +import javax.swing.table.*; + +import org.jfree.ui.*; + +/** + * + * @author Joe Grinstead + */ +public class FileProperty extends GenericProperty { + + protected JFileChooser chooser; + + public FileProperty(PropertyHolder parent, String name) { + super(String.class, parent, name); + + chooser = new JFileChooser(); + } + + public FileProperty(PropertyHolder parent, String name, String defaultValue) { + super(String.class, parent, name, defaultValue); + + chooser = new JFileChooser(getValue()); + } + + @Override + protected String transformValue(Object value) { + if (value instanceof String) { + return (String) value; + } else if (value instanceof File) { + return ((File) value).getPath(); + } else { + return null; + } + } + + public void addExtensionFilter(String description, String extension){ + chooser.addChoosableFileFilter(new ExtensionFileFilter(description, extension.startsWith(".")?extension:"."+extension)); + } + + @Override + public TableCellRenderer getRenderer() { + return null; + } + + @Override + public TableCellEditor getEditor(Component c) { + return new FileTableCellEditor(c); + } + + private class FileTableCellEditor extends AbstractCellEditor implements TableCellEditor { + + private Component c; + private JLabel label; + + public FileTableCellEditor(Component c) { + this.c = c; + label = new JLabel(); + } + + public Object getCellEditorValue() { + return getValue(); + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + chooser.setSelectedFile(new File(getValue())); + switch (chooser.showDialog(c, "Select")) { + case JFileChooser.APPROVE_OPTION: + setValue(chooser.getSelectedFile()); + break; + case JFileChooser.ERROR_OPTION: + case JFileChooser.CANCEL_OPTION: + default: + } + label.setText(getValue().toString()); + return label; + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/GenericProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/GenericProperty.java new file mode 100644 index 0000000..339ae98 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/GenericProperty.java @@ -0,0 +1,37 @@ +package edu.wpi.first.smartdashboard.properties; + +/** + * + * @author Joe Grinstead + */ +public abstract class GenericProperty extends Property { + + private final Class clazz; + + public GenericProperty(Class clazz, PropertyHolder element, String name) { + super(element, name); + + this.clazz = clazz; + } + + public GenericProperty(Class clazz, PropertyHolder element, String name, T defaultValue) { + super(element, name, defaultValue); + + this.clazz = clazz; + } + + @Override + protected T transformValue(Object value) { + return clazz.isInstance(value) ? clazz.cast(value) : null; + } + + @Override + public T getValue() { + return clazz.cast(super.getValue()); + } + + @Override + public T getDefault() { + return clazz.cast(super.getDefault()); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IPAddressProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IPAddressProperty.java new file mode 100644 index 0000000..93c1906 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IPAddressProperty.java @@ -0,0 +1,37 @@ +package edu.wpi.first.smartdashboard.properties; + +/** + * + * @author Joe Grinstead + */ +public class IPAddressProperty extends IntegerListProperty { + + public IPAddressProperty(PropertyHolder parent, String name) { + super(parent, name); + + setDelimeter("\\."); + setValueSplit("."); + } + + public IPAddressProperty(PropertyHolder parent, String name, int[] value) { + super(parent, name, value); + + setDelimeter("\\."); + setValueSplit("."); + } + + @Override + protected int[] transformValue(Object value) { + int[] result = super.transformValue(value); + if (result == null || result.length != 4) { + return null; + } else { + for (int n : result) { + if (n < 0 || n > 255) { + return null; + } + } + return result; + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerListProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerListProperty.java new file mode 100644 index 0000000..54a87eb --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerListProperty.java @@ -0,0 +1,78 @@ +package edu.wpi.first.smartdashboard.properties; + +import javax.swing.table.TableCellRenderer; + +/** + * + * @author Joe Grinstead + */ +public class IntegerListProperty extends Property { + + private String valueSplit = ", "; + private String delimiter = ","; + + public IntegerListProperty(PropertyHolder parent, String name) { + super(parent, name); + } + + public IntegerListProperty(PropertyHolder parent, String name, int[] value) { + super(parent, name, value); + } + + protected void setValueSplit(String split) { + valueSplit = split; + } + + protected void setDelimeter(String delimiter) { + this.delimiter = delimiter; + } + + @Override + protected int[] transformValue(Object value) { + if (value instanceof String) { + String text = (String) value; + + String[] texts = text.split(delimiter); + int[] values = new int[texts.length]; + + for (int i = 0; i < texts.length; i++) { + try { + values[i] = Integer.parseInt(texts[i].trim()); + } catch (NumberFormatException e) { + return null; + } + } + return values; + } else if (value instanceof int[]) { + return (int[]) value; + } + return null; + } + + @Override + public Object getTableValue() { + return getSaveValue(); + } + + @Override + public TableCellRenderer getRenderer() { + return null; + } + + @Override + public String getSaveValue() { + String text = ""; + int[] value = getValue(); + for (int i = 0; i < value.length; i++) { + if (i > 0) { + text += valueSplit; + } + text += value[i]; + } + return text; + } + + public int[] getValue() { + return (int[]) super.getValue(); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerProperty.java new file mode 100644 index 0000000..42a70c8 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerProperty.java @@ -0,0 +1,32 @@ +package edu.wpi.first.smartdashboard.properties; + +/** + * + * @author Joe Grinstead + */ +public class IntegerProperty extends NumberProperty { + + public IntegerProperty(PropertyHolder element, String name) { + super(element, name); + } + + public IntegerProperty(PropertyHolder element, String name, int defaultValue) { + super(element, name, defaultValue); + } + + @Override + protected Integer transformValue(Object value) { + Number number = super.transformValue(value); + return number == null ? null : number.intValue(); + } + + @Override + public Integer getValue() { + return (Integer) super.getValue(); + } + + @Override + public Number getDefault() { + return (Integer) super.getDefault(); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/MultiProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/MultiProperty.java new file mode 100644 index 0000000..42630b6 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/MultiProperty.java @@ -0,0 +1,73 @@ +package edu.wpi.first.smartdashboard.properties; + +import java.awt.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.table.*; + +/** + * + * @author Jeff Copeland + */ +public class MultiProperty extends Property { + public MultiProperty(PropertyHolder holder, String name) { + super(holder, name); + } + + private JComboBox comboBox = new JComboBox(); + private DefaultCellEditor cellEditor = new DefaultCellEditor(comboBox); + private Map values = new HashMap(); + private TableCellRenderer renderer = new TableCellRenderer() { + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + return comboBox; + } + }; + + public void add(String key, Object value) { + comboBox.addItem(key); + values.put(key, value); + } + + @Override + public boolean setDefault(Object key) { + if (super.setDefault(key)) { + if (!hasValue()) { + comboBox.setSelectedItem(key); + } + return true; + } + return false; + } + + @Override + protected void valueChanged() { + comboBox.setSelectedItem(getSaveValue()); + } + + @Override + public TableCellEditor getEditor(Component c) { + return cellEditor; + } + + @Override + protected Object transformValue(Object value) { + return values.get(value); + } + + @Override + public TableCellRenderer getRenderer() { + return renderer; + } + + @Override + public String getSaveValue() { + + for(Map.Entry entry: values.entrySet()) { + if (entry.getValue().equals(getValue())) { + return entry.getKey(); + } + } + return null; + } +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/NumberProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/NumberProperty.java new file mode 100644 index 0000000..a75cc59 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/NumberProperty.java @@ -0,0 +1,33 @@ +package edu.wpi.first.smartdashboard.properties; + +/** + * + * @author Joe Grinstead + */ +public class NumberProperty extends TextInputProperty { + + public NumberProperty(PropertyHolder element, String name) { + super(Number.class, element, name); + } + + public NumberProperty(PropertyHolder element, String name, Number defaultValue) { + super(Number.class, element, name, defaultValue); + } + + @Override + protected Number transformValue(Object value) { + if (value instanceof String) { + try { + return Integer.parseInt((String) value); + } catch (NumberFormatException e) { + try { + return Double.parseDouble((String) value); + } catch (NumberFormatException ex) { + return null; + } + } + } else { + return super.transformValue(value); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/Property.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/Property.java new file mode 100644 index 0000000..df4599c --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/Property.java @@ -0,0 +1,110 @@ +package edu.wpi.first.smartdashboard.properties; + +import java.awt.Component; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; + +/** + * + * + * @author Joe Grinstead + */ +public abstract class Property { + + private final PropertyHolder element; + private final String name; + private Object defaultValue; + private Object value; + + protected Property(PropertyHolder element, String name) { + this.element = element; + this.name = name; + + element.getProperties().put(name, this); + } + + protected Property(PropertyHolder element, String name, Object defaultValue) { + this.element = element; + this.name = name; + this.defaultValue = defaultValue; + + element.getProperties().put(name, this); + } + + public String getName() { + return name; + } + + public boolean hasDefault() { + return defaultValue != null; + } + + public boolean setDefault(Object value) { + value = transformValue(value); + if (value != null) { + defaultValue = value; + return true; + } else { + return false; + } + } + + public boolean setValue(Object value) { + value = transformValue(value); + if (value != null && element.validatePropertyChange(this, value)) { + this.value = value; + valueChanged(); + element.propertyChanged(this); + return true; + } else { + return false; + } + } + + public Object getValue() { + return value == null ? defaultValue : value; + } + + public Object getDefault() { + return defaultValue; + } + + protected abstract Object transformValue(Object value); + + public String getSaveValue() { + return getValue().toString(); + } + + public void setSaveValue(String value) { + _setValue(value); + } + + protected void _setValue(Object value) { + value = transformValue(value); + if (value != null) { + this.value = value; + valueChanged(); + } + } + + protected void valueChanged() { + } + + public boolean isDefault() { + return value == null ? defaultValue != null : value.equals(defaultValue); + } + + public boolean hasValue() { + return value != null; + } + + public Object getTableValue() { + return getValue(); + } + + public abstract TableCellRenderer getRenderer(); + + public TableCellEditor getEditor(Component c) { + return null; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/PropertyHolder.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/PropertyHolder.java new file mode 100644 index 0000000..d052ebc --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/PropertyHolder.java @@ -0,0 +1,56 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package edu.wpi.first.smartdashboard.properties; + +import java.util.Map; + +/** + * This interface defines methods that allow an object to use the standard + * property mechanism used in the SmartDashboard project. + * + * The system is designed to make the necessary coding for an extension developer + * quite small. + * @author Joe Grinstead + * @see Property + */ +public interface PropertyHolder { + + /** + * Returns a mapping between every property name and the corresponding property. + * The map will be edited externally; it should not be a copy of the internal map. + * @return a map + */ + public Map getProperties(); + + /** + * This method allows the {@link PropertyHolder} to veto whether or not the + * given {@link Property} should change to the given value. + * + * It is called whenever {@link Property#setValue(java.lang.Object) setValue(...)} + * is called. + * + *

It is called only when the value if the value is acceptable. + * In other words, it must not be {@code null} and it must correspond to + * the type of element that the {@link Property} accepts. So if the given + * {@link Property} is an {@link IntegerProperty IntegerProperty}, then {@code value} will + * be a non-null {@link Integer Integer} object.

+ * + *

Note that this method will not be called when a property receives its value + * from a save file

+ * @param property the {@link Property} + * @param value the value to change it to + * @return whether or not the change should be allowed + */ + public boolean validatePropertyChange(Property property, Object value); + + /** + * This method will be called after the given {@link Property Property's} value + * has changed. + * @param property the {@link Property} that changed + */ + public void propertyChanged(Property property); + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/StringProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/StringProperty.java new file mode 100644 index 0000000..8a0fe73 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/StringProperty.java @@ -0,0 +1,21 @@ +package edu.wpi.first.smartdashboard.properties; + +/** + * + * @author Joe Grinstead + */ +public class StringProperty extends TextInputProperty { + + public StringProperty(PropertyHolder element, String name) { + super(String.class, element, name); + } + + public StringProperty(PropertyHolder element, String name, String defaultValue) { + super(String.class, element, name, defaultValue); + } + + @Override + protected String transformValue(Object value) { + return value.toString(); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/TextInputProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/TextInputProperty.java new file mode 100644 index 0000000..82b0fb8 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/TextInputProperty.java @@ -0,0 +1,23 @@ +package edu.wpi.first.smartdashboard.properties; + +import javax.swing.table.TableCellRenderer; + +/** + * + * @author Joe Grinstead + */ +public abstract class TextInputProperty extends GenericProperty { + + protected TextInputProperty(Class clazz, PropertyHolder element, String name) { + super(clazz, element, name); + } + + protected TextInputProperty(Class clazz, PropertyHolder element, String name, T defaultValue) { + super(clazz, element, name, defaultValue); + } + + @Override + public TableCellRenderer getRenderer() { + return null; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/robot/Robot.java b/smartdashboard/src/edu/wpi/first/smartdashboard/robot/Robot.java new file mode 100644 index 0000000..8db77a2 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/robot/Robot.java @@ -0,0 +1,73 @@ +package edu.wpi.first.smartdashboard.robot; + +import java.io.*; + +import edu.wpi.first.wpilibj.networktables.*; +import edu.wpi.first.wpilibj.networktables2.client.*; +import edu.wpi.first.wpilibj.networktables2.stream.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Joe + */ +public class Robot { + + public static final String PREF_SAVE_FIELD = "~S A V E~"; + public static final String TABLE_NAME = "SmartDashboard"; + public static final String LIVE_WINDOW_NAME = "LiveWindow"; + public static final String PREFERENCES_NAME = "Preferences"; + + + private static volatile String _host = null; + private static volatile int _port = NetworkTable.DEFAULT_PORT; + private static final IOStreamFactory configurableFactory = new IOStreamFactory() { + @Override + public IOStream createStream() throws IOException { + if(_host==null) + return null; + return new SocketStream(_host, _port); + } + }; + public static final NetworkTableClient client = new NetworkTableClient(configurableFactory); + private static final NetworkTableProvider provider = new NetworkTableProvider(client); + static{ + NetworkTable.setTableProvider(provider); + } + + public static void setTeam(int team) { + setHost("10." + (team / 100) + "." + (team % 100) + ".2"); + } + public static void setHost(String host){ + _host = host; + System.out.println("Host: "+host); + client.close(); + } + public static void setPort(int port){ + _port = port; + client.close(); + } + + public static ITable getTable(String tableName) { + return provider.getRootTable().getSubTable(tableName); + } + + public static ITable getTable() { + return provider.getRootTable().getSubTable(TABLE_NAME); + } + + public static ITable getPreferences() { + return provider.getRootTable().getSubTable(PREFERENCES_NAME); + } + public static ITable getLiveWindow() { + return provider.getRootTable().getSubTable(LIVE_WINDOW_NAME); + } + + public static void addConnectionListener(IRemoteConnectionListener listener, boolean immediateNotify) { + client.addConnectionListener(listener, immediateNotify); + } + public static void removeConnectionListener(IRemoteConnectionListener listener) { + client.removeConnectionListener(listener); + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/DataType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/DataType.java new file mode 100644 index 0000000..01a8356 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/DataType.java @@ -0,0 +1,112 @@ +package edu.wpi.first.smartdashboard.types; + +import edu.wpi.first.smartdashboard.gui.Widget; +import edu.wpi.first.smartdashboard.gui.elements.TextBox; +import edu.wpi.first.wpilibj.tables.ITable; + +/** + * + * @author Joe Grinstead + */ +public class DataType { + + private final DataType[] parents; + private final String name; + private Class defaultClass; + + // This is package in order to prevent others from subclassing DataType directly. + DataType(String name, DataType... parents) { + this.name = name; + this.parents = parents; + } + + // This is package in order to prevent others from subclassing DataType directly. + DataType(String name, Class defaultClass, DataType... parents) { + this.name = name; + this.parents = parents; + this.defaultClass = defaultClass; + } + + public void setDefault(Class defaultClass) { + this.defaultClass = defaultClass; + } + + public Class getDefault() { + return defaultClass; + } + + public DataType[] getParents() { + return parents.clone(); + } + + public boolean isNamed() { + return false; + } + + public String getName() { + return name; + } + + public boolean isChildOf(DataType parent) { + if (equals(parent)) { + return true; + } else for (int i = 0; i < parents.length; i++) { + if (parents[i].isChildOf(parent)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return "[Type:" + name + "]"; + } + + public static DataType getType(String type, boolean isNamedType) { + if (isNamedType) { + return NamedDataType.get(type); + } + if (type.equals(NUMBER.getName())) { + return NUMBER; + } else if (type.equals(BOOLEAN.getName())) { + return BOOLEAN; + } else if (type.equals(BASIC.getName())) { + return BASIC; + } else if (type.equals(STRING.getName())) { + return STRING; + } else if (type.equals(TABLE.getName())) { + return TABLE; + } else { + return NamedDataType.get(type); + } + } + + public static DataType getType(Object value) { + if (value == null) { + throw new IllegalArgumentException("Can not be given null value"); + } else if (value instanceof ITable) { + ITable table = (ITable) value; + + if (table.containsKey("~TYPE~")) { + String typeName = table.getString("~TYPE~"); + return NamedDataType.get(typeName); + } else { + return DataType.TABLE; + } + } else if (value instanceof Double) { + return DataType.NUMBER; + } else if (value instanceof Boolean) { + return DataType.BOOLEAN; + } else if (value instanceof String) { // String + return DataType.STRING; + } else { + throw new IllegalArgumentException("Can not get type for class:" + value.getClass().getName()); + } + } + public static final DataType BASIC = new DataType("Basic", TextBox.class); + public static final DataType NUMBER = new DataType("Number", TextBox.class, BASIC); + public static final DataType BOOLEAN = new DataType("Boolean", TextBox.class, BASIC); + public static final DataType STRING = new DataType("String", TextBox.class, BASIC); + public static final DataType TABLE = new DataType("Table"); +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/DisplayElementRegistry.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/DisplayElementRegistry.java new file mode 100644 index 0000000..71d784c --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/DisplayElementRegistry.java @@ -0,0 +1,210 @@ +package edu.wpi.first.smartdashboard.types; + +import edu.wpi.first.smartdashboard.gui.StaticWidget; +import edu.wpi.first.smartdashboard.gui.Widget; +import edu.wpi.first.smartdashboard.gui.elements.DefaultDisplayElementRegistrar; +import edu.wpi.first.smartdashboard.livewindow.elements.LiveWindowWidgetRegistrar; +import edu.wpi.first.smartdashboard.types.named.PIDCommandType; +import edu.wpi.first.smartdashboard.types.named.PIDSubsystemType; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +/** + * This class contains several {@code static} methods that are used to + * dynamically link widgets to the types of data that they can display. + * + * @author Joe Grinstead + */ +public class DisplayElementRegistry { + + /** The widgets that we have available */ + private static final Set> staticWidgets = new LinkedHashSet>(); + + /** Maps types to the widgets that explicitly support that type */ + private static final Map>> map = new HashMap>>(); + + /** Maps the widgets to the types that they explicitly support */ + private static final Map, DataType[]> declaredTypes = new HashMap, DataType[]>(); + + /** + * Forces the DisplayElementRegistry to register all widgets and types + * supplied in SmartDashboard. This method does not read through the + * file system to find extensions, that is done by {@link FileSniffer}. + */ + static{ + DefaultDisplayElementRegistrar.init(); + LiveWindowWidgetRegistrar.init(); + + // There is a awkward problem where if there is no widget that + // explicitly supports a type, + // the type must be initialized manually + PIDCommandType.get(); + PIDSubsystemType.get(); + } + + /** + * Adds the new {@link StaticWidget} to the registry. + * + * @param clazz + * the class of the {@link StaticWidget}. If it is an abstract + * class, then it will be ignored. + */ + public static void registerStaticWidget(Class clazz) { + if (!Modifier.isAbstract(clazz.getModifiers())) { + staticWidgets.add(clazz); + } + // TODO made this complain if given a normal widget, or make it call + // registerWidget + } + + /** + * Adds the new {@link Widget} to the registry. There are plenty of + * requirements that a {@link Widget} must satisfy, because the + * {@link DisplayElementRegistry} relies heavily on reflection in order to + * simplify code for an extension developer. Make sure to look at what will + * cause a {@link RuntimeException} + * + * @param clazz + * the class of the {@link Widget}. If it is an abstract class, + * then it will be ignored. + * @throws RuntimeException + * there are several ways that this will be thrown. + * + *

+ *

    + *
  • If there is no TYPES field + *
  • If the TYPES field is not static + *
  • If the TYPES field is not final + *
  • If the TYPES field is not public + *
  • If the TYPES field is not an array of {@link DataType} + *
  • If the TYPES field is null + *
+ *

+ */ + public static void registerWidget(Class clazz) { + if (Modifier.isAbstract(clazz.getModifiers())) { + return; + } + + DataType[] types = null; + + try { + Field field = clazz.getDeclaredField("TYPES"); + int modifiers = field.getModifiers(); + if (!Modifier.isStatic(modifiers)) { + throw new RuntimeException("TYPES must be static"); + } else if (!Modifier.isFinal(modifiers)) { + throw new RuntimeException("TYPES must be final"); + } + types = (DataType[]) field.get(null); + declaredTypes.put(clazz, types); + } catch (IllegalArgumentException ex) { + assert false; + } catch (IllegalAccessException ex) { + throw new RuntimeException("TYPES must be public"); + } catch (NoSuchFieldException ex) { + throw new RuntimeException("Every ValueBasedDisplayElement must have a TYPES static field of type DataType[]"); + } catch (SecurityException ex) { + ex.printStackTrace(); + return; + } catch (ClassCastException ex) { + throw new RuntimeException("TYPES must be of type Type[]"); + } + + if (types == null) { + throw new RuntimeException("TYPES must not be null"); + } + + for (DataType type : types) { + Set> list = map.get(type); + if (list == null) { + map.put(type, list = new LinkedHashSet>()); + } + list.add(clazz); + } + } + + /** + * Used internally to generate the types that a widget supports. Widgets + * support more than what they explicitly support, they also deal with + * parent types and what not, so that is what this will figure out. + * + * @param set + * the set that we will add the types to + * @param types + * the types that a widget supports + * @return the set that was given (except it will have types added to it) + */ + private static Set generateTypes(Set set, DataType[] types) { + for (DataType type : types) { + if (set.add(type)) { + generateTypes(set, type.getParents()); + } + } + return set; + } + + /** + * Returns all the widgets which support a given type. + * + * @param type + * the type to support + * @return the widgets which support the given type + */ + public static Set> getWidgetsForType(DataType type) { + Set types = generateTypes(new LinkedHashSet(), type.getParents()); + types.add(type); + + Set> elements = new LinkedHashSet>(); + + for (DataType t : types) { + Set> set = map.get(t); + if (set != null) { + Class priority = t.getDefault(); + if (priority != null) { + elements.add(priority); + } + + } + } + for (DataType t : types) { + Set> set = map.get(t); + if (set != null) { + elements.addAll(set); + } + } + + return elements; + } + + /** + * Returns all the {@link StaticWidget StaticWidgets} that are registered. + */ + public static Set> getStaticWidgets() { + return staticWidgets; + } + + /** + * Returns whether a {@link Widget} of the given class can handle an element + * of the given type. + * + * @param clazz + * the class of the {@link Widget} + * @param type + * the {@link DataType} that we are seeing if the {@link Widget} + * supports + * @return whether or not it is a supported type + */ + public static boolean supportsType(Class clazz, DataType type) { + for (DataType declaredType : declaredTypes.get(clazz)) { + if (type.isChildOf(declaredType)) { + return true; + } + } + return false; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/NamedDataType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/NamedDataType.java new file mode 100644 index 0000000..c1bedcb --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/NamedDataType.java @@ -0,0 +1,49 @@ +package edu.wpi.first.smartdashboard.types; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.gui.elements.*; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author Joe Grinstead + */ +public class NamedDataType extends DataType { + + private static final Map map = new HashMap(); + + public static NamedDataType get(String name) { + return map.get(name); + } + + protected NamedDataType(String name, DataType... parents) { + super(name, parents); + if (name == null) { + throw new IllegalArgumentException("Name can not be null"); + } + if (map.containsKey(name)) { + throw new IllegalArgumentException("Given name \"" + name + "\" has already been claimed"); + } else { + map.put(name, this); + } + } + + protected NamedDataType(String name, Class defaultWidget, DataType... parents) { + super(name, defaultWidget, parents); + if (name == null) { + throw new IllegalArgumentException("Name can not be null"); + } + if (map.containsKey(name)) { + throw new IllegalArgumentException("Given name \"" + name + "\" has already been claimed"); + } else { + map.put(name, this); + } + } + + @Override + public final boolean isNamed() { + return true; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AccelerometerType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AccelerometerType.java new file mode 100644 index 0000000..6282081 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AccelerometerType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.SingleNumberDisplay; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class AccelerometerType extends NamedDataType { + + public static final String LABEL = "Accelerometer"; + + private AccelerometerType() { + super(LABEL, SingleNumberDisplay.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new AccelerometerType(); + } + } + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AnalogInputType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AnalogInputType.java new file mode 100644 index 0000000..35f0022 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AnalogInputType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.SingleNumberDisplay; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class AnalogInputType extends NamedDataType { + + public static final String LABEL = "Analog Input"; + + private AnalogInputType() { + super(LABEL, SingleNumberDisplay.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new AnalogInputType(); + } + } + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ButtonType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ButtonType.java new file mode 100644 index 0000000..ac6ea0f --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ButtonType.java @@ -0,0 +1,25 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author Joe Grinstead + */ +public class ButtonType extends NamedDataType { + + public static final String LABEL = "Button"; + + private ButtonType() { + super(LABEL, Button.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new ButtonType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CommandType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CommandType.java new file mode 100644 index 0000000..28db593 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CommandType.java @@ -0,0 +1,25 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author Joe Grinstead + */ +public class CommandType extends NamedDataType { + + public static final String LABEL = "Command"; + + private CommandType() { + super(LABEL, Command.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new CommandType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompassType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompassType.java new file mode 100644 index 0000000..a079990 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompassType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.GyroDisplay; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class CompassType extends NamedDataType { + + public static final String LABEL = "Compass"; + + private CompassType() { + super(LABEL, GyroDisplay.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new CompassType(); + } + } + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompressorType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompressorType.java new file mode 100644 index 0000000..394d486 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompressorType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.DigitalOutputController; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class CompressorType extends NamedDataType { + + public static final String LABEL = "Compressor"; + + private CompressorType() { + super(LABEL, DigitalOutputController.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new CompressorType(); + } + } + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CounterType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CounterType.java new file mode 100644 index 0000000..761f039 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CounterType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.SingleNumberDisplay; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class CounterType extends NamedDataType { + + public static final String LABEL = "Counter"; + + private CounterType() { + super(LABEL, SingleNumberDisplay.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new CounterType(); + } + } + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalInputType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalInputType.java new file mode 100644 index 0000000..a6477a0 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalInputType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.DigitalInputDisplay; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class DigitalInputType extends NamedDataType { + + public static final String LABEL = "Digital Input"; + + private DigitalInputType() { + super(LABEL, DigitalInputDisplay.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new DigitalInputType(); + } + } + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalOutputType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalOutputType.java new file mode 100644 index 0000000..c8847e8 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalOutputType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.DigitalOutputController; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class DigitalOutputType extends NamedDataType { + + public static final String LABEL = "Digital Output"; + + private DigitalOutputType() { + super(LABEL, DigitalOutputController.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new DigitalOutputType(); + } + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DoubleSolenoidType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DoubleSolenoidType.java new file mode 100644 index 0000000..79b1a3f --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DoubleSolenoidType.java @@ -0,0 +1,31 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.DigitalOutputController; +import edu.wpi.first.smartdashboard.livewindow.elements.RelayController; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class DoubleSolenoidType extends NamedDataType { + + public static final String LABEL = "Double Solenoid"; + + private DoubleSolenoidType() { + super(LABEL, RelayController.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new DoubleSolenoidType(); + } + } + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/EncoderType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/EncoderType.java new file mode 100644 index 0000000..7972617 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/EncoderType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.EncoderDisplay; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class EncoderType extends NamedDataType { + + public static final String LABEL = "Encoder"; + + private EncoderType() { + super(LABEL, EncoderDisplay.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new EncoderType(); + } + } + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GearToothSensorType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GearToothSensorType.java new file mode 100644 index 0000000..da8537b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GearToothSensorType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.SingleNumberDisplay; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class GearToothSensorType extends NamedDataType { + + public static final String LABEL = "Gear Tooth"; + + private GearToothSensorType() { + super(LABEL, SingleNumberDisplay.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new GearToothSensorType(); + } + } + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GyroType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GyroType.java new file mode 100644 index 0000000..866f235 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GyroType.java @@ -0,0 +1,25 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author Joe Grinstead + */ +public class GyroType extends NamedDataType { + + public static final String LABEL = "Gyro"; + + private GyroType() { + super(LABEL, Compass.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new GyroType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWGyroType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWGyroType.java new file mode 100644 index 0000000..37c1bad --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWGyroType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.GyroDisplay; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class LWGyroType extends NamedDataType { + + public static final String LABEL = "LWGyro"; + + private LWGyroType() { + super(LABEL, GyroDisplay.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new LWGyroType(); + } + } + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWSubsystemType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWSubsystemType.java new file mode 100644 index 0000000..1971786 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWSubsystemType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.LWSubsystem; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class LWSubsystemType extends NamedDataType { + + public static final String LABEL = "LW Subsystem"; + + private LWSubsystemType() { + super(LABEL, LWSubsystem.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new LWSubsystemType(); + } + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDCommandType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDCommandType.java new file mode 100644 index 0000000..a4a6407 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDCommandType.java @@ -0,0 +1,24 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Joe Grinstead + */ +public class PIDCommandType extends NamedDataType { + + public static final String LABEL = "PIDCommand"; + + private PIDCommandType() { + super(LABEL, CommandType.get(), PIDType.get()); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new PIDCommandType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDSubsystemType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDSubsystemType.java new file mode 100644 index 0000000..abcd121 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDSubsystemType.java @@ -0,0 +1,24 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Joe Grinstead + */ +public class PIDSubsystemType extends NamedDataType { + + public static final String LABEL = "PIDSubsystem"; + + private PIDSubsystemType() { + super(LABEL, SubsystemType.get(), PIDType.get()); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new PIDSubsystemType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDType.java new file mode 100644 index 0000000..4d66345 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDType.java @@ -0,0 +1,25 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.gui.elements.PIDEditor; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Joe Grinstead + */ +public class PIDType extends NamedDataType { + + public static final String LABEL = "PIDController"; + + private PIDType() { + super(LABEL, PIDEditor.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new PIDType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/QuadratureEncoderType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/QuadratureEncoderType.java new file mode 100644 index 0000000..dbc5ca2 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/QuadratureEncoderType.java @@ -0,0 +1,31 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.EncoderDisplay; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class QuadratureEncoderType extends NamedDataType { + + public static final String LABEL = "Quadrature Encoder"; + + private QuadratureEncoderType() { + super(LABEL, EncoderDisplay.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new QuadratureEncoderType(); + } + } + + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/RelayType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/RelayType.java new file mode 100644 index 0000000..034bd04 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/RelayType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.RelayController; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class RelayType extends NamedDataType { + + public static final String LABEL = "Relay"; + + private RelayType() { + super(LABEL, RelayController.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new RelayType(); + } + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SchedulerType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SchedulerType.java new file mode 100644 index 0000000..3b3e0a1 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SchedulerType.java @@ -0,0 +1,24 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Joe Grinstead + */ +public class SchedulerType extends NamedDataType { + + public static final String LABEL = "Scheduler"; + + private SchedulerType() { + super(LABEL); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new SchedulerType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ServoType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ServoType.java new file mode 100644 index 0000000..19dc948 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ServoType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.ServoController; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class ServoType extends NamedDataType { + + public static final String LABEL = "Servo"; + + private ServoType() { + super(LABEL, ServoController.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new ServoType(); + } + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SolenoidType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SolenoidType.java new file mode 100644 index 0000000..9b1dc70 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SolenoidType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.DigitalOutputController; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class SolenoidType extends NamedDataType { + + public static final String LABEL = "Solenoid"; + + private SolenoidType() { + super(LABEL, DigitalOutputController.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new SolenoidType(); + } + } + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SpeedControllerType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SpeedControllerType.java new file mode 100644 index 0000000..4a0a504 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SpeedControllerType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.SpeedController; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class SpeedControllerType extends NamedDataType { + + public static final String LABEL = "Speed Controller"; + + private SpeedControllerType() { + super(LABEL, SpeedController.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new SpeedControllerType(); + } + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/StringChooserType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/StringChooserType.java new file mode 100644 index 0000000..6f51966 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/StringChooserType.java @@ -0,0 +1,24 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Joe Grinstead + */ +public class StringChooserType extends NamedDataType { + + public static final String LABEL = "String Chooser"; + + private StringChooserType() { + super(LABEL); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new StringChooserType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SubsystemType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SubsystemType.java new file mode 100644 index 0000000..2b1b3d8 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SubsystemType.java @@ -0,0 +1,25 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author Joe Grinstead + */ +public class SubsystemType extends NamedDataType { + + public static final String LABEL = "Subsystem"; + + private SubsystemType() { + super(LABEL, Subsystem.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new SubsystemType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/UltrasonicType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/UltrasonicType.java new file mode 100644 index 0000000..2db0134 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/UltrasonicType.java @@ -0,0 +1,30 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.SingleNumberDisplay; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class UltrasonicType extends NamedDataType { + + public static final String LABEL = "Ultrasonic"; + + private UltrasonicType() { + super(LABEL, SingleNumberDisplay.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new UltrasonicType(); + } + } + +} \ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLReader.java b/smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLReader.java new file mode 100644 index 0000000..1029bea --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLReader.java @@ -0,0 +1,248 @@ +package edu.wpi.first.smartdashboard.xml; + +import edu.wpi.first.smartdashboard.livewindow.elements.LWSubsystem; +import java.awt.*; +import java.io.*; +import java.util.*; +import java.util.List; + +import javax.xml.parsers.*; + +import org.w3c.dom.*; + +/** + * A class used to read data from an XML save file that contains + * the information for size and location of widgets in the + * SmartDashboard and LiveWindow. + */ +public class SmartDashboardXMLReader { + + /** An ArrayList of all the widgets in the XML file*/ + private final List widgets = new ArrayList(); + /** Maps a subsystem to a Map of components and their locations */ + private final Map> subsystems = new HashMap>(); + + private List hiddenFields = new ArrayList(); + private Map properties = new HashMap(); + private boolean finishedReading = false; + private SmartDashboardXMLReader self = this; + + private class ReaderThread extends Thread { + + File xmlFile; + + ReaderThread(String fileName) { + xmlFile = new File(fileName); + } + + @Override + public void run() { + try { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.parse(xmlFile); + doc.getDocumentElement().normalize(); + + NodeList listSD = doc.getElementsByTagName("dashboard"); + NodeList listLW = doc.getElementsByTagName("live-window"); + + if (listSD.getLength() == 0 && listLW.getLength() == 0) { + return; + } + + load(listSD.item(0).getChildNodes()); + load(listLW.item(0).getChildNodes()); + + } catch (Exception e) { + System.out.println("Error while reading "+xmlFile); + e.printStackTrace(); + } + + finishedReading = true; + } + } + + private void load(NodeList elements) { + for (int e = 0; e < elements.getLength(); e++) { + boolean isWidget = elements.item(e).getNodeName().equals("widget"); + if (isWidget || elements.item(e).getNodeName().equals("static-widget")) { + NamedNodeMap atrib = elements.item(e).getAttributes(); + XMLWidget widget = new XMLWidget(); + System.out.println(); + for (int a = 0; a < atrib.getLength(); a++) { + if (atrib.item(a).getNodeName().equals("field")) { + widget.setField(atrib.item(a).getNodeValue()); + } else if (atrib.item(a).getNodeName().equals("class")) { + widget.setClass(atrib.item(a).getNodeValue()); + } else if (atrib.item(a).getNodeName().equals("type")) { + widget.setType(atrib.item(a).getNodeValue()); + } + } + + Map subwidgets = new TreeMap(); + + NodeList values = elements.item(e).getChildNodes(); + for (int a = 0; a < values.getLength(); a++) { + if (values.item(a).getNodeName().trim().equals("location")) { + int x = 0, y = 0; + NamedNodeMap location = values.item(a).getAttributes(); + for (int b = 0; b < location.getLength(); b++) { + + if (location.item(b).getNodeName().equals("x")) { + x = Integer.parseInt(location.item( + b).getNodeValue()); + } else if (location.item(b).getNodeName().equals("y")) { + y = Integer.parseInt(location.item( + b).getNodeValue()); + } + } + widget.setLocation(new Point(x, y)); + } else if (values.item(a).getNodeName().trim().equals("width")) { + widget.setWidth(Integer.parseInt(values.item(a).getChildNodes().item(0).getNodeValue())); + } else if (values.item(a).getNodeName().trim().equals("height")) { + widget.setHeight(Integer.parseInt(values.item(a).getChildNodes().item(0).getNodeValue())); + } else if (values.item(a).getNodeName().trim().equals("property")) { + NamedNodeMap propAtribs = values.item(a).getAttributes(); + String name = null, value = null; + for (int b = 0; b < propAtribs.getLength(); b++) { + if (propAtribs.item(b).getNodeName().equals("name")) { + name = propAtribs.item(b).getNodeValue(); + } else if (propAtribs.item(b).getNodeName().equals("value")) { + value = propAtribs.item(b).getNodeValue(); + } + } + if (name != null && value != null) { + widget.addProperty(name, value); + } + } else if(values.item(a).getNodeName().trim().equals("widget")) { + LWSubsystem.setLoaded(self); // prevents subsystems from regenerating if widgets exist + XMLWidget subwidget = new XMLWidget(); + NamedNodeMap attributes = values.item(a).getAttributes(); + NodeList subvalues = values.item(a).getChildNodes(); + for (int b = 0; b < attributes.getLength(); b++) { + if (attributes.item(b).getNodeName().equals("field")) { + subwidget.setField(attributes.item(b).getNodeValue()); + } else if (attributes.item(b).getNodeName().equals("class")) { + subwidget.setClass(attributes.item(b).getNodeValue()); + } else if (attributes.item(b).getNodeName().equals("type")) { + subwidget.setType(attributes.item(b).getNodeValue()); + } + } + System.out.println("\nLoading subwidget \""+subwidget.getField()+"\""); + for(int b = 0; b < subvalues.getLength(); b++) { + String nodename = subvalues.item(b).getNodeName().trim(); + if(nodename.equals("location")) { + int x = 0, y = 0; + NamedNodeMap location = subvalues.item(b).getAttributes(); + for (int c = 0; c < location.getLength(); c++) { + if (location.item(c).getNodeName().trim().equals("x")) { + x = Integer.parseInt(location.item(c).getNodeValue()); + } else if (location.item(c).getNodeName().trim().equals("y")) { + y = Integer.parseInt(location.item(c).getNodeValue()); + } + } + subwidget.setLocation(new Point(x, y)); + } else if(nodename.equals("height")) { + subwidget.setHeight(Integer.parseInt(subvalues.item(b).getChildNodes().item(0).getNodeValue())); + } else if(nodename.equals("width")) { + subwidget.setWidth(Integer.parseInt(subvalues.item(b).getChildNodes().item(0).getNodeValue())); + } + } + System.out.println("\tLocation: ["+subwidget.getLocation().x+","+subwidget.getLocation().y +"]" + + "\n\tSize: ("+subwidget.getSize().width+","+subwidget.getSize().height+")"); + subwidgets.put(subwidget.getLocation().y, subwidget); + subsystems.put(widget, subwidgets); + } + } + if(!widget.getElementClass().contains("livewindow.elements")) { + widgets.add(widget); + } + } else if (elements.item(e).getNodeName().equals("hidden")) { + NamedNodeMap atrib = elements.item(e).getAttributes(); + for (int a = 0; a < atrib.getLength(); a++) { + if (atrib.item(a).getNodeName().equals("field")) { + hiddenFields.add(atrib.item(a).getNodeValue()); + } + } + } else if (elements.item(e).getNodeName().equals("property")) { + NamedNodeMap propAtribs = elements.item(e).getAttributes(); + String name = null, value = null; + for (int b = 0; b < propAtribs.getLength(); b++) { + if (propAtribs.item(b).getNodeName().equals("name")) { + name = propAtribs.item(b).getNodeValue(); + } else if (propAtribs.item(b).getNodeName().equals("value")) { + value = propAtribs.item(b).getNodeValue(); + } + } + if (name != null && value != null) { + properties.put(name, value); + } + } + } + } + + public SmartDashboardXMLReader(String fileName) + throws FileNotFoundException { + self = this; + new ReaderThread(fileName).start(); + } + + private void waitToFinish() { + while (!finishedReading) { + try { + Thread.sleep(25); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + public List getXMLWidgets() { + waitToFinish(); + return widgets; + } + + public Map getSubwidgetMap(XMLWidget subsystem) { + waitToFinish(); + return subsystems.get(subsystem); + } + + public Map> getSubsystems() { + waitToFinish(); + return subsystems; + } + + public boolean containsWidgetOfName(LWSubsystem subsystem, String name) { + XMLWidget correspondingSystem = null; + for(XMLWidget w : subsystems.keySet()) { + if(((LWSubsystem)w.convertToDisplayElement()).getFieldName().equals(subsystem.getFieldName())) { + correspondingSystem = w; + } + } + if(correspondingSystem != null) { + for(XMLWidget w : subsystems.get(correspondingSystem).values()) { + String othername = w.getField(); + if(w == null || othername == null) return false; + if(othername.equals(name)) { + return true; + } + } + } + return false; + } + + public List getHiddenFields() { + waitToFinish(); + return hiddenFields; + } + + public Map getProperties() { + waitToFinish(); + return properties; + } + + public boolean isFinishedReading() { + return finishedReading; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLWriter.java b/smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLWriter.java new file mode 100644 index 0000000..427d78a --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLWriter.java @@ -0,0 +1,99 @@ +package edu.wpi.first.smartdashboard.xml; + +import java.awt.Point; +import java.io.FileWriter; +import java.io.IOException; + +public class SmartDashboardXMLWriter { + + String fileName; + FileWriter writer; + + public SmartDashboardXMLWriter(String fileName) throws IOException { + this.fileName = fileName; + writer = new FileWriter(fileName); + writer.write("\n"); + } + + public void beginSmartDashboard() throws IOException { + writer.write("\n"); + } + + public void endSmartDashboard() throws IOException { + writer.write("\n"); + } + + public void beginLiveWindow() throws IOException { + writer.write("\n"); + } + + public void endLiveWindow() throws IOException { + writer.write("\n"); + } + + public void beginStaticWidget(String className) throws IOException { + writer.write("\t\n"); + } + + public void endStaticWidget() throws IOException { + writer.write("\t\n"); + } + + public void beginWidget(String field, String className) throws IOException { + writer.write("\t\n"); + } + + public void beginWidget(String field, String type, String className) throws IOException { + writer.write("\t\n"); + } + + public void endWidget() throws IOException { + writer.write("\t\n"); + } + + public void addHiddenField(String field) throws IOException { + writer.write("\t\n"); + } + + public void addLocation(Point p) throws IOException { + writer.write("\t\t\n"); + } + + public void addSubWidget(String field, String type, String className) throws IOException{ + writer.write("\t\t\n"); + } + + public void endSubWidget() throws IOException { + writer.write("\t\t\n"); + } + + public void addSubWidgetLocation(Point p) throws IOException { + writer.write("\t\t\t\n"); + } + + public void addSubWudgetHeight(int height) throws IOException{ + writer.write("\t\t\t" + height + "\n"); + } + + public void addSubWidgetWidth(int width) throws IOException { + writer.write("\t\t\t" + width + "\n"); + } + + public void addWidth(int width) throws IOException { + writer.write("\t\t" + width + "\n"); + } + + public void addHeight(int height) throws IOException { + writer.write("\t\t" + height + "\n"); + } + + public void addProperty(String name, String value) throws IOException { + writer.write("\t\t\n"); + + } + + public void close() throws IOException { + writer.write(""); + writer.close(); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/xml/XMLWidget.java b/smartdashboard/src/edu/wpi/first/smartdashboard/xml/XMLWidget.java new file mode 100644 index 0000000..bb3508b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/xml/XMLWidget.java @@ -0,0 +1,136 @@ +package edu.wpi.first.smartdashboard.xml; + +import java.awt.*; +import java.util.*; +import java.util.logging.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +public class XMLWidget { + + Point location = null; + Dimension size = null; + HashMap properties = new HashMap(); + String field; + String className = ""; + DataType type; + + public XMLWidget() { + } + + public void setLocation(Point p) { + location = p; + } + + public void setSize(Dimension d) { + size = d; + } + + public void setClass(String c) { + className = c; + } + + public void setField(String f) { + field = f; + } + + public void setType(String type) { + this.type = DataType.getType(type, false); + } + + public void setWidth(int width) { + if (size != null) { + size.width = width; + } else { + size = new Dimension(width, -1); + } + } + + public void setHeight(int height) { + if (size != null) { + size.height = height; + } else { + size = new Dimension(-1, height); + } + } + + public void addProperty(String name, String value) { + properties.put(name, value); + } + + public boolean hasLocation() { + return location != null; + } + + public boolean hasSize() { + return size != null; + } + + public Map getProperties() { + return properties; + } + + public Dimension getSize() { + return size; + } + + public DataType getType() { + return type; + } + + public Point getLocation() { + return location; + } + + public String getElementClass() { + return className; + } + + public String getField() { + return field; + } + + public DisplayElement convertToDisplayElement() { + DisplayElement element = null; + try { + element = (DisplayElement) Class.forName(className).newInstance(); + if (field != null) { + ((Widget) element).setFieldName(field); + } + if (size != null) { + element.setSavedSize(size); + } + if (location != null) { + element.setSavedLocation(location); + } + for (String key : properties.keySet()) { + Property property = element.getProperties().get(key); + if (property != null) { + property.setSaveValue(properties.get(key)); + } + } + } catch (Exception ex) { + Logger.getLogger(XMLWidget.class.getName()).log(Level.SEVERE, null, ex); + } + return element; + } + + @Override + public String toString() { +// String returnValue = "ClassName: " + className + "\nField: " + field + "\nSize: " + size +// + "\nLocation: " + location + "\nProperties"; +// +// for (String s : properties.keySet()) { +// String p = properties.get(s); +// returnValue = returnValue + "\n\nName: " + s +// + "\nValue: " + p; +// } +// +// return returnValue; +// return ((Widget)convertToDisplayElement()).getFieldName(); + return getField(); + } +} -- cgit v1.2.3-54-g00ecf