001    /*----------------------------------------------------------------------------*/
002    /* Copyright (c) FIRST 2008-2012. All Rights Reserved.                        */
003    /* Open Source Software - may be modified and shared by FRC teams. The code   */
004    /* must be accompanied by the FIRST BSD license file in the root directory of */
005    /* the project.                                                               */
006    /*----------------------------------------------------------------------------*/
007    package edu.wpi.first.wpilibj.camera;
008    
009    import com.sun.cldc.jna.BlockingFunction;
010    import com.sun.cldc.jna.Function;
011    import com.sun.cldc.jna.NativeLibrary;
012    import com.sun.cldc.jna.Pointer;
013    import com.sun.cldc.jna.TaskExecutor;
014    import com.sun.squawk.VM;
015    import edu.wpi.first.wpilibj.DriverStation;
016    import edu.wpi.first.wpilibj.communication.UsageReporting;
017    import edu.wpi.first.wpilibj.image.ColorImage;
018    import edu.wpi.first.wpilibj.image.HSLImage;
019    import edu.wpi.first.wpilibj.image.NIVisionException;
020    import edu.wpi.first.wpilibj.parsing.ISensor;
021    import edu.wpi.first.wpilibj.util.BoundaryException;
022    
023    //TODO figure out where to use finally to free resources
024    //TODO go through old camera code and make sure all features are implemented
025    //TODO make work with all three passwords
026    //TODO get images of different types
027    //TODO continue attempting to connect until succesful
028    //TODO optimize and use Pointers in all locations which make sense - possibly JNA memcpy?
029    /**
030     * This class is a singleton used to configure and get images from the axis camera.
031     * @author dtjones
032     */
033    public class AxisCamera implements ISensor {
034    
035        private static AxisCamera m_instance = null;
036    
037        /**
038         * Enumaration representing the different values which exposure may be set to.
039         */
040        public static class ExposureT {
041    
042            /**
043             * The integer value of the enumeration.
044             */
045            public final int value;
046            static final ExposureT[] allValues = new ExposureT[4];
047            /**
048             * The Axis camera automatically determines what exposure level to use.
049             */
050            public static final ExposureT automatic = new ExposureT(0);
051            /**
052             * Hold the current exposure level.
053             */
054            public static final ExposureT hold = new ExposureT(1);
055            /**
056             * Set exposure for flicker free 50 hz.
057             */
058            public static final ExposureT flickerfree50 = new ExposureT(2);
059            /**
060             * Set exposure for flicker free 60 hz.
061             */
062            public static final ExposureT flickerfree60 = new ExposureT(3);
063    
064            private ExposureT(int value) {
065                this.value = value;
066                allValues[value] = this;
067            }
068    
069            private static ExposureT get(int val) {
070                return allValues[val];
071            }
072        }
073    
074        /**
075         * Enumeration representing the different values which white balence may be
076         * set to.
077         */
078        public static class WhiteBalanceT {
079    
080            /**
081             * The integer value of the enumeration.
082             */
083            public final int value;
084            static final WhiteBalanceT[] allValues = new WhiteBalanceT[7];
085            /**
086             * The axis camera automatically adjusts the whit balance.
087             */
088            public static final WhiteBalanceT automatic = new WhiteBalanceT(0);
089            /**
090             * Hold the current white balance.
091             */
092            public static final WhiteBalanceT hold = new WhiteBalanceT(1);
093            /**
094             * White balance for outdoors.
095             */
096            public static final WhiteBalanceT fixedOutdoor1 = new WhiteBalanceT(2);
097            /**
098             * White balance for outdoors.
099             */
100            public static final WhiteBalanceT fixedOutdoor2 = new WhiteBalanceT(3);
101            /**
102             * White balance for indoors.
103             */
104            public static final WhiteBalanceT fixedIndoor = new WhiteBalanceT(4);
105            /**
106             * White balance for fourescent lighting.
107             */
108            public static final WhiteBalanceT fixedFlour1 = new WhiteBalanceT(5);
109            /**
110             * White balance for fourescent lighting.
111             */
112            public static final WhiteBalanceT fixedFlour2 = new WhiteBalanceT(6);
113    
114            private WhiteBalanceT(int value) {
115                this.value = value;
116                allValues[value] = this;
117            }
118    
119            private static WhiteBalanceT get(int value) {
120                return allValues[value];
121            }
122        }
123    
124        /**
125         * Enumeration representing the image resoultion provided by the camera.
126         */
127        public static class ResolutionT {
128    
129            /**
130             * The integer value of the enumeration.
131             */
132            public final int value;
133            /**
134             * Number of pixels wide.
135             */
136            public final int width;
137            /**
138             * Number of pixels tall.
139             */
140            public final int height;
141            static final ResolutionT[] allValues = new ResolutionT[4];
142            /**
143             * Image is 640 pixels wide by 480 tall
144             */
145            public static final ResolutionT k640x480 = new ResolutionT(0, 640, 480);
146            /**
147             * Image is 640 pixels wide by 360 tall
148             */
149            public static final ResolutionT k640x360 = new ResolutionT(1, 640, 360);
150            /**
151             * Image is 320 pixels wide by 240 tall
152             */
153            public static final ResolutionT k320x240 = new ResolutionT(2, 320, 240);
154            /**
155             * Image is 160 pixels wide by 120 tall
156             */
157            public static final ResolutionT k160x120 = new ResolutionT(3, 160, 120);
158    
159            private ResolutionT(int value, int horizontal, int vertical) {
160                this.value = value;
161                this.width = horizontal;
162                this.height = vertical;
163                allValues[value] = this;
164            }
165    
166            private static ResolutionT get(int value) {
167                return allValues[value];
168            }
169        }
170    
171        /**
172         * Enumeration representing the orientation of the picture.
173         */
174        public static class RotationT {
175    
176            /**
177             * The integer value of the enumeration.
178             */
179            public final int value;
180            static final RotationT[] allValues = new RotationT[2];
181            /**
182             * Picture is right side up.
183             */
184            public static final RotationT k0 = new RotationT(0);
185            /**
186             * Picture is rotated 180 degrees.
187             */
188            public static final RotationT k180 = new RotationT(1);
189    
190            private RotationT(int value) {
191                this.value = value;
192                allValues[value] = this;
193            }
194    
195            private static RotationT get(int value) {
196                return allValues[value];
197            }
198        }
199    
200        /**
201         * Enumeration representing the exposure priority.
202         */
203        public static class ExposurePriorityT {
204    
205            /**
206             * The integer value of the enumeration.
207             */
208            public final int value;
209            static final ExposurePriorityT[] allValues = new ExposurePriorityT[3];
210            /**
211             * Prioritize image quality.
212             */
213            public static final ExposurePriorityT imageQuality = new ExposurePriorityT(0);
214            /**
215             * No prioritization.
216             */
217            public static final ExposurePriorityT none = new ExposurePriorityT(50);
218            /**
219             * Prioritize frame rate.
220             */
221            public static final ExposurePriorityT frameRate = new ExposurePriorityT(100);
222    
223            private ExposurePriorityT(int value) {
224                this.value = value;
225                allValues[value / 50] = this;
226            }
227    
228            private static ExposurePriorityT get(int value) {
229                return allValues[value / 50];
230            }
231        }
232    
233        /**
234         * Get a reference to the AxisCamera, or initialize the AxisCamera if it
235         * has not yet been initialized. If the camera is connected to the
236         * Ethernet switch on the robot, then this address should be 10.x.y.11
237         * where x.y are your team number subnet address (same as the other IP
238         * addresses on the robot network).
239         * @param address A string containing the IP address for the camera in the
240         * form "10.x.y.2" for cameras on the Ethernet switch or "192.168.0.90"
241         * for cameras connected to the 2nd Ethernet port on an 8-slot cRIO.
242         * @return A reference to the AxisCamera.
243         */
244        public static synchronized AxisCamera getInstance(String address) {
245            if (m_instance == null) {
246                m_instance = new AxisCamera(address);
247                UsageReporting.report(UsageReporting.kResourceType_AxisCamera, 2);
248            }
249            return m_instance;
250        }
251    
252        /**
253         * Get a reference to the AxisCamera, or initialize the AxisCamera if it
254         * has not yet been initialized. By default this will connect to a camera
255         * with an IP address of 10.x.y.11 with the preference that the camera be
256         * connected to the Ethernet switch on the robot rather than port 2 of the
257         * 8-slot cRIO.
258         * @return A reference to the AxisCamera.
259         */
260        public static synchronized AxisCamera getInstance() {
261            if (m_instance == null) {
262                DriverStation.getInstance().waitForData();
263                int teamNumber = DriverStation.getInstance().getTeamNumber();
264                String address = "10."+(teamNumber/100)+"."+(teamNumber%100)+".11";
265                m_instance = new AxisCamera(address);
266                UsageReporting.report(UsageReporting.kResourceType_AxisCamera, 1);
267            }
268    
269            return m_instance;
270        }
271        private static final Function cameraStartFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraStart");
272    
273        /**
274         * Axis camera constructor that calls the C++ library to actually create the instance.
275         * @param IPAddress 
276         */
277        AxisCamera(String IPAddress) {
278            Pointer ptr = Pointer.createStringBuffer(IPAddress);
279            cameraStartFn.call1(ptr);
280        }
281        private static final TaskExecutor cameraTaskExecutor = new TaskExecutor("camera task executor");
282        private static final BlockingFunction getImageFn = NativeLibrary.getDefaultInstance().getBlockingFunction("AxisCameraGetImage");
283    
284        static {
285            getImageFn.setTaskExecutor(cameraTaskExecutor);
286        }
287    
288        /**
289         * Get an image from the camera. Be sure to free the image when you are done with it.
290         * @return A new image from the camera.
291         */
292        public ColorImage getImage() throws AxisCameraException, NIVisionException {
293            ColorImage image = new HSLImage();
294            if (getImageFn.call1(image.image) == 0) {
295                image.free();
296                throw new AxisCameraException("No image available");
297            }
298            return image;
299        }
300        // Mid-stream gets & writes
301        private static final Function writeBrightnessFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteBrightness");
302    
303        /**
304         * Write the brightness for the camera to use.
305         * @param brightness This must be an integer between 0 and 100, with 100 being the brightest
306         */
307        public void writeBrightness(int brightness) {
308            BoundaryException.assertWithinBounds(brightness, 0, 100);
309            writeBrightnessFn.call1(brightness);
310        }
311        private static final Function getBrightnessFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetBrightness");
312    
313        /**
314         * Get the current brightness of the AxisCamera
315         * @return An integer representing the current brightness of the axis
316         * camera with 0 being the darkest and 100 being the brightest.
317         */
318        public int getBrightness() {
319            return getBrightnessFn.call0();
320        }
321        private static final Function writeWhiteBalenceFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteWhiteBalance");
322    
323        /**
324         * Write the WhiteBalance for the camera to use.
325         * @param whiteBalance The value to set the white balance to on the camera.
326         */
327        public void writeWhiteBalance(WhiteBalanceT whiteBalance) {
328            writeWhiteBalenceFn.call1(whiteBalance.value);
329        }
330        private static final Function getWhiteBalanceFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetWhiteBalance");
331    
332        /**
333         * Get the white balance set on the camera.
334         * @return The white balance currently set on the camera.
335         */
336        public WhiteBalanceT getWhiteBalance() {
337            return WhiteBalanceT.get(getWhiteBalanceFn.call0());
338        }
339        private static final Function writeColorLevelFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteColorLevel");
340    
341        /**
342         * Set the color level of images returned from the camera.
343         * @param value This must be an integer from 0 to 100 with 0 being black and white.
344         */
345        public void writeColorLevel(int value) {
346            BoundaryException.assertWithinBounds(value, 0, 100);
347            writeColorLevelFn.call1(value);
348        }
349        private static final Function getColorLevelFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetColorLevel");
350    
351        /**
352         * Get the color level of images being retunred from the camera.
353         * @return The color level of images being returned from the camera. 0 is black and white.
354         */
355        public int getColorLevel() {
356            return getColorLevelFn.call0();
357        }
358        private static final Function writeExposureControlFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteExposureControl");
359    
360        /**
361         * Write the exposure mode for the camera to use.
362         * @param value The expsure mode for the camera to use.
363         */
364        public void writeExposureControl(ExposureT value) {
365            writeExposureControlFn.call1(value.value);
366        }
367        private static final Function getExposureControlFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetExposureControl");
368    
369        /**
370         * Get the exposure mode that the camera is using.
371         * @return The exposure mode that the camera is using.
372         */
373        public ExposureT getExposureControl() {
374            return ExposureT.get(getExposureControlFn.call0());
375        }
376        private static final Function writeExposurePriorityFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteExposurePriority");
377    
378        /**
379         * Write the exposure priority for the camera to use.
380         * @param value The exposure priority to use.
381         */
382        public void writeExposurePriority(ExposurePriorityT value) {
383            writeExposurePriorityFn.call1(value.value);
384        }
385        private static final Function getExposurePriorityFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetExposurePriority");
386    
387        /**
388         * Get the exposure priority that the camera is using.
389         * @return The exposure priority that the camera is using
390         */
391        public ExposurePriorityT getExposurePriority() {
392            return ExposurePriorityT.get(getExposurePriorityFn.call0());
393        }
394        // New-Stream gets & writes
395        private static final Function writeResolutionFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteResolution");
396    
397        /**
398         * Set the resolution of the images to return.
399         * @param value The resolution imaga for the camera to return.
400         */
401        public void writeResolution(ResolutionT value) {
402            writeResolutionFn.call1(value.value);
403        }
404        private static final Function getResolutionFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetResolution");
405    
406        /**
407         * Get the resolution fo the images that the camera is returning.
408         * @return  The resolution of the images that the camera is returning.
409         */
410        public ResolutionT getResolution() {
411            return ResolutionT.get(getResolutionFn.call0());
412        }
413        private static final Function writeCompressionFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteCompression");
414    
415        /**
416         * Write the level of JPEG compression to use on images returned from the camera.
417         * @param value The level of JPEG compression to use from 0 to 100 with 0 being the least compression.
418         */
419        public void writeCompression(int value) {
420            BoundaryException.assertWithinBounds(value, 0, 100);
421            writeCompressionFn.call1(value);
422        }
423        private static final Function getCompressionFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetCompression");
424    
425        /**
426         * Get the compression of the images eing returned by the camera.
427         * @return The level of compression of the image being returned from the
428         * camera with 0 being the least and 100 being the greatest.
429         */
430        public int getCompression() {
431            return getCompressionFn.call0();
432        }
433        private static final Function writeRotationFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteRotation");
434    
435        /**
436         * Write the rotation of images for the camera to return.
437         * @param value The rotation to be applied to images from the camera.
438         */
439        public void writeRotation(RotationT value) {
440            writeRotationFn.call1(value.value);
441        }
442        private static final Function getRotationFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetRotation");
443    
444        /**
445         * Get the rotation of the images returned from the camera.
446         * @return The rotation of the images returned from the camera.
447         */
448        public RotationT getRotation() {
449            return RotationT.get(getRotationFn.call0());
450        }
451        private static final Function freshImageFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraFreshImage");
452    
453        /**
454         * Has the current image from the camera been retrieved yet.
455         * @return true if the latest image from the camera has not been retrieved yet.
456         */
457        public boolean freshImage() {
458            return freshImageFn.call0() == 0 ? false : true;
459        }
460        private static final Function getMaxFPSFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraGetMaxFPS");
461    
462        /**
463         * Get the maximum frames per second that the camera will generate.
464         * @return the max fps.
465         */
466        public int getMaxFPS() {
467            return getMaxFPSFn.call0();
468        }
469        private static final Function writeMaxFPSFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraWriteMaxFPS");
470    
471        /**
472         * Set the maximum frames per second that the camera will generate.
473         * @param value the new max fps
474         */
475        public void writeMaxFPS(int value) {
476            writeMaxFPSFn.call1(value);
477        }
478        private static final Function deleteInstanceFn = NativeLibrary.getDefaultInstance().getFunction("AxisCameraDeleteInstance");
479    
480        static {
481            VM.addShutdownHook(new Thread() {
482    
483                public void run() {
484                    deleteInstanceFn.call0();
485                }
486            });
487        }
488    }