From 7ab0ba412e897532178479f4bc6d95da0a074c80 Mon Sep 17 00:00:00 2001 From: Martin C <78377730+MilkManMaki@users.noreply.github.com> Date: Mon, 8 Apr 2024 22:45:45 -0400 Subject: [PATCH 1/4] refactor: moving examples into an examples folder in the root directory --- .../pyntcore/examples/pubsub_client.py | 41 ------ .../pyntcore/examples/pubsub_server.py | 36 ----- .../pyntcore/examples/simple_client.py | 52 ------- .../pyntcore/examples/simple_poller.py | 59 -------- subprojects/pyntcore/examples/simple_robot.py | 34 ----- .../robotpy-cscore/examples/CameraServer.java | 127 ------------------ .../robotpy-cscore/examples/cvstream.py | 39 ------ .../examples/dual_cameraserver.py | 35 ----- .../robotpy-cscore/examples/enum_usb.py | 62 --------- .../robotpy-cscore/examples/httpcvstream.py | 41 ------ .../examples/intermediate_cameraserver.py | 63 --------- .../examples/quick_cameraserver.py | 33 ----- .../robotpy-cscore/examples/settings.py | 102 -------------- .../examples/switched_cameraserver.py | 51 ------- .../robotpy-cscore/examples/usbcvstream.py | 46 ------- .../robotpy-cscore/examples/usbstream.py | 12 -- 16 files changed, 833 deletions(-) delete mode 100755 subprojects/pyntcore/examples/pubsub_client.py delete mode 100755 subprojects/pyntcore/examples/pubsub_server.py delete mode 100755 subprojects/pyntcore/examples/simple_client.py delete mode 100755 subprojects/pyntcore/examples/simple_poller.py delete mode 100755 subprojects/pyntcore/examples/simple_robot.py delete mode 100644 subprojects/robotpy-cscore/examples/CameraServer.java delete mode 100755 subprojects/robotpy-cscore/examples/cvstream.py delete mode 100755 subprojects/robotpy-cscore/examples/dual_cameraserver.py delete mode 100755 subprojects/robotpy-cscore/examples/enum_usb.py delete mode 100755 subprojects/robotpy-cscore/examples/httpcvstream.py delete mode 100755 subprojects/robotpy-cscore/examples/intermediate_cameraserver.py delete mode 100755 subprojects/robotpy-cscore/examples/quick_cameraserver.py delete mode 100755 subprojects/robotpy-cscore/examples/settings.py delete mode 100755 subprojects/robotpy-cscore/examples/switched_cameraserver.py delete mode 100755 subprojects/robotpy-cscore/examples/usbcvstream.py delete mode 100755 subprojects/robotpy-cscore/examples/usbstream.py diff --git a/subprojects/pyntcore/examples/pubsub_client.py b/subprojects/pyntcore/examples/pubsub_client.py deleted file mode 100755 index f67257fc..00000000 --- a/subprojects/pyntcore/examples/pubsub_client.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 -# -# A client that publishes some synchronized values periodically - -import argparse -import os -from os.path import basename -import logging -import time - -import ntcore - -if __name__ == "__main__": - logging.basicConfig(level=logging.DEBUG) - - parser = argparse.ArgumentParser() - parser.add_argument("ip", type=str, help="IP address to connect to") - args = parser.parse_args() - - # Initialize NT4 client - inst = ntcore.NetworkTableInstance.getDefault() - - identity = f"{basename(__file__)}-{os.getpid()}" - inst.startClient4(identity) - - inst.setServer(args.ip) - - # publish two values - table = inst.getTable("data") - pub1 = table.getDoubleTopic("1").publish() - pub2 = table.getDoubleTopic("2").publish() - - i = 3 - - while True: - # These values are being published fast than the server is polling - pub1.set(i) - pub2.set(i + 100) - - time.sleep(0.5) - i += 1 diff --git a/subprojects/pyntcore/examples/pubsub_server.py b/subprojects/pyntcore/examples/pubsub_server.py deleted file mode 100755 index 6356b0c5..00000000 --- a/subprojects/pyntcore/examples/pubsub_server.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python3 -# -# A server that reads from the subscription -# - -import logging -import time - -import ntcore - -if __name__ == "__main__": - logging.basicConfig(level=logging.DEBUG) - - # initialize networktables server (on a robot this is already done) - inst = ntcore.NetworkTableInstance.getDefault() - inst.startServer() - - # Initialize two subscriptions - table = inst.getTable("data") - - # only keep the latest value for this topic - sub1 = table.getDoubleTopic("1").subscribe(-1.0) - - # keep the last 10 values for this topic - sub2 = table.getDoubleTopic("2").subscribe( - -2.0, ntcore.PubSubOptions(pollStorage=10) - ) - - # Periodically read from them - # - note sub1 only has 1 value, but sub2 sometimes has more than 1 - while True: - print("---", ntcore._now()) - print("/data/1:", sub1.readQueue()) - print("/data/2:", sub2.readQueue()) - - time.sleep(1.2) diff --git a/subprojects/pyntcore/examples/simple_client.py b/subprojects/pyntcore/examples/simple_client.py deleted file mode 100755 index d49155b7..00000000 --- a/subprojects/pyntcore/examples/simple_client.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python3 -# -# This is a NetworkTables client (eg, the DriverStation/coprocessor side). -# You need to tell it the IP address of the NetworkTables server (the -# robot or simulator). -# -# When running, this will continue incrementing the value 'dsTime', and the -# value should be visible to other networktables clients and the robot. -# - -import argparse -from os.path import basename -import logging -import time - -import ntcore - - -if __name__ == "__main__": - logging.basicConfig(level=logging.DEBUG) - - parser = argparse.ArgumentParser() - parser.add_argument( - "-p", - "--protocol", - type=int, - choices=[3, 4], - help="NT Protocol to use", - default=4, - ) - parser.add_argument("ip", type=str, help="IP address to connect to") - args = parser.parse_args() - - inst = ntcore.NetworkTableInstance.getDefault() - - identity = basename(__file__) - if args.protocol == 3: - inst.startClient3(identity) - else: - inst.startClient4(identity) - - inst.setServer(args.ip) - - sd = inst.getTable("SmartDashboard") - - i = 0 - while True: - print("robotTime:", sd.getNumber("robotTime", -1)) - - sd.putNumber("dsTime", i) - time.sleep(1) - i += 1 diff --git a/subprojects/pyntcore/examples/simple_poller.py b/subprojects/pyntcore/examples/simple_poller.py deleted file mode 100755 index 3c7c78e8..00000000 --- a/subprojects/pyntcore/examples/simple_poller.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python3 -# -# This is a NetworkTables client (eg, the DriverStation/coprocessor side). -# You need to tell it the IP address of the NetworkTables server (the -# robot or simulator). -# -# This is intended to be ran at the same time as the simple_robot.py -# and simple_client.py examples. It will output values as they change. -# - -import argparse -from os.path import basename -import logging -import time - -import ntcore - - -if __name__ == "__main__": - logging.basicConfig(level=logging.DEBUG) - - parser = argparse.ArgumentParser() - parser.add_argument( - "-p", - "--protocol", - type=int, - choices=[3, 4], - help="NT Protocol to use", - default=4, - ) - parser.add_argument("ip", type=str, help="IP address to connect to") - args = parser.parse_args() - - inst = ntcore.NetworkTableInstance.getDefault() - - identity = basename(__file__) - if args.protocol == 3: - inst.startClient3(identity) - else: - inst.startClient4(identity) - - inst.setServer(args.ip) - - # Create a poller - poller = ntcore.NetworkTableListenerPoller(inst) - - # Listen for all connection events - poller.addConnectionListener(True) - - # Listen to all changes - msub = ntcore.MultiSubscriber(inst, [""]) - poller.addListener(msub, ntcore.EventFlags.kValueRemote) - - while True: - # periodically read from the queue - for event in poller.readQueue(): - print(event) - - time.sleep(1) diff --git a/subprojects/pyntcore/examples/simple_robot.py b/subprojects/pyntcore/examples/simple_robot.py deleted file mode 100755 index 1eeb6ebc..00000000 --- a/subprojects/pyntcore/examples/simple_robot.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python3 -# -# This is a NetworkTables server (eg, the robot or simulator side). -# -# On a real robot, you probably would create an instance of the -# wpilib.SmartDashboard object and use that instead -- but it's really -# just a passthru to the underlying NetworkTable object. -# -# When running, this will continue incrementing the value 'robotTime', -# and the value should be visible to networktables clients such as -# Shuffleboard or simple_client.py -# - -import logging -import time - -import ntcore - - -if __name__ == "__main__": - logging.basicConfig(level=logging.DEBUG) - - inst = ntcore.NetworkTableInstance.getDefault() - - inst.startServer() - sd = inst.getTable("SmartDashboard") - - i = 0 - while True: - print("dsTime:", sd.getNumber("dsTime", -1)) - - sd.putNumber("robotTime", i) - time.sleep(1) - i += 1 diff --git a/subprojects/robotpy-cscore/examples/CameraServer.java b/subprojects/robotpy-cscore/examples/CameraServer.java deleted file mode 100644 index af9bc72a..00000000 --- a/subprojects/robotpy-cscore/examples/CameraServer.java +++ /dev/null @@ -1,127 +0,0 @@ -package io.github.robotpy.cscore; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.ProcessBuilder.Redirect; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; - -import edu.wpi.first.wpilibj.DriverStation; - -/** - * Provides a way to launch an out of process Python robotpy-cscore based - * camera service instance from a Java robot program. - * - * You must have Python and python36-robotpy-cscore installed, or this - * just simply won't work. Refer to the RobotPy documentation for details. - * - * The python code should be a single file, and must compiled into your java - * jar file. If your file was in the src directory as 'vision.py', you would - * add this to the 'project' section of build.xml: - */ -// -// -// [athena-jar] Making jar ${dist.jar}. -// -// -// -// [athena-jar] Copying jars to ${build.jars}. -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -// -public class CameraServer { - - public static String visionPath = "/home/lvuser/vision.py"; - private static boolean isAlive = false; - private static boolean launched = false; - - /** - * @return true if the vision process is (probably) alive - */ - public static boolean isAlive() { - return isAlive; - } - - /** - * Launches an embedded resource called "vision.py" which contains a - * "main" function. - */ - public static void startPythonVision() { - startPythonVision("/vision.py", "main"); - } - - /** - * Call this function to launch a python vision program in an external - * process. - * - * @param resource The resource built into the jar (see above), such as /vision.py - * @param functionName The name of the function to call - */ - public static void startPythonVision(String resource, String functionName) { - // don't allow restart - if (launched) { - return; - } - - launched = true; - - System.out.println("Launching python process from " + resource); - - try { - // extract the resource and write it to vision.py - InputStream is = CameraServer.class.getResourceAsStream(resource); - if (is == null) { - throw new IOException("Resource " + resource + " not found"); - } - - Files.copy(is, Paths.get(visionPath), StandardCopyOption.REPLACE_EXISTING); - - // launch the process - ProcessBuilder pb = new ProcessBuilder(); - pb.command("/usr/local/bin/python3", "-m", "cscore", visionPath + ":" + functionName); - - // we open a pipe to it so that when the robot program exits, the child dies - pb.redirectInput(Redirect.PIPE); - - // and let us see stdout/stderr - pb.redirectOutput(Redirect.INHERIT); - pb.redirectError(Redirect.INHERIT); - - final Process p = pb.start(); - isAlive = true; - - Thread t = new Thread(()-> { - try { - p.waitFor(); - } catch (InterruptedException e) { - // empty - } - - isAlive = false; - }); - t.setDaemon(true); - t.start(); - - } catch (IOException e) { - System.out.println("Error launching vision! " + e.toString()); - //if (!DriverStation.getInstance().isFMSAttached()) { - // throw new RuntimeException("Error launching vision", e); - //} - } - } -} diff --git a/subprojects/robotpy-cscore/examples/cvstream.py b/subprojects/robotpy-cscore/examples/cvstream.py deleted file mode 100755 index 67dbe3cb..00000000 --- a/subprojects/robotpy-cscore/examples/cvstream.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -# -# WARNING: You should only use this approach for testing cscore on platforms that -# it doesn't support using UsbCamera -# - -import cscore as cs - -if hasattr(cs, "UsbCamera"): - camera = cs.UsbCamera("usbcam", 0) - camera.setVideoMode(cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) -else: - import cv2 - import threading - - camera = cs.CvSource("cvsource", cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) - - # tell OpenCV to capture video for us - cap = cv2.VideoCapture(0) - cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320) - cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240) - - # Do it in another thread - def _thread(): - img = None - while True: - retval, img = cap.read(img) - if retval: - camera.putFrame(img) - - th = threading.Thread(target=_thread, daemon=True) - th.start() - - -mjpegServer = cs.MjpegServer("httpserver", 8081) -mjpegServer.setSource(camera) - -print("mjpg server listening at http://0.0.0.0:8081") -input("Press enter to exit...") diff --git a/subprojects/robotpy-cscore/examples/dual_cameraserver.py b/subprojects/robotpy-cscore/examples/dual_cameraserver.py deleted file mode 100755 index 74bf59cc..00000000 --- a/subprojects/robotpy-cscore/examples/dual_cameraserver.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -# -# Uses the CameraServer class to automatically capture video from two USB -# webcams and send it to the FRC dashboard without doing any vision -# processing. -# -# Warning: If you're using this with a python-based robot, do not run this -# in the same program as your robot code! -# - -from cscore import CameraServer as CS - - -def main(): - CS.enableLogging() - - usb1 = CS.startAutomaticCapture(dev=0) - usb2 = CS.startAutomaticCapture(dev=1) - - CS.waitForever() - - -if __name__ == "__main__": - # To see messages from networktables, you must setup logging - import logging - - logging.basicConfig(level=logging.DEBUG) - - # You should uncomment these to connect to the RoboRIO - # import ntcore - # nt = ntcore.NetworkTableInstance.getDefault() - # nt.setServerTeam(XXXX) - # nt.startClient4(__file__) - - main() diff --git a/subprojects/robotpy-cscore/examples/enum_usb.py b/subprojects/robotpy-cscore/examples/enum_usb.py deleted file mode 100755 index 1dd937aa..00000000 --- a/subprojects/robotpy-cscore/examples/enum_usb.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3 - -import cscore as cs - - -def main(): - for caminfo in cs.UsbCamera.enumerateUsbCameras(): - print("%s: %s (%s)" % (caminfo.dev, caminfo.path, caminfo.name)) - if caminfo.otherPaths: - print("Other device paths:") - for path in caminfo.otherPaths: - print(" ", path) - - camera = cs.UsbCamera("usbcam", caminfo.dev) - - print("Properties:") - for prop in camera.enumerateProperties(): - kind = prop.getKind() - if kind == cs.VideoProperty.Kind.kBoolean: - print( - prop.getName(), - "(bool) value=%s default=%s" % (prop.get(), prop.getDefault()), - ) - elif kind == cs.VideoProperty.Kind.kInteger: - print( - prop.getName(), - "(int): value=%s min=%s max=%s step=%s default=%s" - % ( - prop.get(), - prop.getMin(), - prop.getMax(), - prop.getStep(), - prop.getDefault(), - ), - ) - elif kind == cs.VideoProperty.Kind.kString: - print(prop.getName(), "(string):", prop.getString()) - elif kind == cs.VideoProperty.Kind.kEnum: - print(prop.getName(), "(enum): value=%s" % prop.get()) - for i, choice in enumerate(prop.getChoices()): - if choice: - print(" %s: %s" % (i, choice)) - - print("Video Modes") - for mode in camera.enumerateVideoModes(): - if mode.pixelFormat == cs.VideoMode.PixelFormat.kMJPEG: - fmt = "MJPEG" - elif mode.pixelFormat == cs.VideoMode.PixelFormat.kYUYV: - fmt = "YUYV" - elif mode.pixelFormat == cs.VideoMode.PixelFormat.kRGB565: - fmt = "RGB565" - else: - fmt = "Unknown" - - print(" PixelFormat:", fmt) - print(" Width:", mode.width) - print(" Height:", mode.height) - print(" FPS: ", mode.fps) - - -if __name__ == "__main__": - main() diff --git a/subprojects/robotpy-cscore/examples/httpcvstream.py b/subprojects/robotpy-cscore/examples/httpcvstream.py deleted file mode 100755 index 9acfbbfa..00000000 --- a/subprojects/robotpy-cscore/examples/httpcvstream.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 -# -# Demonstrates streaming from a HTTP camera server -# - - -import cscore as cs -import numpy as np -import cv2 - - -def main(): - camera = cs.HttpCamera("httpcam", "http://localhost:8081/?action=stream") - camera.setVideoMode(cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) - - cvsink = cs.CvSink("cvsink") - cvsink.setSource(camera) - - cvSource = cs.CvSource("cvsource", cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) - cvMjpegServer = cs.MjpegServer("cvhttpserver", 8083) - cvMjpegServer.setSource(cvSource) - - print("OpenCV output mjpg server listening at http://0.0.0.0:8083") - - test = np.zeros(shape=(240, 320, 3), dtype=np.uint8) - flip = np.zeros(shape=(240, 320, 3), dtype=np.uint8) - - while True: - time, test = cvsink.grabFrame(test) - if time == 0: - print("error:", cvsink.getError()) - continue - - print("got frame at time", time, test.shape) - - cv2.flip(test, flipCode=0, dst=flip) - cvSource.putFrame(flip) - - -if __name__ == "__main__": - main() diff --git a/subprojects/robotpy-cscore/examples/intermediate_cameraserver.py b/subprojects/robotpy-cscore/examples/intermediate_cameraserver.py deleted file mode 100755 index c6bd2b1a..00000000 --- a/subprojects/robotpy-cscore/examples/intermediate_cameraserver.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python3 -# -# This is a demo program showing CameraServer usage with OpenCV to do image -# processing. The image is acquired from the USB camera, then a rectangle -# is put on the image and sent to the dashboard. OpenCV has many methods -# for different types of processing. -# -# Warning: If you're using this with a python-based robot, do not run this -# in the same program as your robot code! -# - -import cv2 -import numpy as np - -from cscore import CameraServer as CS - - -def main(): - CS.enableLogging() - - camera = CS.startAutomaticCapture() - - camera.setResolution(640, 480) - - # Get a CvSink. This will capture images from the camera - cvSink = CS.getVideo() - - # (optional) Setup a CvSource. This will send images back to the Dashboard - outputStream = CS.putVideo("Rectangle", 640, 480) - - # Allocating new images is very expensive, always try to preallocate - img = np.zeros(shape=(480, 640, 3), dtype=np.uint8) - - while True: - # Tell the CvSink to grab a frame from the camera and put it - # in the source image. If there is an error notify the output. - time, img = cvSink.grabFrame(img) - if time == 0: - # Send the output the error. - outputStream.notifyError(cvSink.getError()) - # skip the rest of the current iteration - continue - - # Put a rectangle on the image - cv2.rectangle(img, (100, 100), (400, 400), (255, 255, 255), 5) - - # Give the output stream a new image to display - outputStream.putFrame(img) - - -if __name__ == "__main__": - # To see messages from networktables, you must setup logging - import logging - - logging.basicConfig(level=logging.DEBUG) - - # You should uncomment these to connect to the RoboRIO - # import ntcore - # nt = ntcore.NetworkTableInstance.getDefault() - # nt.setServerTeam(XXXX) - # nt.startClient4(__file__) - - main() diff --git a/subprojects/robotpy-cscore/examples/quick_cameraserver.py b/subprojects/robotpy-cscore/examples/quick_cameraserver.py deleted file mode 100755 index db39fa67..00000000 --- a/subprojects/robotpy-cscore/examples/quick_cameraserver.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 -# -# Uses the CameraServer class to automatically capture video from a USB webcam -# and send it to the FRC dashboard without doing any vision processing. -# -# Warning: If you're using this with a python-based robot, do not run this -# in the same program as your robot code! -# - - -from cscore import CameraServer as CS - - -def main(): - CS.enableLogging() - - CS.startAutomaticCapture() - CS.waitForever() - - -if __name__ == "__main__": - # To see messages from networktables, you must setup logging - import logging - - logging.basicConfig(level=logging.DEBUG) - - # You should uncomment these to connect to the RoboRIO - # import ntcore - # nt = ntcore.NetworkTableInstance.getDefault() - # nt.setServerTeam(XXXX) - # nt.startClient4(__file__) - - main() diff --git a/subprojects/robotpy-cscore/examples/settings.py b/subprojects/robotpy-cscore/examples/settings.py deleted file mode 100755 index 2b800f76..00000000 --- a/subprojects/robotpy-cscore/examples/settings.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import time - -import cscore as cs - - -def _usage(): - print("Usage: settings.py camera [prop val] ... -- [prop val]", file=sys.stderr) - print(" Example: settings.py brightness 30 raw_contrast 10", file=sys.stderr) - - -def main(): - if not hasattr(cs, "UsbCamera"): - print("ERROR: This platform does not currently have cscore UsbCamera support") - exit(1) - - if len(sys.argv) < 3: - _usage() - exit(1) - - try: - cid = int(sys.argv[1]) - except ValueError: - print("ERROR: Expected first argument to be a number, got '%s'" % sys.argv[1]) - _usage() - exit(2) - - camera = cs.UsbCamera("usbcam", cid) - - # Set prior to connect - argc = 2 - propName = None - - for arg in sys.argv[argc:]: - argc += 1 - if arg == "--": - break - - if propName is None: - propName = arg - else: - try: - propVal = int(arg) - except ValueError: - camera.getProperty(propName).setString(arg) - else: - camera.getProperty(propName).set(propVal) - - propName = None - - # Wait to connect - while not camera.isConnected(): - time.sleep(0.010) - - # Set rest - for arg in sys.argv[argc:]: - if propName is None: - propName = arg - else: - try: - propVal = int(arg) - except ValueError: - camera.getProperty(propName).setString(arg) - else: - camera.getProperty(propName).set(propVal) - - propName = None - - # Print settings - print("Properties:") - for prop in camera.enumerateProperties(): - kind = prop.getKind() - if kind == cs.VideoProperty.Kind.kBoolean: - print( - prop.getName(), - "(bool) value=%s default=%s" % (prop.get(), prop.getDefault()), - ) - elif kind == cs.VideoProperty.Kind.kInteger: - print( - prop.getName(), - "(int): value=%s min=%s max=%s step=%s default=%s" - % ( - prop.get(), - prop.getMin(), - prop.getMax(), - prop.getStep(), - prop.getDefault(), - ), - ) - elif kind == cs.VideoProperty.Kind.kString: - print(prop.getName(), "(string):", prop.getString()) - elif kind == cs.VideoProperty.Kind.kEnum: - print(prop.getName(), "(enum): value=%s" % prop.get()) - for i, choice in enumerate(prop.getChoices()): - if choice: - print(" %s: %s" % (i, choice)) - - -if __name__ == "__main__": - main() diff --git a/subprojects/robotpy-cscore/examples/switched_cameraserver.py b/subprojects/robotpy-cscore/examples/switched_cameraserver.py deleted file mode 100755 index 10e8d94a..00000000 --- a/subprojects/robotpy-cscore/examples/switched_cameraserver.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python3 -# -# Uses the CameraServer class to automatically capture video from two USB -# webcams and send one of them to the dashboard without doing any processing. -# To switch between the cameras, change the /CameraPublisher/selected value in NetworkTables -# -# Warning: If you're using this with a python-based robot, do not run this -# in the same program as your robot code! -# - -from cscore import CameraServer as CS -from cscore import UsbCamera -import ntcore - - -def main(): - CS.enableLogging() - - usb0 = UsbCamera("Camera 0", 0) - usb1 = UsbCamera("Camera 1", 1) - - server = CS.addSwitchedCamera("Switched") - server.setSource(usb0) - - # Use networktables to switch the source - # -> obviously, you can switch them however you'd like - def _listener(source, key, value, isNew): - if str(value) == "0": - server.setSource(usb0) - elif str(value) == "1": - server.setSource(usb1) - - table = ntcore.NetworkTableInstance.getDefault().getTable("/CameraPublisher") - table.putString("selected", "0") - table.addEntryListener(_listener, key="selected") - - CS.waitForever() - - -if __name__ == "__main__": - # To see messages from networktables, you must setup logging - import logging - - logging.basicConfig(level=logging.DEBUG) - - # You should change this to connect to the RoboRIO - nt = ntcore.NetworkTableInstance.getDefault() - nt.setServer("localhost") - nt.startClient4(__file__) - - main() diff --git a/subprojects/robotpy-cscore/examples/usbcvstream.py b/subprojects/robotpy-cscore/examples/usbcvstream.py deleted file mode 100755 index 65651721..00000000 --- a/subprojects/robotpy-cscore/examples/usbcvstream.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python3 -# -# Demonstrates streaming and modifying the image via OpenCV -# - - -import cscore as cs -import numpy as np -import cv2 - - -def main(): - camera = cs.UsbCamera("usbcam", 0) - camera.setVideoMode(cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) - - mjpegServer = cs.MjpegServer("httpserver", 8081) - mjpegServer.setSource(camera) - - print("mjpg server listening at http://0.0.0.0:8081") - - cvsink = cs.CvSink("cvsink") - cvsink.setSource(camera) - - cvSource = cs.CvSource("cvsource", cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) - cvMjpegServer = cs.MjpegServer("cvhttpserver", 8082) - cvMjpegServer.setSource(cvSource) - - print("OpenCV output mjpg server listening at http://0.0.0.0:8082") - - test = np.zeros(shape=(240, 320, 3), dtype=np.uint8) - flip = np.zeros(shape=(240, 320, 3), dtype=np.uint8) - - while True: - time, test = cvsink.grabFrame(test) - if time == 0: - print("error:", cvsink.getError()) - continue - - print("got frame at time", time, test.shape) - - cv2.flip(test, flipCode=0, dst=flip) - cvSource.putFrame(flip) - - -if __name__ == "__main__": - main() diff --git a/subprojects/robotpy-cscore/examples/usbstream.py b/subprojects/robotpy-cscore/examples/usbstream.py deleted file mode 100755 index ded3a399..00000000 --- a/subprojects/robotpy-cscore/examples/usbstream.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python3 - -import cscore as cs - -camera = cs.UsbCamera("usbcam", 0) -camera.setVideoMode(cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) - -mjpegServer = cs.MjpegServer("httpserver", 8081) -mjpegServer.setSource(camera) - -print("mjpg server listening at http://0.0.0.0:8081") -input("Press enter to exit...") From 6b55743dd3f9b9011a4afe9ca8e21593ea147c35 Mon Sep 17 00:00:00 2001 From: Martin C <78377730+MilkManMaki@users.noreply.github.com> Date: Mon, 8 Apr 2024 22:47:02 -0400 Subject: [PATCH 2/4] refactor: moving examples to an example folder in the root directory --- EXAMPLES/examplecscore/CameraServer.java | 127 ++++++++++++++++++ EXAMPLES/examplecscore/cvstream.py | 39 ++++++ EXAMPLES/examplecscore/dual_cameraserver.py | 35 +++++ EXAMPLES/examplecscore/enum_usb.py | 62 +++++++++ EXAMPLES/examplecscore/httpcvstream.py | 41 ++++++ .../intermediate_cameraserver.py | 63 +++++++++ EXAMPLES/examplecscore/quick_cameraserver.py | 33 +++++ EXAMPLES/examplecscore/settings.py | 102 ++++++++++++++ .../examplecscore/switched_cameraserver.py | 51 +++++++ EXAMPLES/examplecscore/usbcvstream.py | 46 +++++++ EXAMPLES/examplecscore/usbstream.py | 12 ++ EXAMPLES/examplentcore/pubsub_client.py | 41 ++++++ EXAMPLES/examplentcore/pubsub_server.py | 36 +++++ EXAMPLES/examplentcore/simple_client.py | 52 +++++++ EXAMPLES/examplentcore/simple_poller.py | 59 ++++++++ EXAMPLES/examplentcore/simple_robot.py | 34 +++++ 16 files changed, 833 insertions(+) create mode 100644 EXAMPLES/examplecscore/CameraServer.java create mode 100644 EXAMPLES/examplecscore/cvstream.py create mode 100644 EXAMPLES/examplecscore/dual_cameraserver.py create mode 100644 EXAMPLES/examplecscore/enum_usb.py create mode 100644 EXAMPLES/examplecscore/httpcvstream.py create mode 100644 EXAMPLES/examplecscore/intermediate_cameraserver.py create mode 100644 EXAMPLES/examplecscore/quick_cameraserver.py create mode 100644 EXAMPLES/examplecscore/settings.py create mode 100644 EXAMPLES/examplecscore/switched_cameraserver.py create mode 100644 EXAMPLES/examplecscore/usbcvstream.py create mode 100644 EXAMPLES/examplecscore/usbstream.py create mode 100644 EXAMPLES/examplentcore/pubsub_client.py create mode 100644 EXAMPLES/examplentcore/pubsub_server.py create mode 100644 EXAMPLES/examplentcore/simple_client.py create mode 100644 EXAMPLES/examplentcore/simple_poller.py create mode 100644 EXAMPLES/examplentcore/simple_robot.py diff --git a/EXAMPLES/examplecscore/CameraServer.java b/EXAMPLES/examplecscore/CameraServer.java new file mode 100644 index 00000000..af9bc72a --- /dev/null +++ b/EXAMPLES/examplecscore/CameraServer.java @@ -0,0 +1,127 @@ +package io.github.robotpy.cscore; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.ProcessBuilder.Redirect; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +import edu.wpi.first.wpilibj.DriverStation; + +/** + * Provides a way to launch an out of process Python robotpy-cscore based + * camera service instance from a Java robot program. + * + * You must have Python and python36-robotpy-cscore installed, or this + * just simply won't work. Refer to the RobotPy documentation for details. + * + * The python code should be a single file, and must compiled into your java + * jar file. If your file was in the src directory as 'vision.py', you would + * add this to the 'project' section of build.xml: + */ +// +// +// [athena-jar] Making jar ${dist.jar}. +// +// +// +// [athena-jar] Copying jars to ${build.jars}. +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +public class CameraServer { + + public static String visionPath = "/home/lvuser/vision.py"; + private static boolean isAlive = false; + private static boolean launched = false; + + /** + * @return true if the vision process is (probably) alive + */ + public static boolean isAlive() { + return isAlive; + } + + /** + * Launches an embedded resource called "vision.py" which contains a + * "main" function. + */ + public static void startPythonVision() { + startPythonVision("/vision.py", "main"); + } + + /** + * Call this function to launch a python vision program in an external + * process. + * + * @param resource The resource built into the jar (see above), such as /vision.py + * @param functionName The name of the function to call + */ + public static void startPythonVision(String resource, String functionName) { + // don't allow restart + if (launched) { + return; + } + + launched = true; + + System.out.println("Launching python process from " + resource); + + try { + // extract the resource and write it to vision.py + InputStream is = CameraServer.class.getResourceAsStream(resource); + if (is == null) { + throw new IOException("Resource " + resource + " not found"); + } + + Files.copy(is, Paths.get(visionPath), StandardCopyOption.REPLACE_EXISTING); + + // launch the process + ProcessBuilder pb = new ProcessBuilder(); + pb.command("/usr/local/bin/python3", "-m", "cscore", visionPath + ":" + functionName); + + // we open a pipe to it so that when the robot program exits, the child dies + pb.redirectInput(Redirect.PIPE); + + // and let us see stdout/stderr + pb.redirectOutput(Redirect.INHERIT); + pb.redirectError(Redirect.INHERIT); + + final Process p = pb.start(); + isAlive = true; + + Thread t = new Thread(()-> { + try { + p.waitFor(); + } catch (InterruptedException e) { + // empty + } + + isAlive = false; + }); + t.setDaemon(true); + t.start(); + + } catch (IOException e) { + System.out.println("Error launching vision! " + e.toString()); + //if (!DriverStation.getInstance().isFMSAttached()) { + // throw new RuntimeException("Error launching vision", e); + //} + } + } +} diff --git a/EXAMPLES/examplecscore/cvstream.py b/EXAMPLES/examplecscore/cvstream.py new file mode 100644 index 00000000..67dbe3cb --- /dev/null +++ b/EXAMPLES/examplecscore/cvstream.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# +# WARNING: You should only use this approach for testing cscore on platforms that +# it doesn't support using UsbCamera +# + +import cscore as cs + +if hasattr(cs, "UsbCamera"): + camera = cs.UsbCamera("usbcam", 0) + camera.setVideoMode(cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) +else: + import cv2 + import threading + + camera = cs.CvSource("cvsource", cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) + + # tell OpenCV to capture video for us + cap = cv2.VideoCapture(0) + cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240) + + # Do it in another thread + def _thread(): + img = None + while True: + retval, img = cap.read(img) + if retval: + camera.putFrame(img) + + th = threading.Thread(target=_thread, daemon=True) + th.start() + + +mjpegServer = cs.MjpegServer("httpserver", 8081) +mjpegServer.setSource(camera) + +print("mjpg server listening at http://0.0.0.0:8081") +input("Press enter to exit...") diff --git a/EXAMPLES/examplecscore/dual_cameraserver.py b/EXAMPLES/examplecscore/dual_cameraserver.py new file mode 100644 index 00000000..74bf59cc --- /dev/null +++ b/EXAMPLES/examplecscore/dual_cameraserver.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# Uses the CameraServer class to automatically capture video from two USB +# webcams and send it to the FRC dashboard without doing any vision +# processing. +# +# Warning: If you're using this with a python-based robot, do not run this +# in the same program as your robot code! +# + +from cscore import CameraServer as CS + + +def main(): + CS.enableLogging() + + usb1 = CS.startAutomaticCapture(dev=0) + usb2 = CS.startAutomaticCapture(dev=1) + + CS.waitForever() + + +if __name__ == "__main__": + # To see messages from networktables, you must setup logging + import logging + + logging.basicConfig(level=logging.DEBUG) + + # You should uncomment these to connect to the RoboRIO + # import ntcore + # nt = ntcore.NetworkTableInstance.getDefault() + # nt.setServerTeam(XXXX) + # nt.startClient4(__file__) + + main() diff --git a/EXAMPLES/examplecscore/enum_usb.py b/EXAMPLES/examplecscore/enum_usb.py new file mode 100644 index 00000000..1dd937aa --- /dev/null +++ b/EXAMPLES/examplecscore/enum_usb.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +import cscore as cs + + +def main(): + for caminfo in cs.UsbCamera.enumerateUsbCameras(): + print("%s: %s (%s)" % (caminfo.dev, caminfo.path, caminfo.name)) + if caminfo.otherPaths: + print("Other device paths:") + for path in caminfo.otherPaths: + print(" ", path) + + camera = cs.UsbCamera("usbcam", caminfo.dev) + + print("Properties:") + for prop in camera.enumerateProperties(): + kind = prop.getKind() + if kind == cs.VideoProperty.Kind.kBoolean: + print( + prop.getName(), + "(bool) value=%s default=%s" % (prop.get(), prop.getDefault()), + ) + elif kind == cs.VideoProperty.Kind.kInteger: + print( + prop.getName(), + "(int): value=%s min=%s max=%s step=%s default=%s" + % ( + prop.get(), + prop.getMin(), + prop.getMax(), + prop.getStep(), + prop.getDefault(), + ), + ) + elif kind == cs.VideoProperty.Kind.kString: + print(prop.getName(), "(string):", prop.getString()) + elif kind == cs.VideoProperty.Kind.kEnum: + print(prop.getName(), "(enum): value=%s" % prop.get()) + for i, choice in enumerate(prop.getChoices()): + if choice: + print(" %s: %s" % (i, choice)) + + print("Video Modes") + for mode in camera.enumerateVideoModes(): + if mode.pixelFormat == cs.VideoMode.PixelFormat.kMJPEG: + fmt = "MJPEG" + elif mode.pixelFormat == cs.VideoMode.PixelFormat.kYUYV: + fmt = "YUYV" + elif mode.pixelFormat == cs.VideoMode.PixelFormat.kRGB565: + fmt = "RGB565" + else: + fmt = "Unknown" + + print(" PixelFormat:", fmt) + print(" Width:", mode.width) + print(" Height:", mode.height) + print(" FPS: ", mode.fps) + + +if __name__ == "__main__": + main() diff --git a/EXAMPLES/examplecscore/httpcvstream.py b/EXAMPLES/examplecscore/httpcvstream.py new file mode 100644 index 00000000..9acfbbfa --- /dev/null +++ b/EXAMPLES/examplecscore/httpcvstream.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# +# Demonstrates streaming from a HTTP camera server +# + + +import cscore as cs +import numpy as np +import cv2 + + +def main(): + camera = cs.HttpCamera("httpcam", "http://localhost:8081/?action=stream") + camera.setVideoMode(cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) + + cvsink = cs.CvSink("cvsink") + cvsink.setSource(camera) + + cvSource = cs.CvSource("cvsource", cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) + cvMjpegServer = cs.MjpegServer("cvhttpserver", 8083) + cvMjpegServer.setSource(cvSource) + + print("OpenCV output mjpg server listening at http://0.0.0.0:8083") + + test = np.zeros(shape=(240, 320, 3), dtype=np.uint8) + flip = np.zeros(shape=(240, 320, 3), dtype=np.uint8) + + while True: + time, test = cvsink.grabFrame(test) + if time == 0: + print("error:", cvsink.getError()) + continue + + print("got frame at time", time, test.shape) + + cv2.flip(test, flipCode=0, dst=flip) + cvSource.putFrame(flip) + + +if __name__ == "__main__": + main() diff --git a/EXAMPLES/examplecscore/intermediate_cameraserver.py b/EXAMPLES/examplecscore/intermediate_cameraserver.py new file mode 100644 index 00000000..c6bd2b1a --- /dev/null +++ b/EXAMPLES/examplecscore/intermediate_cameraserver.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# +# This is a demo program showing CameraServer usage with OpenCV to do image +# processing. The image is acquired from the USB camera, then a rectangle +# is put on the image and sent to the dashboard. OpenCV has many methods +# for different types of processing. +# +# Warning: If you're using this with a python-based robot, do not run this +# in the same program as your robot code! +# + +import cv2 +import numpy as np + +from cscore import CameraServer as CS + + +def main(): + CS.enableLogging() + + camera = CS.startAutomaticCapture() + + camera.setResolution(640, 480) + + # Get a CvSink. This will capture images from the camera + cvSink = CS.getVideo() + + # (optional) Setup a CvSource. This will send images back to the Dashboard + outputStream = CS.putVideo("Rectangle", 640, 480) + + # Allocating new images is very expensive, always try to preallocate + img = np.zeros(shape=(480, 640, 3), dtype=np.uint8) + + while True: + # Tell the CvSink to grab a frame from the camera and put it + # in the source image. If there is an error notify the output. + time, img = cvSink.grabFrame(img) + if time == 0: + # Send the output the error. + outputStream.notifyError(cvSink.getError()) + # skip the rest of the current iteration + continue + + # Put a rectangle on the image + cv2.rectangle(img, (100, 100), (400, 400), (255, 255, 255), 5) + + # Give the output stream a new image to display + outputStream.putFrame(img) + + +if __name__ == "__main__": + # To see messages from networktables, you must setup logging + import logging + + logging.basicConfig(level=logging.DEBUG) + + # You should uncomment these to connect to the RoboRIO + # import ntcore + # nt = ntcore.NetworkTableInstance.getDefault() + # nt.setServerTeam(XXXX) + # nt.startClient4(__file__) + + main() diff --git a/EXAMPLES/examplecscore/quick_cameraserver.py b/EXAMPLES/examplecscore/quick_cameraserver.py new file mode 100644 index 00000000..db39fa67 --- /dev/null +++ b/EXAMPLES/examplecscore/quick_cameraserver.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# +# Uses the CameraServer class to automatically capture video from a USB webcam +# and send it to the FRC dashboard without doing any vision processing. +# +# Warning: If you're using this with a python-based robot, do not run this +# in the same program as your robot code! +# + + +from cscore import CameraServer as CS + + +def main(): + CS.enableLogging() + + CS.startAutomaticCapture() + CS.waitForever() + + +if __name__ == "__main__": + # To see messages from networktables, you must setup logging + import logging + + logging.basicConfig(level=logging.DEBUG) + + # You should uncomment these to connect to the RoboRIO + # import ntcore + # nt = ntcore.NetworkTableInstance.getDefault() + # nt.setServerTeam(XXXX) + # nt.startClient4(__file__) + + main() diff --git a/EXAMPLES/examplecscore/settings.py b/EXAMPLES/examplecscore/settings.py new file mode 100644 index 00000000..2b800f76 --- /dev/null +++ b/EXAMPLES/examplecscore/settings.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 + +import sys +import time + +import cscore as cs + + +def _usage(): + print("Usage: settings.py camera [prop val] ... -- [prop val]", file=sys.stderr) + print(" Example: settings.py brightness 30 raw_contrast 10", file=sys.stderr) + + +def main(): + if not hasattr(cs, "UsbCamera"): + print("ERROR: This platform does not currently have cscore UsbCamera support") + exit(1) + + if len(sys.argv) < 3: + _usage() + exit(1) + + try: + cid = int(sys.argv[1]) + except ValueError: + print("ERROR: Expected first argument to be a number, got '%s'" % sys.argv[1]) + _usage() + exit(2) + + camera = cs.UsbCamera("usbcam", cid) + + # Set prior to connect + argc = 2 + propName = None + + for arg in sys.argv[argc:]: + argc += 1 + if arg == "--": + break + + if propName is None: + propName = arg + else: + try: + propVal = int(arg) + except ValueError: + camera.getProperty(propName).setString(arg) + else: + camera.getProperty(propName).set(propVal) + + propName = None + + # Wait to connect + while not camera.isConnected(): + time.sleep(0.010) + + # Set rest + for arg in sys.argv[argc:]: + if propName is None: + propName = arg + else: + try: + propVal = int(arg) + except ValueError: + camera.getProperty(propName).setString(arg) + else: + camera.getProperty(propName).set(propVal) + + propName = None + + # Print settings + print("Properties:") + for prop in camera.enumerateProperties(): + kind = prop.getKind() + if kind == cs.VideoProperty.Kind.kBoolean: + print( + prop.getName(), + "(bool) value=%s default=%s" % (prop.get(), prop.getDefault()), + ) + elif kind == cs.VideoProperty.Kind.kInteger: + print( + prop.getName(), + "(int): value=%s min=%s max=%s step=%s default=%s" + % ( + prop.get(), + prop.getMin(), + prop.getMax(), + prop.getStep(), + prop.getDefault(), + ), + ) + elif kind == cs.VideoProperty.Kind.kString: + print(prop.getName(), "(string):", prop.getString()) + elif kind == cs.VideoProperty.Kind.kEnum: + print(prop.getName(), "(enum): value=%s" % prop.get()) + for i, choice in enumerate(prop.getChoices()): + if choice: + print(" %s: %s" % (i, choice)) + + +if __name__ == "__main__": + main() diff --git a/EXAMPLES/examplecscore/switched_cameraserver.py b/EXAMPLES/examplecscore/switched_cameraserver.py new file mode 100644 index 00000000..10e8d94a --- /dev/null +++ b/EXAMPLES/examplecscore/switched_cameraserver.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Uses the CameraServer class to automatically capture video from two USB +# webcams and send one of them to the dashboard without doing any processing. +# To switch between the cameras, change the /CameraPublisher/selected value in NetworkTables +# +# Warning: If you're using this with a python-based robot, do not run this +# in the same program as your robot code! +# + +from cscore import CameraServer as CS +from cscore import UsbCamera +import ntcore + + +def main(): + CS.enableLogging() + + usb0 = UsbCamera("Camera 0", 0) + usb1 = UsbCamera("Camera 1", 1) + + server = CS.addSwitchedCamera("Switched") + server.setSource(usb0) + + # Use networktables to switch the source + # -> obviously, you can switch them however you'd like + def _listener(source, key, value, isNew): + if str(value) == "0": + server.setSource(usb0) + elif str(value) == "1": + server.setSource(usb1) + + table = ntcore.NetworkTableInstance.getDefault().getTable("/CameraPublisher") + table.putString("selected", "0") + table.addEntryListener(_listener, key="selected") + + CS.waitForever() + + +if __name__ == "__main__": + # To see messages from networktables, you must setup logging + import logging + + logging.basicConfig(level=logging.DEBUG) + + # You should change this to connect to the RoboRIO + nt = ntcore.NetworkTableInstance.getDefault() + nt.setServer("localhost") + nt.startClient4(__file__) + + main() diff --git a/EXAMPLES/examplecscore/usbcvstream.py b/EXAMPLES/examplecscore/usbcvstream.py new file mode 100644 index 00000000..65651721 --- /dev/null +++ b/EXAMPLES/examplecscore/usbcvstream.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +# +# Demonstrates streaming and modifying the image via OpenCV +# + + +import cscore as cs +import numpy as np +import cv2 + + +def main(): + camera = cs.UsbCamera("usbcam", 0) + camera.setVideoMode(cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) + + mjpegServer = cs.MjpegServer("httpserver", 8081) + mjpegServer.setSource(camera) + + print("mjpg server listening at http://0.0.0.0:8081") + + cvsink = cs.CvSink("cvsink") + cvsink.setSource(camera) + + cvSource = cs.CvSource("cvsource", cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) + cvMjpegServer = cs.MjpegServer("cvhttpserver", 8082) + cvMjpegServer.setSource(cvSource) + + print("OpenCV output mjpg server listening at http://0.0.0.0:8082") + + test = np.zeros(shape=(240, 320, 3), dtype=np.uint8) + flip = np.zeros(shape=(240, 320, 3), dtype=np.uint8) + + while True: + time, test = cvsink.grabFrame(test) + if time == 0: + print("error:", cvsink.getError()) + continue + + print("got frame at time", time, test.shape) + + cv2.flip(test, flipCode=0, dst=flip) + cvSource.putFrame(flip) + + +if __name__ == "__main__": + main() diff --git a/EXAMPLES/examplecscore/usbstream.py b/EXAMPLES/examplecscore/usbstream.py new file mode 100644 index 00000000..ded3a399 --- /dev/null +++ b/EXAMPLES/examplecscore/usbstream.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 + +import cscore as cs + +camera = cs.UsbCamera("usbcam", 0) +camera.setVideoMode(cs.VideoMode.PixelFormat.kMJPEG, 320, 240, 30) + +mjpegServer = cs.MjpegServer("httpserver", 8081) +mjpegServer.setSource(camera) + +print("mjpg server listening at http://0.0.0.0:8081") +input("Press enter to exit...") diff --git a/EXAMPLES/examplentcore/pubsub_client.py b/EXAMPLES/examplentcore/pubsub_client.py new file mode 100644 index 00000000..f67257fc --- /dev/null +++ b/EXAMPLES/examplentcore/pubsub_client.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# +# A client that publishes some synchronized values periodically + +import argparse +import os +from os.path import basename +import logging +import time + +import ntcore + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + + parser = argparse.ArgumentParser() + parser.add_argument("ip", type=str, help="IP address to connect to") + args = parser.parse_args() + + # Initialize NT4 client + inst = ntcore.NetworkTableInstance.getDefault() + + identity = f"{basename(__file__)}-{os.getpid()}" + inst.startClient4(identity) + + inst.setServer(args.ip) + + # publish two values + table = inst.getTable("data") + pub1 = table.getDoubleTopic("1").publish() + pub2 = table.getDoubleTopic("2").publish() + + i = 3 + + while True: + # These values are being published fast than the server is polling + pub1.set(i) + pub2.set(i + 100) + + time.sleep(0.5) + i += 1 diff --git a/EXAMPLES/examplentcore/pubsub_server.py b/EXAMPLES/examplentcore/pubsub_server.py new file mode 100644 index 00000000..6356b0c5 --- /dev/null +++ b/EXAMPLES/examplentcore/pubsub_server.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# A server that reads from the subscription +# + +import logging +import time + +import ntcore + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + + # initialize networktables server (on a robot this is already done) + inst = ntcore.NetworkTableInstance.getDefault() + inst.startServer() + + # Initialize two subscriptions + table = inst.getTable("data") + + # only keep the latest value for this topic + sub1 = table.getDoubleTopic("1").subscribe(-1.0) + + # keep the last 10 values for this topic + sub2 = table.getDoubleTopic("2").subscribe( + -2.0, ntcore.PubSubOptions(pollStorage=10) + ) + + # Periodically read from them + # - note sub1 only has 1 value, but sub2 sometimes has more than 1 + while True: + print("---", ntcore._now()) + print("/data/1:", sub1.readQueue()) + print("/data/2:", sub2.readQueue()) + + time.sleep(1.2) diff --git a/EXAMPLES/examplentcore/simple_client.py b/EXAMPLES/examplentcore/simple_client.py new file mode 100644 index 00000000..d49155b7 --- /dev/null +++ b/EXAMPLES/examplentcore/simple_client.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# +# This is a NetworkTables client (eg, the DriverStation/coprocessor side). +# You need to tell it the IP address of the NetworkTables server (the +# robot or simulator). +# +# When running, this will continue incrementing the value 'dsTime', and the +# value should be visible to other networktables clients and the robot. +# + +import argparse +from os.path import basename +import logging +import time + +import ntcore + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + + parser = argparse.ArgumentParser() + parser.add_argument( + "-p", + "--protocol", + type=int, + choices=[3, 4], + help="NT Protocol to use", + default=4, + ) + parser.add_argument("ip", type=str, help="IP address to connect to") + args = parser.parse_args() + + inst = ntcore.NetworkTableInstance.getDefault() + + identity = basename(__file__) + if args.protocol == 3: + inst.startClient3(identity) + else: + inst.startClient4(identity) + + inst.setServer(args.ip) + + sd = inst.getTable("SmartDashboard") + + i = 0 + while True: + print("robotTime:", sd.getNumber("robotTime", -1)) + + sd.putNumber("dsTime", i) + time.sleep(1) + i += 1 diff --git a/EXAMPLES/examplentcore/simple_poller.py b/EXAMPLES/examplentcore/simple_poller.py new file mode 100644 index 00000000..3c7c78e8 --- /dev/null +++ b/EXAMPLES/examplentcore/simple_poller.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# +# This is a NetworkTables client (eg, the DriverStation/coprocessor side). +# You need to tell it the IP address of the NetworkTables server (the +# robot or simulator). +# +# This is intended to be ran at the same time as the simple_robot.py +# and simple_client.py examples. It will output values as they change. +# + +import argparse +from os.path import basename +import logging +import time + +import ntcore + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + + parser = argparse.ArgumentParser() + parser.add_argument( + "-p", + "--protocol", + type=int, + choices=[3, 4], + help="NT Protocol to use", + default=4, + ) + parser.add_argument("ip", type=str, help="IP address to connect to") + args = parser.parse_args() + + inst = ntcore.NetworkTableInstance.getDefault() + + identity = basename(__file__) + if args.protocol == 3: + inst.startClient3(identity) + else: + inst.startClient4(identity) + + inst.setServer(args.ip) + + # Create a poller + poller = ntcore.NetworkTableListenerPoller(inst) + + # Listen for all connection events + poller.addConnectionListener(True) + + # Listen to all changes + msub = ntcore.MultiSubscriber(inst, [""]) + poller.addListener(msub, ntcore.EventFlags.kValueRemote) + + while True: + # periodically read from the queue + for event in poller.readQueue(): + print(event) + + time.sleep(1) diff --git a/EXAMPLES/examplentcore/simple_robot.py b/EXAMPLES/examplentcore/simple_robot.py new file mode 100644 index 00000000..1eeb6ebc --- /dev/null +++ b/EXAMPLES/examplentcore/simple_robot.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# This is a NetworkTables server (eg, the robot or simulator side). +# +# On a real robot, you probably would create an instance of the +# wpilib.SmartDashboard object and use that instead -- but it's really +# just a passthru to the underlying NetworkTable object. +# +# When running, this will continue incrementing the value 'robotTime', +# and the value should be visible to networktables clients such as +# Shuffleboard or simple_client.py +# + +import logging +import time + +import ntcore + + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + + inst = ntcore.NetworkTableInstance.getDefault() + + inst.startServer() + sd = inst.getTable("SmartDashboard") + + i = 0 + while True: + print("dsTime:", sd.getNumber("dsTime", -1)) + + sd.putNumber("robotTime", i) + time.sleep(1) + i += 1 From e0b7ae61654d21a0542f766ddf3e7fccc6524779 Mon Sep 17 00:00:00 2001 From: Martin C <78377730+MilkManMaki@users.noreply.github.com> Date: Sun, 9 Jun 2024 02:46:56 -0400 Subject: [PATCH 3/4] fix: fix naming convention --- EXAMPLES/{examplecscore => cscore}/CameraServer.java | 0 EXAMPLES/{examplecscore => cscore}/cvstream.py | 0 EXAMPLES/{examplecscore => cscore}/dual_cameraserver.py | 0 EXAMPLES/{examplecscore => cscore}/enum_usb.py | 0 EXAMPLES/{examplecscore => cscore}/httpcvstream.py | 0 EXAMPLES/{examplecscore => cscore}/intermediate_cameraserver.py | 0 EXAMPLES/{examplecscore => cscore}/quick_cameraserver.py | 0 EXAMPLES/{examplecscore => cscore}/settings.py | 0 EXAMPLES/{examplecscore => cscore}/switched_cameraserver.py | 0 EXAMPLES/{examplecscore => cscore}/usbcvstream.py | 0 EXAMPLES/{examplecscore => cscore}/usbstream.py | 0 EXAMPLES/{examplentcore => ntcore}/pubsub_client.py | 0 EXAMPLES/{examplentcore => ntcore}/pubsub_server.py | 0 EXAMPLES/{examplentcore => ntcore}/simple_client.py | 0 EXAMPLES/{examplentcore => ntcore}/simple_poller.py | 0 EXAMPLES/{examplentcore => ntcore}/simple_robot.py | 0 16 files changed, 0 insertions(+), 0 deletions(-) rename EXAMPLES/{examplecscore => cscore}/CameraServer.java (100%) rename EXAMPLES/{examplecscore => cscore}/cvstream.py (100%) rename EXAMPLES/{examplecscore => cscore}/dual_cameraserver.py (100%) rename EXAMPLES/{examplecscore => cscore}/enum_usb.py (100%) rename EXAMPLES/{examplecscore => cscore}/httpcvstream.py (100%) rename EXAMPLES/{examplecscore => cscore}/intermediate_cameraserver.py (100%) rename EXAMPLES/{examplecscore => cscore}/quick_cameraserver.py (100%) rename EXAMPLES/{examplecscore => cscore}/settings.py (100%) rename EXAMPLES/{examplecscore => cscore}/switched_cameraserver.py (100%) rename EXAMPLES/{examplecscore => cscore}/usbcvstream.py (100%) rename EXAMPLES/{examplecscore => cscore}/usbstream.py (100%) rename EXAMPLES/{examplentcore => ntcore}/pubsub_client.py (100%) rename EXAMPLES/{examplentcore => ntcore}/pubsub_server.py (100%) rename EXAMPLES/{examplentcore => ntcore}/simple_client.py (100%) rename EXAMPLES/{examplentcore => ntcore}/simple_poller.py (100%) rename EXAMPLES/{examplentcore => ntcore}/simple_robot.py (100%) diff --git a/EXAMPLES/examplecscore/CameraServer.java b/EXAMPLES/cscore/CameraServer.java similarity index 100% rename from EXAMPLES/examplecscore/CameraServer.java rename to EXAMPLES/cscore/CameraServer.java diff --git a/EXAMPLES/examplecscore/cvstream.py b/EXAMPLES/cscore/cvstream.py similarity index 100% rename from EXAMPLES/examplecscore/cvstream.py rename to EXAMPLES/cscore/cvstream.py diff --git a/EXAMPLES/examplecscore/dual_cameraserver.py b/EXAMPLES/cscore/dual_cameraserver.py similarity index 100% rename from EXAMPLES/examplecscore/dual_cameraserver.py rename to EXAMPLES/cscore/dual_cameraserver.py diff --git a/EXAMPLES/examplecscore/enum_usb.py b/EXAMPLES/cscore/enum_usb.py similarity index 100% rename from EXAMPLES/examplecscore/enum_usb.py rename to EXAMPLES/cscore/enum_usb.py diff --git a/EXAMPLES/examplecscore/httpcvstream.py b/EXAMPLES/cscore/httpcvstream.py similarity index 100% rename from EXAMPLES/examplecscore/httpcvstream.py rename to EXAMPLES/cscore/httpcvstream.py diff --git a/EXAMPLES/examplecscore/intermediate_cameraserver.py b/EXAMPLES/cscore/intermediate_cameraserver.py similarity index 100% rename from EXAMPLES/examplecscore/intermediate_cameraserver.py rename to EXAMPLES/cscore/intermediate_cameraserver.py diff --git a/EXAMPLES/examplecscore/quick_cameraserver.py b/EXAMPLES/cscore/quick_cameraserver.py similarity index 100% rename from EXAMPLES/examplecscore/quick_cameraserver.py rename to EXAMPLES/cscore/quick_cameraserver.py diff --git a/EXAMPLES/examplecscore/settings.py b/EXAMPLES/cscore/settings.py similarity index 100% rename from EXAMPLES/examplecscore/settings.py rename to EXAMPLES/cscore/settings.py diff --git a/EXAMPLES/examplecscore/switched_cameraserver.py b/EXAMPLES/cscore/switched_cameraserver.py similarity index 100% rename from EXAMPLES/examplecscore/switched_cameraserver.py rename to EXAMPLES/cscore/switched_cameraserver.py diff --git a/EXAMPLES/examplecscore/usbcvstream.py b/EXAMPLES/cscore/usbcvstream.py similarity index 100% rename from EXAMPLES/examplecscore/usbcvstream.py rename to EXAMPLES/cscore/usbcvstream.py diff --git a/EXAMPLES/examplecscore/usbstream.py b/EXAMPLES/cscore/usbstream.py similarity index 100% rename from EXAMPLES/examplecscore/usbstream.py rename to EXAMPLES/cscore/usbstream.py diff --git a/EXAMPLES/examplentcore/pubsub_client.py b/EXAMPLES/ntcore/pubsub_client.py similarity index 100% rename from EXAMPLES/examplentcore/pubsub_client.py rename to EXAMPLES/ntcore/pubsub_client.py diff --git a/EXAMPLES/examplentcore/pubsub_server.py b/EXAMPLES/ntcore/pubsub_server.py similarity index 100% rename from EXAMPLES/examplentcore/pubsub_server.py rename to EXAMPLES/ntcore/pubsub_server.py diff --git a/EXAMPLES/examplentcore/simple_client.py b/EXAMPLES/ntcore/simple_client.py similarity index 100% rename from EXAMPLES/examplentcore/simple_client.py rename to EXAMPLES/ntcore/simple_client.py diff --git a/EXAMPLES/examplentcore/simple_poller.py b/EXAMPLES/ntcore/simple_poller.py similarity index 100% rename from EXAMPLES/examplentcore/simple_poller.py rename to EXAMPLES/ntcore/simple_poller.py diff --git a/EXAMPLES/examplentcore/simple_robot.py b/EXAMPLES/ntcore/simple_robot.py similarity index 100% rename from EXAMPLES/examplentcore/simple_robot.py rename to EXAMPLES/ntcore/simple_robot.py From 0b258b713e54aa9a1cb52634c732d4df4059c516 Mon Sep 17 00:00:00 2001 From: Martin C <78377730+MilkManMaki@users.noreply.github.com> Date: Sun, 9 Jun 2024 03:03:58 -0400 Subject: [PATCH 4/4] fix: rename examples case --- {EXAMPLES => examples}/cscore/CameraServer.java | 0 {EXAMPLES => examples}/cscore/cvstream.py | 0 {EXAMPLES => examples}/cscore/dual_cameraserver.py | 0 {EXAMPLES => examples}/cscore/enum_usb.py | 0 {EXAMPLES => examples}/cscore/httpcvstream.py | 0 {EXAMPLES => examples}/cscore/intermediate_cameraserver.py | 0 {EXAMPLES => examples}/cscore/quick_cameraserver.py | 0 {EXAMPLES => examples}/cscore/settings.py | 0 {EXAMPLES => examples}/cscore/switched_cameraserver.py | 0 {EXAMPLES => examples}/cscore/usbcvstream.py | 0 {EXAMPLES => examples}/cscore/usbstream.py | 0 {EXAMPLES => examples}/ntcore/pubsub_client.py | 0 {EXAMPLES => examples}/ntcore/pubsub_server.py | 0 {EXAMPLES => examples}/ntcore/simple_client.py | 0 {EXAMPLES => examples}/ntcore/simple_poller.py | 0 {EXAMPLES => examples}/ntcore/simple_robot.py | 0 16 files changed, 0 insertions(+), 0 deletions(-) rename {EXAMPLES => examples}/cscore/CameraServer.java (100%) rename {EXAMPLES => examples}/cscore/cvstream.py (100%) rename {EXAMPLES => examples}/cscore/dual_cameraserver.py (100%) rename {EXAMPLES => examples}/cscore/enum_usb.py (100%) rename {EXAMPLES => examples}/cscore/httpcvstream.py (100%) rename {EXAMPLES => examples}/cscore/intermediate_cameraserver.py (100%) rename {EXAMPLES => examples}/cscore/quick_cameraserver.py (100%) rename {EXAMPLES => examples}/cscore/settings.py (100%) rename {EXAMPLES => examples}/cscore/switched_cameraserver.py (100%) rename {EXAMPLES => examples}/cscore/usbcvstream.py (100%) rename {EXAMPLES => examples}/cscore/usbstream.py (100%) rename {EXAMPLES => examples}/ntcore/pubsub_client.py (100%) rename {EXAMPLES => examples}/ntcore/pubsub_server.py (100%) rename {EXAMPLES => examples}/ntcore/simple_client.py (100%) rename {EXAMPLES => examples}/ntcore/simple_poller.py (100%) rename {EXAMPLES => examples}/ntcore/simple_robot.py (100%) diff --git a/EXAMPLES/cscore/CameraServer.java b/examples/cscore/CameraServer.java similarity index 100% rename from EXAMPLES/cscore/CameraServer.java rename to examples/cscore/CameraServer.java diff --git a/EXAMPLES/cscore/cvstream.py b/examples/cscore/cvstream.py similarity index 100% rename from EXAMPLES/cscore/cvstream.py rename to examples/cscore/cvstream.py diff --git a/EXAMPLES/cscore/dual_cameraserver.py b/examples/cscore/dual_cameraserver.py similarity index 100% rename from EXAMPLES/cscore/dual_cameraserver.py rename to examples/cscore/dual_cameraserver.py diff --git a/EXAMPLES/cscore/enum_usb.py b/examples/cscore/enum_usb.py similarity index 100% rename from EXAMPLES/cscore/enum_usb.py rename to examples/cscore/enum_usb.py diff --git a/EXAMPLES/cscore/httpcvstream.py b/examples/cscore/httpcvstream.py similarity index 100% rename from EXAMPLES/cscore/httpcvstream.py rename to examples/cscore/httpcvstream.py diff --git a/EXAMPLES/cscore/intermediate_cameraserver.py b/examples/cscore/intermediate_cameraserver.py similarity index 100% rename from EXAMPLES/cscore/intermediate_cameraserver.py rename to examples/cscore/intermediate_cameraserver.py diff --git a/EXAMPLES/cscore/quick_cameraserver.py b/examples/cscore/quick_cameraserver.py similarity index 100% rename from EXAMPLES/cscore/quick_cameraserver.py rename to examples/cscore/quick_cameraserver.py diff --git a/EXAMPLES/cscore/settings.py b/examples/cscore/settings.py similarity index 100% rename from EXAMPLES/cscore/settings.py rename to examples/cscore/settings.py diff --git a/EXAMPLES/cscore/switched_cameraserver.py b/examples/cscore/switched_cameraserver.py similarity index 100% rename from EXAMPLES/cscore/switched_cameraserver.py rename to examples/cscore/switched_cameraserver.py diff --git a/EXAMPLES/cscore/usbcvstream.py b/examples/cscore/usbcvstream.py similarity index 100% rename from EXAMPLES/cscore/usbcvstream.py rename to examples/cscore/usbcvstream.py diff --git a/EXAMPLES/cscore/usbstream.py b/examples/cscore/usbstream.py similarity index 100% rename from EXAMPLES/cscore/usbstream.py rename to examples/cscore/usbstream.py diff --git a/EXAMPLES/ntcore/pubsub_client.py b/examples/ntcore/pubsub_client.py similarity index 100% rename from EXAMPLES/ntcore/pubsub_client.py rename to examples/ntcore/pubsub_client.py diff --git a/EXAMPLES/ntcore/pubsub_server.py b/examples/ntcore/pubsub_server.py similarity index 100% rename from EXAMPLES/ntcore/pubsub_server.py rename to examples/ntcore/pubsub_server.py diff --git a/EXAMPLES/ntcore/simple_client.py b/examples/ntcore/simple_client.py similarity index 100% rename from EXAMPLES/ntcore/simple_client.py rename to examples/ntcore/simple_client.py diff --git a/EXAMPLES/ntcore/simple_poller.py b/examples/ntcore/simple_poller.py similarity index 100% rename from EXAMPLES/ntcore/simple_poller.py rename to examples/ntcore/simple_poller.py diff --git a/EXAMPLES/ntcore/simple_robot.py b/examples/ntcore/simple_robot.py similarity index 100% rename from EXAMPLES/ntcore/simple_robot.py rename to examples/ntcore/simple_robot.py