blob: 8bcc54d031bf46790038186f1e12f46ca8a9b97b [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2018 FIRST. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8import java.io.IOException;
9import java.nio.file.Files;
10import java.nio.file.Paths;
11import java.util.ArrayList;
12import java.util.List;
13
14import com.google.gson.Gson;
15import com.google.gson.GsonBuilder;
16import com.google.gson.JsonArray;
17import com.google.gson.JsonElement;
18import com.google.gson.JsonObject;
19import com.google.gson.JsonParser;
20
21import edu.wpi.cscore.VideoSource;
22import edu.wpi.first.cameraserver.CameraServer;
23import edu.wpi.first.networktables.NetworkTableInstance;
24
25/*
26 JSON format:
27 {
28 "team": <team number>,
29 "ntmode": <"client" or "server", "client" if unspecified>
30 "cameras": [
31 {
32 "name": <camera name>
33 "path": <path, e.g. "/dev/video0">
34 "pixel format": <"MJPEG", "YUYV", etc> // optional
35 "width": <video mode width> // optional
36 "height": <video mode height> // optional
37 "fps": <video mode fps> // optional
38 "brightness": <percentage brightness> // optional
39 "white balance": <"auto", "hold", value> // optional
40 "exposure": <"auto", "hold", value> // optional
41 "properties": [ // optional
42 {
43 "name": <property name>
44 "value": <property value>
45 }
46 ]
47 }
48 ]
49 }
50 */
51
52public final class Main {
53 private static String configFile = "/boot/frc.json";
54
55 @SuppressWarnings("MemberName")
56 public static class CameraConfig {
57 public String name;
58 public String path;
59 public JsonObject config;
60 }
61
62 public static int team;
63 public static boolean server;
64 public static List<CameraConfig> cameras = new ArrayList<>();
65
66 private Main() {
67 }
68
69 /**
70 * Report parse error.
71 */
72 public static void parseError(String str) {
73 System.err.println("config error in '" + configFile + "': " + str);
74 }
75
76 /**
77 * Read single camera configuration.
78 */
79 public static boolean readCameraConfig(JsonObject config) {
80 CameraConfig cam = new CameraConfig();
81
82 // name
83 JsonElement nameElement = config.get("name");
84 if (nameElement == null) {
85 parseError("could not read camera name");
86 return false;
87 }
88 cam.name = nameElement.getAsString();
89
90 // path
91 JsonElement pathElement = config.get("path");
92 if (pathElement == null) {
93 parseError("camera '" + cam.name + "': could not read path");
94 return false;
95 }
96 cam.path = pathElement.getAsString();
97
98 cam.config = config;
99
100 cameras.add(cam);
101 return true;
102 }
103
104 /**
105 * Read configuration file.
106 */
107 @SuppressWarnings("PMD.CyclomaticComplexity")
108 public static boolean readConfig() {
109 // parse file
110 JsonElement top;
111 try {
112 top = new JsonParser().parse(Files.newBufferedReader(Paths.get(configFile)));
113 } catch (IOException ex) {
114 System.err.println("could not open '" + configFile + "': " + ex);
115 return false;
116 }
117
118 // top level must be an object
119 if (!top.isJsonObject()) {
120 parseError("must be JSON object");
121 return false;
122 }
123 JsonObject obj = top.getAsJsonObject();
124
125 // team number
126 JsonElement teamElement = obj.get("team");
127 if (teamElement == null) {
128 parseError("could not read team number");
129 return false;
130 }
131 team = teamElement.getAsInt();
132
133 // ntmode (optional)
134 if (obj.has("ntmode")) {
135 String str = obj.get("ntmode").getAsString();
136 if ("client".equalsIgnoreCase(str)) {
137 server = false;
138 } else if ("server".equalsIgnoreCase(str)) {
139 server = true;
140 } else {
141 parseError("could not understand ntmode value '" + str + "'");
142 }
143 }
144
145 // cameras
146 JsonElement camerasElement = obj.get("cameras");
147 if (camerasElement == null) {
148 parseError("could not read cameras");
149 return false;
150 }
151 JsonArray cameras = camerasElement.getAsJsonArray();
152 for (JsonElement camera : cameras) {
153 if (!readCameraConfig(camera.getAsJsonObject())) {
154 return false;
155 }
156 }
157
158 return true;
159 }
160
161 /**
162 * Start running the camera.
163 */
164 public static void startCamera(CameraConfig config) {
165 System.out.println("Starting camera '" + config.name + "' on " + config.path);
166 VideoSource camera = CameraServer.getInstance().startAutomaticCapture(
167 config.name, config.path);
168
169 Gson gson = new GsonBuilder().create();
170
171 camera.setConfigJson(gson.toJson(config.config));
172 }
173
174 /**
175 * Main.
176 */
177 public static void main(String... args) {
178 if (args.length > 0) {
179 configFile = args[0];
180 }
181
182 // read configuration
183 if (!readConfig()) {
184 return;
185 }
186
187 // start NetworkTables
188 NetworkTableInstance ntinst = NetworkTableInstance.getDefault();
189 if (server) {
190 System.out.println("Setting up NetworkTables server");
191 ntinst.startServer();
192 } else {
193 System.out.println("Setting up NetworkTables client for team " + team);
194 ntinst.startClientTeam(team);
195 }
196
197 // start cameras
198 for (CameraConfig camera : cameras) {
199 startCamera(camera);
200 }
201
202 // loop forever
203 for (;;) {
204 try {
205 Thread.sleep(10000);
206 } catch (InterruptedException ex) {
207 return;
208 }
209 }
210 }
211}