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 }