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}