001/* 002 * Copyright (C) Photon Vision. 003 * 004 * This program is free software: you can redistribute it and/or modify 005 * it under the terms of the GNU General Public License as published by 006 * the Free Software Foundation, either version 3 of the License, or 007 * (at your option) any later version. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU General Public License for more details. 013 * 014 * You should have received a copy of the GNU General Public License 015 * along with this program. If not, see <https://www.gnu.org/licenses/>. 016 */ 017 018package org.photonvision.vision.camera; 019 020import com.fasterxml.jackson.annotation.JsonCreator; 021import com.fasterxml.jackson.annotation.JsonGetter; 022import com.fasterxml.jackson.annotation.JsonProperty; 023import com.fasterxml.jackson.annotation.JsonSubTypes; 024import com.fasterxml.jackson.annotation.JsonTypeInfo; 025import com.fasterxml.jackson.annotation.JsonTypeName; 026import edu.wpi.first.cscore.UsbCameraInfo; 027import java.util.Arrays; 028 029@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) 030@JsonSubTypes({ 031 @JsonSubTypes.Type(value = PVCameraInfo.PVUsbCameraInfo.class), 032 @JsonSubTypes.Type(value = PVCameraInfo.PVCSICameraInfo.class), 033 @JsonSubTypes.Type(value = PVCameraInfo.PVFileCameraInfo.class) 034}) 035public sealed interface PVCameraInfo { 036 /** 037 * @return The path of the camera. 038 */ 039 String path(); 040 041 /** 042 * @return The base name of the camera aka the name as just ascii. 043 */ 044 String name(); 045 046 /** 047 * @return Returns a human readable name 048 */ 049 default String humanReadableName() { 050 return name().replaceAll(" ", "_"); 051 } 052 053 /** 054 * If the camera is a USB camera this method returns a unique descriptor of the USB port this 055 * camera is attached to. EG "/dev/v4l/by-path/platform-fc800000.usb-usb-0:1.3:1.0-video-index0". 056 * If the camera is a CSI camera this method returns the path of the camera. 057 * 058 * <p>If we are on Windows, this will return the opaque path as described by 059 * MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK (see 060 * https://learn.microsoft.com/en-us/windows/win32/medfound/mf-devsource-attribute-source-type-vidcap-symbolic-link) 061 * 062 * @return The unique path of the camera 063 */ 064 @JsonGetter(value = "uniquePath") 065 String uniquePath(); 066 067 String[] otherPaths(); 068 069 CameraType type(); 070 071 default boolean equals(PVCameraInfo other) { 072 return uniquePath().equals(other.uniquePath()); 073 } 074 075 @JsonTypeName("PVUsbCameraInfo") 076 public static final class PVUsbCameraInfo extends UsbCameraInfo implements PVCameraInfo { 077 @JsonCreator 078 public PVUsbCameraInfo( 079 @JsonProperty("dev") int dev, 080 @JsonProperty("path") String path, 081 @JsonProperty("name") String name, 082 @JsonProperty("otherPaths") String[] otherPaths, 083 @JsonProperty("vendorId") int vendorId, 084 @JsonProperty("productId") int productId) { 085 super(dev, path, name, otherPaths, vendorId, productId); 086 } 087 088 private PVUsbCameraInfo(UsbCameraInfo info) { 089 super(info.dev, info.path, info.name, info.otherPaths, info.vendorId, info.productId); 090 } 091 092 @Override 093 public String path() { 094 return super.path; 095 } 096 097 @Override 098 public String name() { 099 return super.name.replaceAll("[^\\x00-\\x7F]", ""); 100 } 101 102 @Override 103 public String uniquePath() { 104 return Arrays.stream(super.otherPaths) 105 .sorted() // Must sort to ensure a consistent unique path as we can get more than one 106 // by-path and their order changes at random? 107 .filter(path -> path.contains("/by-path/")) 108 .findFirst() 109 .orElse(path()); 110 } 111 112 @Override 113 public String[] otherPaths() { 114 return super.otherPaths; 115 } 116 117 @Override 118 public CameraType type() { 119 return CameraType.UsbCamera; 120 } 121 122 @Override 123 public boolean equals(Object obj) { 124 if (this == obj) return true; 125 if (obj == null) return false; 126 if (obj instanceof PVCameraInfo info) { 127 return equals(info); 128 } 129 return false; 130 } 131 132 @Override 133 public String toString() { 134 return "PVUsbCameraInfo[type=" 135 + type() 136 + ", dev=" 137 + super.dev 138 + ", path='" 139 + super.path 140 + "', name='" 141 + super.name 142 + "', otherPaths=" 143 + Arrays.toString(super.otherPaths) 144 + ", vid=" 145 + super.vendorId 146 + ", pid=" 147 + super.productId 148 + ", uniquePath='" 149 + uniquePath() 150 + "']"; 151 } 152 } 153 154 @JsonTypeName("PVCSICameraInfo") 155 public static final class PVCSICameraInfo implements PVCameraInfo { 156 public final String path; 157 public final String baseName; 158 159 @JsonCreator 160 public PVCSICameraInfo( 161 @JsonProperty("path") String path, @JsonProperty("baseName") String baseName) { 162 this.path = path; 163 this.baseName = baseName; 164 } 165 166 @Override 167 public String path() { 168 return path; 169 } 170 171 @Override 172 public String name() { 173 return baseName.replaceAll("[^\\x00-\\x7F]", ""); 174 } 175 176 @Override 177 public String uniquePath() { 178 return path(); 179 } 180 181 @Override 182 public String[] otherPaths() { 183 return new String[0]; 184 } 185 186 @Override 187 public CameraType type() { 188 return CameraType.ZeroCopyPicam; 189 } 190 191 @Override 192 public boolean equals(Object obj) { 193 if (this == obj) return true; 194 if (obj == null) return false; 195 if (obj instanceof PVCameraInfo info) { 196 return equals(info); 197 } 198 return false; 199 } 200 201 @Override 202 public String toString() { 203 return "PVCsiCameraInfo[type=" 204 + type() 205 + ", basename=" 206 + baseName 207 + ", path='" 208 + path 209 + "', uniquePath='" 210 + uniquePath() 211 + "']"; 212 } 213 } 214 215 @JsonTypeName("PVFileCameraInfo") 216 public static final class PVFileCameraInfo implements PVCameraInfo { 217 public final String path; 218 public final String name; 219 220 @JsonCreator 221 public PVFileCameraInfo(@JsonProperty("path") String path, @JsonProperty("name") String name) { 222 this.path = path; 223 this.name = name; 224 } 225 226 @Override 227 public String path() { 228 return path; 229 } 230 231 @Override 232 public String name() { 233 return name; 234 } 235 236 @Override 237 public String uniquePath() { 238 return path(); 239 } 240 241 @Override 242 public String[] otherPaths() { 243 return new String[0]; 244 } 245 246 @Override 247 public CameraType type() { 248 return CameraType.FileCamera; 249 } 250 251 @Override 252 public boolean equals(Object obj) { 253 if (this == obj) return true; 254 if (obj == null) return false; 255 if (obj instanceof PVFileCameraInfo info) { 256 return equals(info); 257 } 258 return false; 259 } 260 261 @Override 262 public String toString() { 263 return "PVFileCameraInfo[type=" + type() + ", filename=" + name + ", path='" + path + "']"; 264 } 265 } 266 267 public static PVCameraInfo fromUsbCameraInfo(UsbCameraInfo info) { 268 return new PVUsbCameraInfo(info); 269 } 270 271 public static PVCameraInfo fromCSICameraInfo(String path, String baseName) { 272 return new PVCSICameraInfo(path, baseName); 273 } 274 275 public static PVCameraInfo fromFileInfo(String path, String baseName) { 276 return new PVFileCameraInfo(path, baseName); 277 } 278}