[wpilib] Deprecate getInstance() in favor of static functions (#3440)

Co-authored-by: Noam Zaks <imnoamzaks@gmail.com>
This commit is contained in:
Peter Johnson
2021-06-15 23:06:03 -07:00
committed by GitHub
parent 26ff9371d9
commit 362066a9b7
105 changed files with 1500 additions and 1539 deletions

View File

@@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* Singleton class for creating and keeping camera servers. Also publishes camera information to
* NetworkTables.
*/
@SuppressWarnings("PMD.UnusedPrivateField")
public final class CameraServer {
public static final int kBasePort = 1181;
@@ -46,8 +47,9 @@ public final class CameraServer {
/**
* Get the CameraServer instance.
*
* @return CameraServer instance
* @deprecated Use the static methods
*/
@Deprecated
public static synchronized CameraServer getInstance() {
if (server == null) {
server = new CameraServer();
@@ -55,18 +57,211 @@ public final class CameraServer {
return server;
}
private final AtomicInteger m_defaultUsbDevice;
private String m_primarySourceName;
private final Map<String, VideoSource> m_sources;
private final Map<String, VideoSink> m_sinks;
private final Map<Integer, NetworkTable> m_tables; // indexed by source handle
private static final AtomicInteger m_defaultUsbDevice = new AtomicInteger();
private static String m_primarySourceName;
private static final Map<String, VideoSource> m_sources = new HashMap<>();
private static final Map<String, VideoSink> m_sinks = new HashMap<>();
private static final Map<Integer, NetworkTable> m_tables =
new HashMap<>(); // indexed by source handle
// source handle indexed by sink handle
private final Map<Integer, Integer> m_fixedSources;
private final NetworkTable m_publishTable;
private final VideoListener m_videoListener; // NOPMD
private final int m_tableListener; // NOPMD
private int m_nextPort;
private String[] m_addresses;
private static final Map<Integer, Integer> m_fixedSources = new HashMap<>();
private static final NetworkTable m_publishTable =
NetworkTableInstance.getDefault().getTable(kPublishName);
// We publish sources to NetworkTables using the following structure:
// "/CameraPublisher/{Source.Name}/" - root
// - "source" (string): Descriptive, prefixed with type (e.g. "usb:0")
// - "streams" (string array): URLs that can be used to stream data
// - "description" (string): Description of the source
// - "connected" (boolean): Whether source is connected
// - "mode" (string): Current video mode
// - "modes" (string array): Available video modes
// - "Property/{Property}" - Property values
// - "PropertyInfo/{Property}" - Property supporting information
// Listener for video events
private static final VideoListener m_videoListener =
new VideoListener(
event -> {
switch (event.kind) {
case kSourceCreated:
{
// Create subtable for the camera
NetworkTable table = m_publishTable.getSubTable(event.name);
m_tables.put(event.sourceHandle, table);
table.getEntry("source").setString(makeSourceValue(event.sourceHandle));
table
.getEntry("description")
.setString(CameraServerJNI.getSourceDescription(event.sourceHandle));
table
.getEntry("connected")
.setBoolean(CameraServerJNI.isSourceConnected(event.sourceHandle));
table
.getEntry("streams")
.setStringArray(getSourceStreamValues(event.sourceHandle));
try {
VideoMode mode = CameraServerJNI.getSourceVideoMode(event.sourceHandle);
table.getEntry("mode").setDefaultString(videoModeToString(mode));
table.getEntry("modes").setStringArray(getSourceModeValues(event.sourceHandle));
} catch (VideoException ignored) {
// Do nothing. Let the other event handlers update this if there is an error.
}
break;
}
case kSourceDestroyed:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("source").setString("");
table.getEntry("streams").setStringArray(new String[0]);
table.getEntry("modes").setStringArray(new String[0]);
}
break;
}
case kSourceConnected:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
// update the description too (as it may have changed)
table
.getEntry("description")
.setString(CameraServerJNI.getSourceDescription(event.sourceHandle));
table.getEntry("connected").setBoolean(true);
}
break;
}
case kSourceDisconnected:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("connected").setBoolean(false);
}
break;
}
case kSourceVideoModesUpdated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("modes").setStringArray(getSourceModeValues(event.sourceHandle));
}
break;
}
case kSourceVideoModeChanged:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("mode").setString(videoModeToString(event.mode));
}
break;
}
case kSourcePropertyCreated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
putSourcePropertyValue(table, event, true);
}
break;
}
case kSourcePropertyValueUpdated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
putSourcePropertyValue(table, event, false);
}
break;
}
case kSourcePropertyChoicesUpdated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
try {
String[] choices =
CameraServerJNI.getEnumPropertyChoices(event.propertyHandle);
table
.getEntry("PropertyInfo/" + event.name + "/choices")
.setStringArray(choices);
} catch (VideoException ignored) {
// ignore
}
}
break;
}
case kSinkSourceChanged:
case kSinkCreated:
case kSinkDestroyed:
case kNetworkInterfacesChanged:
{
m_addresses = CameraServerJNI.getNetworkInterfaces();
updateStreamValues();
break;
}
default:
break;
}
},
0x4fff,
true);
private static final int m_tableListener =
NetworkTableInstance.getDefault()
.addEntryListener(
kPublishName + "/",
event -> {
String relativeKey = event.name.substring(kPublishName.length() + 1);
// get source (sourceName/...)
int subKeyIndex = relativeKey.indexOf('/');
if (subKeyIndex == -1) {
return;
}
String sourceName = relativeKey.substring(0, subKeyIndex);
VideoSource source = m_sources.get(sourceName);
if (source == null) {
return;
}
// get subkey
relativeKey = relativeKey.substring(subKeyIndex + 1);
// handle standard names
String propName;
if ("mode".equals(relativeKey)) {
// reset to current mode
event.getEntry().setString(videoModeToString(source.getVideoMode()));
return;
} else if (relativeKey.startsWith("Property/")) {
propName = relativeKey.substring(9);
} else if (relativeKey.startsWith("RawProperty/")) {
propName = relativeKey.substring(12);
} else {
return; // ignore
}
// everything else is a property
VideoProperty property = source.getProperty(propName);
switch (property.getKind()) {
case kNone:
return;
case kBoolean:
// reset to current setting
event.getEntry().setBoolean(property.get() != 0);
return;
case kInteger:
case kEnum:
// reset to current setting
event.getEntry().setDouble(property.get());
return;
case kString:
// reset to current setting
event.getEntry().setString(property.getString());
return;
default:
return;
}
},
EntryListenerFlags.kImmediate | EntryListenerFlags.kUpdate);
private static int m_nextPort = kBasePort;
private static String[] m_addresses = new String[0];
@SuppressWarnings("MissingJavadocMethod")
private static String makeSourceValue(int source) {
@@ -95,7 +290,7 @@ public final class CameraServer {
}
@SuppressWarnings("MissingJavadocMethod")
private synchronized String[] getSinkStreamValues(int sink) {
private static synchronized String[] getSinkStreamValues(int sink) {
// Ignore all but MjpegServer
if (VideoSink.getKindFromInt(CameraServerJNI.getSinkKind(sink)) != VideoSink.Kind.kMjpeg) {
return new String[0];
@@ -125,7 +320,7 @@ public final class CameraServer {
}
@SuppressWarnings("MissingJavadocMethod")
private synchronized String[] getSourceStreamValues(int source) {
private static synchronized String[] getSourceStreamValues(int source) {
// Ignore all but HttpCamera
if (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source))
!= VideoSource.Kind.kHttp) {
@@ -160,7 +355,7 @@ public final class CameraServer {
}
@SuppressWarnings("MissingJavadocMethod")
private synchronized void updateStreamValues() {
private static synchronized void updateStreamValues() {
// Over all the sinks...
for (VideoSink i : m_sinks.values()) {
int sink = i.getHandle();
@@ -304,218 +499,7 @@ public final class CameraServer {
}
}
@SuppressWarnings("MissingJavadocMethod")
private CameraServer() {
m_defaultUsbDevice = new AtomicInteger();
m_sources = new HashMap<>();
m_sinks = new HashMap<>();
m_fixedSources = new HashMap<>();
m_tables = new HashMap<>();
m_publishTable = NetworkTableInstance.getDefault().getTable(kPublishName);
m_nextPort = kBasePort;
m_addresses = new String[0];
// We publish sources to NetworkTables using the following structure:
// "/CameraPublisher/{Source.Name}/" - root
// - "source" (string): Descriptive, prefixed with type (e.g. "usb:0")
// - "streams" (string array): URLs that can be used to stream data
// - "description" (string): Description of the source
// - "connected" (boolean): Whether source is connected
// - "mode" (string): Current video mode
// - "modes" (string array): Available video modes
// - "Property/{Property}" - Property values
// - "PropertyInfo/{Property}" - Property supporting information
// Listener for video events
m_videoListener =
new VideoListener(
event -> {
switch (event.kind) {
case kSourceCreated:
{
// Create subtable for the camera
NetworkTable table = m_publishTable.getSubTable(event.name);
m_tables.put(event.sourceHandle, table);
table.getEntry("source").setString(makeSourceValue(event.sourceHandle));
table
.getEntry("description")
.setString(CameraServerJNI.getSourceDescription(event.sourceHandle));
table
.getEntry("connected")
.setBoolean(CameraServerJNI.isSourceConnected(event.sourceHandle));
table
.getEntry("streams")
.setStringArray(getSourceStreamValues(event.sourceHandle));
try {
VideoMode mode = CameraServerJNI.getSourceVideoMode(event.sourceHandle);
table.getEntry("mode").setDefaultString(videoModeToString(mode));
table
.getEntry("modes")
.setStringArray(getSourceModeValues(event.sourceHandle));
} catch (VideoException ignored) {
// Do nothing. Let the other event handlers update this if there is an error.
}
break;
}
case kSourceDestroyed:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("source").setString("");
table.getEntry("streams").setStringArray(new String[0]);
table.getEntry("modes").setStringArray(new String[0]);
}
break;
}
case kSourceConnected:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
// update the description too (as it may have changed)
table
.getEntry("description")
.setString(CameraServerJNI.getSourceDescription(event.sourceHandle));
table.getEntry("connected").setBoolean(true);
}
break;
}
case kSourceDisconnected:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("connected").setBoolean(false);
}
break;
}
case kSourceVideoModesUpdated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table
.getEntry("modes")
.setStringArray(getSourceModeValues(event.sourceHandle));
}
break;
}
case kSourceVideoModeChanged:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
table.getEntry("mode").setString(videoModeToString(event.mode));
}
break;
}
case kSourcePropertyCreated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
putSourcePropertyValue(table, event, true);
}
break;
}
case kSourcePropertyValueUpdated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
putSourcePropertyValue(table, event, false);
}
break;
}
case kSourcePropertyChoicesUpdated:
{
NetworkTable table = m_tables.get(event.sourceHandle);
if (table != null) {
try {
String[] choices =
CameraServerJNI.getEnumPropertyChoices(event.propertyHandle);
table
.getEntry("PropertyInfo/" + event.name + "/choices")
.setStringArray(choices);
} catch (VideoException ignored) {
// ignore
}
}
break;
}
case kSinkSourceChanged:
case kSinkCreated:
case kSinkDestroyed:
case kNetworkInterfacesChanged:
{
m_addresses = CameraServerJNI.getNetworkInterfaces();
updateStreamValues();
break;
}
default:
break;
}
},
0x4fff,
true);
// Listener for NetworkTable events
// We don't currently support changing settings via NT due to
// synchronization issues, so just update to current setting if someone
// else tries to change it.
m_tableListener =
NetworkTableInstance.getDefault()
.addEntryListener(
kPublishName + "/",
event -> {
String relativeKey = event.name.substring(kPublishName.length() + 1);
// get source (sourceName/...)
int subKeyIndex = relativeKey.indexOf('/');
if (subKeyIndex == -1) {
return;
}
String sourceName = relativeKey.substring(0, subKeyIndex);
VideoSource source = m_sources.get(sourceName);
if (source == null) {
return;
}
// get subkey
relativeKey = relativeKey.substring(subKeyIndex + 1);
// handle standard names
String propName;
if ("mode".equals(relativeKey)) {
// reset to current mode
event.getEntry().setString(videoModeToString(source.getVideoMode()));
return;
} else if (relativeKey.startsWith("Property/")) {
propName = relativeKey.substring(9);
} else if (relativeKey.startsWith("RawProperty/")) {
propName = relativeKey.substring(12);
} else {
return; // ignore
}
// everything else is a property
VideoProperty property = source.getProperty(propName);
switch (property.getKind()) {
case kNone:
return;
case kBoolean:
// reset to current setting
event.getEntry().setBoolean(property.get() != 0);
return;
case kInteger:
case kEnum:
// reset to current setting
event.getEntry().setDouble(property.get());
return;
case kString:
// reset to current setting
event.getEntry().setString(property.getString());
return;
default:
return;
}
},
EntryListenerFlags.kImmediate | EntryListenerFlags.kUpdate);
}
private CameraServer() {}
/**
* Start automatically capturing images to send to the dashboard.
@@ -529,7 +513,7 @@ public final class CameraServer {
*
* @return The USB camera capturing images.
*/
public UsbCamera startAutomaticCapture() {
public static UsbCamera startAutomaticCapture() {
UsbCamera camera = startAutomaticCapture(m_defaultUsbDevice.getAndIncrement());
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
return camera;
@@ -544,7 +528,7 @@ public final class CameraServer {
* @param dev The device number of the camera interface
* @return The USB camera capturing images.
*/
public UsbCamera startAutomaticCapture(int dev) {
public static UsbCamera startAutomaticCapture(int dev) {
UsbCamera camera = new UsbCamera("USB Camera " + dev, dev);
startAutomaticCapture(camera);
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
@@ -558,7 +542,7 @@ public final class CameraServer {
* @param dev The device number of the camera interface
* @return The USB camera capturing images.
*/
public UsbCamera startAutomaticCapture(String name, int dev) {
public static UsbCamera startAutomaticCapture(String name, int dev) {
UsbCamera camera = new UsbCamera(name, dev);
startAutomaticCapture(camera);
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
@@ -572,7 +556,7 @@ public final class CameraServer {
* @param path The device path (e.g. "/dev/video0") of the camera
* @return The USB camera capturing images.
*/
public UsbCamera startAutomaticCapture(String name, String path) {
public static UsbCamera startAutomaticCapture(String name, String path) {
UsbCamera camera = new UsbCamera(name, path);
startAutomaticCapture(camera);
CameraServerSharedStore.getCameraServerShared().reportUsbCamera(camera.getHandle());
@@ -585,7 +569,7 @@ public final class CameraServer {
* @param camera Camera
* @return The MJPEG server serving images from the given camera.
*/
public MjpegServer startAutomaticCapture(VideoSource camera) {
public static MjpegServer startAutomaticCapture(VideoSource camera) {
addCamera(camera);
MjpegServer server = addServer("serve_" + camera.getName());
server.setSource(camera);
@@ -600,7 +584,7 @@ public final class CameraServer {
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
* @return The Axis camera capturing images.
*/
public AxisCamera addAxisCamera(String host) {
public static AxisCamera addAxisCamera(String host) {
return addAxisCamera("Axis Camera", host);
}
@@ -612,7 +596,7 @@ public final class CameraServer {
* @param hosts Array of Camera host IPs/DNS names
* @return The Axis camera capturing images.
*/
public AxisCamera addAxisCamera(String[] hosts) {
public static AxisCamera addAxisCamera(String[] hosts) {
return addAxisCamera("Axis Camera", hosts);
}
@@ -623,7 +607,7 @@ public final class CameraServer {
* @param host Camera host IP or DNS name (e.g. "10.x.y.11")
* @return The Axis camera capturing images.
*/
public AxisCamera addAxisCamera(String name, String host) {
public static AxisCamera addAxisCamera(String name, String host) {
AxisCamera camera = new AxisCamera(name, host);
// Create a passthrough MJPEG server for USB access
startAutomaticCapture(camera);
@@ -638,7 +622,7 @@ public final class CameraServer {
* @param hosts Array of Camera host IPs/DNS names
* @return The Axis camera capturing images.
*/
public AxisCamera addAxisCamera(String name, String[] hosts) {
public static AxisCamera addAxisCamera(String name, String[] hosts) {
AxisCamera camera = new AxisCamera(name, hosts);
// Create a passthrough MJPEG server for USB access
startAutomaticCapture(camera);
@@ -654,11 +638,11 @@ public final class CameraServer {
* @param name The name to give the camera
* @return The MJPEG server serving images from the given camera.
*/
public MjpegServer addSwitchedCamera(String name) {
public static MjpegServer addSwitchedCamera(String name) {
// create a dummy CvSource
CvSource source = new CvSource(name, VideoMode.PixelFormat.kMJPEG, 160, 120, 30);
MjpegServer server = startAutomaticCapture(source);
synchronized (this) {
synchronized (CameraServer.class) {
m_fixedSources.put(server.getHandle(), source.getHandle());
}
@@ -674,9 +658,9 @@ public final class CameraServer {
*
* @return OpenCV sink for the primary camera feed
*/
public CvSink getVideo() {
public static CvSink getVideo() {
VideoSource source;
synchronized (this) {
synchronized (CameraServer.class) {
if (m_primarySourceName == null) {
throw new VideoException("no camera available");
}
@@ -695,10 +679,10 @@ public final class CameraServer {
* @param camera Camera (e.g. as returned by startAutomaticCapture).
* @return OpenCV sink for the specified camera
*/
public CvSink getVideo(VideoSource camera) {
public static CvSink getVideo(VideoSource camera) {
String name = "opencv_" + camera.getName();
synchronized (this) {
synchronized (CameraServer.class) {
VideoSink sink = m_sinks.get(name);
if (sink != null) {
VideoSink.Kind kind = sink.getKind();
@@ -722,9 +706,9 @@ public final class CameraServer {
* @param name Camera name
* @return OpenCV sink for the specified camera
*/
public CvSink getVideo(String name) {
public static CvSink getVideo(String name) {
VideoSource source;
synchronized (this) {
synchronized (CameraServer.class) {
source = m_sources.get(name);
if (source == null) {
throw new VideoException("could not find camera " + name);
@@ -742,7 +726,7 @@ public final class CameraServer {
* @param height Height of the image being sent
* @return OpenCV source for the MJPEG stream
*/
public CvSource putVideo(String name, int width, int height) {
public static CvSource putVideo(String name, int width, int height) {
CvSource source = new CvSource(name, VideoMode.PixelFormat.kMJPEG, width, height, 30);
startAutomaticCapture(source);
return source;
@@ -754,9 +738,9 @@ public final class CameraServer {
* @param name Server name
* @return The MJPEG server
*/
public MjpegServer addServer(String name) {
public static MjpegServer addServer(String name) {
int port;
synchronized (this) {
synchronized (CameraServer.class) {
port = m_nextPort;
m_nextPort++;
}
@@ -770,7 +754,7 @@ public final class CameraServer {
* @param port Server port
* @return The MJPEG server
*/
public MjpegServer addServer(String name, int port) {
public static MjpegServer addServer(String name, int port) {
MjpegServer server = new MjpegServer(name, port);
addServer(server);
return server;
@@ -781,8 +765,8 @@ public final class CameraServer {
*
* @param server Server
*/
public void addServer(VideoSink server) {
synchronized (this) {
public static void addServer(VideoSink server) {
synchronized (CameraServer.class) {
m_sinks.put(server.getName(), server);
}
}
@@ -792,8 +776,8 @@ public final class CameraServer {
*
* @param name Server name
*/
public void removeServer(String name) {
synchronized (this) {
public static void removeServer(String name) {
synchronized (CameraServer.class) {
m_sinks.remove(name);
}
}
@@ -806,8 +790,8 @@ public final class CameraServer {
*
* @return The server for the primary camera feed
*/
public VideoSink getServer() {
synchronized (this) {
public static VideoSink getServer() {
synchronized (CameraServer.class) {
if (m_primarySourceName == null) {
throw new VideoException("no camera available");
}
@@ -821,8 +805,8 @@ public final class CameraServer {
* @param name Server name
* @return The server
*/
public VideoSink getServer(String name) {
synchronized (this) {
public static VideoSink getServer(String name) {
synchronized (CameraServer.class) {
return m_sinks.get(name);
}
}
@@ -832,9 +816,9 @@ public final class CameraServer {
*
* @param camera Camera
*/
public void addCamera(VideoSource camera) {
public static void addCamera(VideoSource camera) {
String name = camera.getName();
synchronized (this) {
synchronized (CameraServer.class) {
if (m_primarySourceName == null) {
m_primarySourceName = name;
}
@@ -847,8 +831,8 @@ public final class CameraServer {
*
* @param name Camera name
*/
public void removeCamera(String name) {
synchronized (this) {
public static void removeCamera(String name) {
synchronized (CameraServer.class) {
m_sources.remove(name);
}
}

View File

@@ -44,7 +44,7 @@
*
* {@literal @}Override
* public void robotInit() {
* usbCamera = CameraServer.getInstance().startAutomaticCapture(0);
* usbCamera = CameraServer.startAutomaticCapture(0);
* findTotePipeline = new MyFindTotePipeline();
* findToteThread = new VisionThread(usbCamera, findTotePipeline, this);
* }