diff --git a/README.md b/README.md index fce7089..a01d4a0 100755 --- a/README.md +++ b/README.md @@ -8,14 +8,14 @@ Copyright (c): 2018-2019 Brookhaven National Laboratory ### An EPICS Driver for USB Video Class (UVC) devices -Release versions of this driver are available on Github. Release notes are available on https://jwlodek.github.io/ADUVC. Please report any problems or feature requests on the issues page on https://github.com/jwlodek/ADUVC. +Release versions of this driver are available on Github. Release notes are available on https://jwlodek.github.io/ADUVC. Please report any problems or feature requests on the issues page on https://github.com/epicsNSLS2-areaDetector/ADUVC. ### Installation Prior to installing the ADUVC area detector driver, there are several dependencies that must be met. First, libusb, libjpeg, and cmake and their development packages must be installed. On a debian based linux machine, this can be done with the following command: ``` -sudo apt install libusb-dev libjpeg-dev cmake +sudo apt install libusb-dev libusub-1.0-0-dev libjpeg-dev cmake ``` If you wish to use the libjpeg version included with ADSupport, it is important to specifiy that during the build of libuvc, because otherwise there will be a conflict when building ADUVC. The simplest solution is to set JPEG_EXTERNAL=YES in the CONFIG_SITE.local file in the configure directory at the top level of areaDetector. @@ -61,7 +61,7 @@ sudo ./startEPICS.sh Note that running as sudo is not required if connecting via product ID. -Further documentation is available at https://jwlodek.github.io/ADUVC +Further documentation, including CSS screenshots and usage information, is available at the driver's [website](https://jwlodek.github.io/ADUVC). ### Possible use cases @@ -90,7 +90,7 @@ There are also several more traditional industrial cameras that use the UVC stan * If using ADUVC with Virtualbox, you need to passthrough the hold of the camera to the guest OS. Instructions for doing so can be found on this website: https://scribles.net/using-webcam-in-virtualbox-guest-os-on-windows-host/ -### USB Camera IOC Issues +### Fixing issues with root ownership of UVC devices * The USB camera device is typically owned by root, which prevents EPICS IOC from running as softioc user, and automatic startup using procServer. To grant access to USB camera device by other users, such as softioc, we wrote udev rules. diff --git a/RELEASE.md b/RELEASE.md index ddac856..04561aa 100755 --- a/RELEASE.md +++ b/RELEASE.md @@ -4,9 +4,28 @@ Author: Jakub Wlodek ADUVC requires libusb, libuvc, epics-base, epics-modules, ADCore, and ADSupport. Further installation information can be found in the README file. + Release Notes ============= +R1-2 (11-June-2019) +----- +* Key detector features implemented: + * Camera modes now read into structs at startup + * Valid camera mode structs selectable from dropdown in CSS - improves usability + * Autosave functionality tested and working. + * More extensive status messages/detector feedback + +* Key fixes and improvements + * Removed unused PVs (ADUVC_VendorID, ADUVC_ProductID) + * Fixed memory leak caused by early return from frame conversion function on error + * Added Makefiles to the support modules + * Documentation updates + * Added information on using camera with Virtual Box + * Removed newlines from status messages for better readability. + * CSS screen updated + + R1-1 (28-January-2019) ----- * Key detector features implemented: diff --git a/adUVCApp/Db/ADUVC.template b/adUVCApp/Db/ADUVC.template index e5e341e..decb543 100755 --- a/adUVCApp/Db/ADUVC.template +++ b/adUVCApp/Db/ADUVC.template @@ -128,8 +128,81 @@ record(mbbi, "$(P)$(R)UVCImageFormat_RBV"){ field(SCAN, "I/O Intr") } +############################################## +# Stores supported camera modes for easier switching +################################################ +record(mbbo, "$(P)$(R)UVCCameraFormat"){ + field(PINI, "YES") + field(DTYP, "asynInt32") + field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))UVC_CAMERA_FORMAT") + field(ZRST, "Supported Mode 1") + field(ZRVL, "0") + field(ONST, "Supported Mode 2") + field(ONVL, "1") + field(TWST, "Supported Mode 3") + field(TWVL, "2") + field(THST, "Supported Mode 4") + field(THVL, "3") + field(FRST, "Supported Mode 5") + field(FRVL, "4") + field(FVST, "Supported Mode 6") + field(FVVL, "5") + field(SXST, "Supported Mode 7") + field(SXVL, "6") + field(VAL, "0") + info(autosaveFields, "VAL") +} + +record(mbbi, "$(P)$(R)UVCCameraFormat_RBV"){ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))UVC_CAMERA_FORMAT") + field(ZRST, "Supported Mode 1") + field(ZRVL, "0") + field(ONST, "Supported Mode 2") + field(ONVL, "1") + field(TWST, "Supported Mode 3") + field(TWVL, "2") + field(THST, "Supported Mode 4") + field(THVL, "3") + field(FRST, "Supported Mode 5") + field(FRVL, "4") + field(FVST, "Supported Mode 6") + field(FVVL, "5") + field(SXST, "Supported Mode 7") + field(SXVL, "6") + field(SCAN, "I/O Intr") +} + +############################################## +# Shows information regarding the currently selected supported format +################################################ +record(waveform, "$(P)$(R)UVCFormatDescription_RBV"){ + field(DTYP, "asynOctetRead") + field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))UVC_FORMAT_DESCRIPTION") + field(FTVL, "CHAR") + field(NELM, "256") + field(SCAN, "I/O Intr") +} + +############################################## +# Applies the supported format to the IOC +################################################ +record(ao, "$(P)$(R)UVCApplyFormat"){ + field(PINI, "YES") + field(DTYP, "asynInt32") + field(OUT, "@asyn($(PORT),$(ADDR),$(TIMEOUT))UVC_APPLY_FORMAT") + field(VAL, "0") + field(autosaveFields, "VAL") +} + +record(ai, "$(P)$(R)UVCApplyFormat_RBV"){ + field(DTYP, "asynInt32") + field(INP, "@asyn($(PORT),$(ADDR),$(TIMEOUT))UVC_APPLY_FORMAT") + field(SCAN, "I/O Intr") +} + ################################################################################################ -# Additional Camera Functions (UNDER DEVELOPMENT -> Currently not in use) +# Additional Camera Functions -> used by getter and setter functions in the driver ################################################################################################ ################################################## diff --git a/adUVCApp/Db/ADUVC_settings.req b/adUVCApp/Db/ADUVC_settings.req index e1abfb9..6d2b4c6 100755 --- a/adUVCApp/Db/ADUVC_settings.req +++ b/adUVCApp/Db/ADUVC_settings.req @@ -1,6 +1,15 @@ -$(P)$(R)UVCBrightness +file "ADBase_settings.req", P=$(P), R=$(R) +$(P)$(R)UVCComplianceLevel +$(P)$(R)UVCFramerate +$(P)$(R)UVCImageFormat +$(P)$(R)UVCGamma $(P)$(R)UVCBacklightCompensation +$(P)$(R)UVCBrightness $(P)$(R)UVCContrast $(P)$(R)UVCPowerLine $(P)$(R)UVCHue -$(P)$(R)UVCSaturation \ No newline at end of file +$(P)$(R)UVCSaturation +$(P)$(R)UVCSharpness +$(P)$(R)UVCCameraFormat +$(P)$(R)UVCFormatDescription_RBV +$(P)$(R)UVCApplyFormat \ No newline at end of file diff --git a/adUVCApp/op/ADUVC.opi b/adUVCApp/op/ADUVC.opi index cce46d5..93ba4a8 100644 --- a/adUVCApp/op/ADUVC.opi +++ b/adUVCApp/op/ADUVC.opi @@ -32,6 +32,64 @@ -11870864:166d4cb956b:-4e85 670 172 + + + false + 255 + true + false + + + + + + + false + + + + 0 + 1 + true + + + + 0.0 + + Default + + false + + + + false + 130 + true + + + + 0 + 1 + Rectangle_5 + + + + + true + true + false + + + $(pv_name) +$(pv_value) + true + true + Rectangle + 378 + -33b311ef:16b0efba116:-6925 + 718 + 342 + false @@ -56,7 +114,7 @@ 100.0 - Default + Default false @@ -102,7 +160,7 @@ $(pv_value) 1 true - Default + Default @@ -194,7 +252,7 @@ $(pv_value) 0.0 - Default + Default false @@ -252,7 +310,7 @@ $(pv_value) 100.0 - Default + Default false @@ -1097,7 +1155,7 @@ $(pv_value) 1 true - Default + Default false @@ -1151,7 +1209,7 @@ $(pv_value) 1 true - Default + Default false @@ -1317,7 +1375,7 @@ $(pv_value) 1 true - Default + Default false @@ -1878,7 +1936,7 @@ $(pv_value) 1 true - Default + Default false @@ -2198,7 +2256,7 @@ $(pv_value) 1 true - Default + Default false @@ -2334,7 +2392,7 @@ $(pv_value) 1 true - Default + Default false @@ -2479,7 +2537,7 @@ $(pv_value) 100.0 - Default + Default false @@ -2537,7 +2595,7 @@ $(pv_value) 0.0 - Default + Default false @@ -2685,7 +2743,7 @@ $(pv_value) 1 true - Default + Default false @@ -2777,7 +2835,7 @@ $(pv_value) 1 true - Default + Default false @@ -2831,7 +2889,7 @@ $(pv_value) 1 true - Default + Default false @@ -4982,7 +5040,7 @@ $(pv_value) 1 true - Default + Default @@ -4993,7 +5051,7 @@ $(pv_value) true Linking Container_1 - ../ADCore/R3-3-2/ADShutter.opi + ../ADCore/R3-5/ADShutter.opi 2 @@ -5022,7 +5080,7 @@ $(pv_value) 1 true - Default + Default @@ -5033,7 +5091,7 @@ $(pv_value) true Linking Container_2 - ../ADCore/R3-3-2/ADReadout.opi + ../ADCore/R3-5/ADReadout.opi 2 @@ -5062,7 +5120,7 @@ $(pv_value) 1 true - Default + Default @@ -5073,7 +5131,7 @@ $(pv_value) true Linking Container_3 - ../ADCore/R3-3-2/ADPlugins.opi + ../ADCore/R3-5/ADPlugins.opi 2 @@ -5102,7 +5160,7 @@ $(pv_value) 1 true - Default + Default @@ -5113,7 +5171,7 @@ $(pv_value) true Linking Container_4 - ../ADCore/R3-3-2/ADBuffers.opi + ../ADCore/R3-5/ADBuffers.opi 2 @@ -5142,7 +5200,7 @@ $(pv_value) 1 true - Default + Default @@ -5153,7 +5211,7 @@ $(pv_value) true Linking Container_5 - ../ADCore/R3-3-2/ADAttrFile.opi + ../ADCore/R3-5/ADAttrFile.opi 2 @@ -5182,7 +5240,7 @@ $(pv_value) 1 true - Default + Default @@ -5193,7 +5251,7 @@ $(pv_value) true Linking Container_6 - ../ADCore/R3-3-2/ADDriverFile.opi + ../ADCore/R3-5/ADDriverFile.opi 2 @@ -5265,7 +5323,7 @@ $(pv_value) 1 true - Default + Default false @@ -5495,4 +5553,326 @@ $(pv_value) 605 667 + + + false + + + + + + + 0 + 1 + true + + + + + + + 20 + 2 + Label_84 + + + true + true + false + + + Pre-Loaded Modes + + true + 1 + true + Label + 146 + false + -33b311ef:16b0efba116:-6963 + 722 + 355 + + + true + false + false + + + + false + + + + 6 + 1 + true + + Default + + false + + + + 20 + + Menu Button_6 + $(P)$(R)UVCCameraFormat + + + + true + true + false + + + false + $(pv_name) +$(pv_value) + false + true + Menu Button + 103 + -33b311ef:16b0efba116:-6962 + 878 + 355 + + + + false + false + false + + + + true + + + + 0 + 1 + true + + + + false + + + + 4 + 18 + 1 + Text Update_57 + 0 + true + $(P)$(R)UVCCameraFormat_RBV + + 0.0 + + + true + true + false + + + false + ###### + $(pv_name) +$(pv_value) + false + 1 + true + Text Update + 100 + false + -33b311ef:16b0efba116:-6961 + 995 + 356 + + + + + $(P)$(R)UVCApplyFormat + 1 + 10 + + + + + false + false + + + + false + + + + 0 + 1 + true + + Default + + false + + + + 20 + + Action Button_4 + 0 + $(P)$(R)UVCApplyFormat + + + + true + true + false + + + + Apply + false + $(pv_name) +$(pv_value) + true + Action Button + 98 + -33b311ef:16b0efba116:-6956 + 839 + 431 + + + + false + + + + + + + 0 + 1 + true + + + + + + + 20 + 2 + Label_85 + + + true + true + false + + + Apply Mode + + true + 1 + true + Label + 98 + false + -33b311ef:16b0efba116:-6955 + 725 + 430 + + + + false + + + + + + + 0 + 1 + true + + + + + + + 20 + 2 + Label_86 + + + true + true + false + + + Currently Loaded Mode + + true + 1 + true + Label + 179 + false + -33b311ef:16b0efba116:-694a + 723 + 378 + + + + false + false + false + + + + true + + + + 0 + 1 + true + + + + false + + + + 4 + 18 + 0 + Text Update_58 + 0 + true + $(P)$(R)UVCFormatDescription_RBV + + 0.0 + + + true + true + false + + + false + ###### + $(pv_name) +$(pv_value) + false + 1 + true + Text Update + 356 + false + -33b311ef:16b0efba116:-6949 + 727 + 402 + \ No newline at end of file diff --git a/adUVCApp/src/ADUVC.cpp b/adUVCApp/src/ADUVC.cpp index a3dec2b..e37e6aa 100755 --- a/adUVCApp/src/ADUVC.cpp +++ b/adUVCApp/src/ADUVC.cpp @@ -33,9 +33,10 @@ #include #include - +// standard namespace using namespace std; +// define driver name for logging static const char* driverName = "ADUVC"; // Constants @@ -84,14 +85,267 @@ void ADUVC::reportUVCError(uvc_error_t status, const char* functionName){ asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s UVC Error: %s\n", driverName, functionName, uvc_strerror(status)); if(status != UVC_ERROR_OTHER){ - char statusMessage[25]; - epicsSnprintf(statusMessage, sizeof(statusMessage), "UVC Error: %s\n", uvc_strerror(status)); - setStringParam(ADStatusMessage, statusMessage); - callParamCallbacks(); + char errorMessage[25]; + epicsSnprintf(errorMessage, sizeof(errorMessage), "UVC Error: %s", uvc_strerror(status)); + updateStatus(errorMessage); } } +/** + * Function that writes to ADStatus PV + * + * @params[in]: status -> message to write to ADStatus PV + */ +void ADUVC::updateStatus(const char* status){ + char statusMessage[25]; + epicsSnprintf(statusMessage, sizeof(statusMessage), "%s", status); + setStringParam(ADStatusMessage, statusMessage); + callParamCallbacks(); +} + + +//----------------------------------------------- +// ADUVC Camera Format selector functions +//----------------------------------------------- + + +/** + * External C function pulled from libuvc_internal diagnostics. + * Simply returns a string for a given subtype + * + * @return: string representing certain subtype identifier + */ +extern "C" const char* get_string_for_subtype(uint8_t subtype) { + switch (subtype) { + case UVC_VS_FORMAT_UNCOMPRESSED: + return "UncompressedFormat"; + case UVC_VS_FORMAT_MJPEG: + return "MJPEGFormat"; + default: + return "Unknown"; + } +} + + +/** + * Function that updates the description for a selected camera format + */ +void ADUVC::updateCameraFormatDesc(){ + const char* functionName = "updateCameraFormatDesc"; + int selectedFormat; + getIntegerParam(ADUVC_CameraFormat, &selectedFormat); + asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s::%s Updating Format Description\n", driverName, functionName); + char description[256]; + epicsSnprintf(description, sizeof(description), "%s", this->supportedFormats[selectedFormat].formatDesc); + setStringParam(ADUVC_FormatDescription, description); + updateStatus("Updated format Desc."); + callParamCallbacks(); +} + + +/** + * Function that handles applying a selected supported camera format to the approptiate PVs. + * Sets the data type, color mode, framerate, xsize, ysize, and frame format PVs. + */ +void ADUVC::applyCameraFormat(){ + const char* functionName = "applyCameraFormat"; + int selectedFormat; + getIntegerParam(ADUVC_CameraFormat, &selectedFormat); + asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s::%s Applying Format\n", driverName, functionName); + ADUVC_CamFormat_t format = this->supportedFormats[selectedFormat]; + if( (int) format.frameFormat < 0){ + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "%s::%s Cannot apply format - is not used\n", driverName, functionName); + } + else { + setIntegerParam(NDDataType, format.dataType); + setIntegerParam(NDColorMode, format.colorMode); + setIntegerParam(ADUVC_Framerate, format.framerate); + setIntegerParam(ADSizeX, format.xSize); + setIntegerParam(ADSizeY, format.ySize); + setIntegerParam(ADUVC_ImageFormat, format.frameFormat); + } + setIntegerParam(ADUVC_ApplyFormat, 0); + updateStatus("Applied format"); + callParamCallbacks(); +} + + +/** + * Function that reads all supported camera formats into a ADUVC_CamFormat_t struct, and then + * selects the ones that are most likely to be useful for easy selection. + * + * @return: asynSuccess if successful, asynError if error + */ +asynStatus ADUVC::readSupportedCameraFormats(){ + const char* functionName = "readSupportedCameraFormats"; + asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, + "%s::%s Reading in supported camera formats\n", driverName, functionName); + asynStatus status = asynSuccess; + ADUVC_CamFormat_t* formatBuffer = (ADUVC_CamFormat_t*) calloc(1, 256 * sizeof(ADUVC_CamFormat_t)); + int bufferIndex = 0; + if(this->pdeviceHandle != NULL){ + uvc_streaming_interface_t* interfaces = this->pdeviceHandle->info->stream_ifs; + while(interfaces != NULL){ + uvc_format_desc_t* interfaceFormats = interfaces->format_descs; + while(interfaceFormats != NULL) { + uvc_frame_desc_t* frameFormats = interfaceFormats->frame_descs; + while(frameFormats != NULL) { + populateCameraFormat(&formatBuffer[bufferIndex], interfaceFormats, frameFormats); + bufferIndex++; + frameFormats = frameFormats->next; + } + interfaceFormats = interfaceFormats->next; + } + interfaces = interfaces->next; + } + } + else{ + status = asynError; + } + int formatIndex = selectBestCameraFormats(formatBuffer, bufferIndex); + free(formatBuffer); + int i; + for(i = formatIndex; i < SUPPORTED_FORMAT_COUNT; i++){ + initEmptyCamFormat(i); + } + return status; +} + + +/** + * Function that takes a format descriptor and a frame descriptor, and populates a camera format + * structure with the appropriate values. + * + * @params[out]: camFormat -> Output camFormat struct + * @params[in]: format_desc -> Format descriptor struct + * @params[in]: frame_desc -> Frame descriptor struct + * @return: void + */ +void ADUVC:: populateCameraFormat(ADUVC_CamFormat_t* camFormat, uvc_format_desc_t* format_desc, uvc_frame_desc_t* frame_desc){ + const char* functionName = "populateCameraFormat"; + switch(format_desc->bDescriptorSubtype){ + case UVC_VS_FORMAT_MJPEG: + camFormat->frameFormat = ADUVC_FrameMJPEG; + camFormat->dataType = NDUInt8; + camFormat->colorMode = NDColorModeRGB1; + break; + case UVC_VS_FORMAT_UNCOMPRESSED: + camFormat->frameFormat = ADUVC_FrameUncompressed; + camFormat->dataType = NDUInt16; + camFormat->colorMode = NDColorModeMono; + break; + default: + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, + "%s::%s Unsupported format desc.\n", driverName, functionName); + break; + } + camFormat->xSize = frame_desc->wWidth; + camFormat->ySize = frame_desc->wHeight; + camFormat->framerate = 10000000 / frame_desc->dwDefaultFrameInterval; + camFormat->formatDesc = (char*) malloc(256); + epicsSnprintf(camFormat->formatDesc, 256, "%s, X: %d, Y: %d, Rate: %d/s", + get_string_for_subtype(format_desc->bDescriptorSubtype), (int) camFormat->xSize, (int) camFormat->ySize, camFormat->framerate); +} + + +/** + * Function that initializes a Camera Format struct with empty placeholder + * + * @params[in]: arrayIndex -> index in array of supported formats + * @return: void + */ +void ADUVC::initEmptyCamFormat(int arrayIndex){ + this->supportedFormats[arrayIndex].formatDesc = (char*) malloc(256); + epicsSnprintf(this->supportedFormats[arrayIndex].formatDesc, 256, "Unused Camera Format"); + this->supportedFormats[arrayIndex].frameFormat = ADUVC_FrameUnsupported; +} + + +/** + * Function that compares two camera format structs against one another + * + * @params[in]: cameraFormat1 -> first camera format struct + * @params[in]: cameraFormat2 -> second camera format struct + * @return: 0 if the two structs are identical, -1 if they are not + */ +int ADUVC::compareFormats(ADUVC_CamFormat_t camFormat1, ADUVC_CamFormat_t camFormat2){ + if (camFormat1.xSize != camFormat2.xSize) return -1; + if (camFormat1.ySize != camFormat2.ySize) return -1; + if (camFormat1.colorMode != camFormat2.colorMode) return -1; + if (camFormat1.dataType != camFormat2.dataType) return -1; + if (camFormat1.framerate != camFormat2.framerate) return -1; + if (camFormat1.frameFormat != camFormat2.frameFormat) return -1; + return 0; +} + + +/** + * Function that checks if a Cam format struct is already in the camera's format array + * + * @params[in]: camFormat -> format to test + * @return: true if it is in the supportedFormats array, false otherwise + */ +bool ADUVC::formatAlreadySaved(ADUVC_CamFormat_t camFormat){ + int i; + for(i = 0; i< SUPPORTED_FORMAT_COUNT; i++){ + if(compareFormats(camFormat, this->supportedFormats[i]) == 0){ + return true; + } + } + return false; +} + + +/** + * Function that selects the best discovered formats from the set of all discovered formats. + * The current order of importance is MJPEG > Uncompressed (Higher end devices only have uncompressed, + * and lower end devices exhibit poor performance when using Uncompressed), then image resolution, + * then framerate. + * + * @params[in]: formatBuffer -> Buffer containing all detected camera formats + * @params[in]: numFormats -> counter for how many formats were detected + * @return: readFormats -> The number of formats read from the formatBuffer + */ +int ADUVC::selectBestCameraFormats(ADUVC_CamFormat_t* formatBuffer, int numFormats){ + const char* functionName = "selectBestCameraFormats"; + asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, + "%s::%s Selecting best camera formats\n", driverName, functionName); + + int readFormats = 0; + while(readFormats < SUPPORTED_FORMAT_COUNT && readFormats != numFormats){ + int bestFormatIndex = 0; + int i; + for(i = 0; i< numFormats; i++){ + if(!formatAlreadySaved(formatBuffer[i])){ + if(formatBuffer[i].frameFormat == ADUVC_FrameMJPEG && formatBuffer[bestFormatIndex].frameFormat != ADUVC_FrameMJPEG){ + bestFormatIndex = i; + } + else if(formatBuffer[i].xSize > formatBuffer[bestFormatIndex].xSize){ + bestFormatIndex = i; + } + else if(formatBuffer[i].framerate > formatBuffer[bestFormatIndex].framerate){ + bestFormatIndex = i; + } + } + } + this->supportedFormats[readFormats].colorMode = formatBuffer[bestFormatIndex].colorMode; + this->supportedFormats[readFormats].dataType = formatBuffer[bestFormatIndex].dataType; + this->supportedFormats[readFormats].frameFormat = formatBuffer[bestFormatIndex].frameFormat; + this->supportedFormats[readFormats].framerate = formatBuffer[bestFormatIndex].framerate; + this->supportedFormats[readFormats].xSize = formatBuffer[bestFormatIndex].xSize; + this->supportedFormats[readFormats].ySize = formatBuffer[bestFormatIndex].ySize; + this->supportedFormats[readFormats].formatDesc = (char*) malloc(256); + memcpy(this->supportedFormats[readFormats].formatDesc, formatBuffer[bestFormatIndex].formatDesc, 256); + readFormats++; + } + for(int j = 0; j < numFormats; j++){ + free(formatBuffer[j].formatDesc); + } + return readFormats; +} + //----------------------------------------------- // ADUVC connection/disconnection functions @@ -117,14 +371,17 @@ asynStatus ADUVC::connectToDeviceUVC(int connectionType, const char* serialNumbe return asynError; } else{ - asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s Initialized UVC context\n", driverName, functionName); + asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, + "%s::%s Initialized UVC context\n", driverName, functionName); } if(connectionType == 0){ - asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s Searching for UVC device with serial number: %s\n", driverName, functionName, serialNumber); + asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, + "%s::%s Searching for UVC device with serial number: %s\n", driverName, functionName, serialNumber); deviceStatus = uvc_find_device(pdeviceContext, &pdevice, 0, 0, serialNumber); } else{ - asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s Searching for UVC device with Product ID: %d\n", driverName, functionName, productID); + asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, + "%s::%s Searching for UVC device with Product ID: %d\n", driverName, functionName, productID); deviceStatus = uvc_find_device(pdeviceContext, &pdevice, 0, productID, NULL); } if(deviceStatus<0){ @@ -132,7 +389,8 @@ asynStatus ADUVC::connectToDeviceUVC(int connectionType, const char* serialNumbe return asynError; } else{ - asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s Found UVC device\n", driverName, functionName); + asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, + "%s::%s Found UVC device\n", driverName, functionName); } deviceStatus = uvc_open(pdevice, &pdeviceHandle); if(deviceStatus<0){ @@ -141,7 +399,8 @@ asynStatus ADUVC::connectToDeviceUVC(int connectionType, const char* serialNumbe } else{ connected = 1; - asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s Opened UVC device\n", driverName, functionName); + asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, + "%s::%s Opened UVC device\n", driverName, functionName); } return status; } @@ -155,7 +414,13 @@ asynStatus ADUVC::connectToDeviceUVC(int connectionType, const char* serialNumbe */ asynStatus ADUVC::disconnectFromDeviceUVC(){ const char* functionName = "disconnectFromDeviceUVC"; - asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s::%s Calling all free functions for ADUVC\n", driverName, functionName); + asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, + "%s::%s Calling all free functions for ADUVC\n", driverName, functionName); + int i; + for(i = 0; i< SUPPORTED_FORMAT_COUNT; i++){ + // free mem in supported + free(this->supportedFormats[i].formatDesc); + } if(connected == 1){ uvc_close(pdeviceHandle); uvc_unref_device(pdevice); @@ -322,6 +587,7 @@ uvc_error_t ADUVC::acquireStart(uvc_frame_format imageFormat){ callParamCallbacks(); } } + updateStatus("Started acquisition"); return deviceStatus; } @@ -343,7 +609,8 @@ void ADUVC::acquireStop(){ setIntegerParam(ADStatus, ADStatusIdle); setIntegerParam(ADAcquire, 0); callParamCallbacks(); - asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s Stopping aquisition\n",driverName, functionName); + updateStatus("Stopped acquisition"); + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s Stopping aquisition\n", driverName, functionName); } @@ -384,11 +651,12 @@ void ADUVC::newFrameCallbackWrapper(uvc_frame_t* frame, void* ptr){ */ asynStatus ADUVC::uvc2NDArray(uvc_frame_t* frame, NDArray* pArray, NDDataType_t dataType, NDColorMode_t colorMode, size_t imBytes){ static const char* functionName = "uvc2NDArray"; + asynStatus status = asynSuccess; // if data is grayscale, we do not need to convert it, we just copy over the data. if(colorMode == NDColorModeMono){ if(frame->data_bytes != imBytes){ asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s Error invalid frame size. Frame has %d bytes and array has %d bytes\n", driverName, functionName, (int) frame->data_bytes, (int) imBytes); - return asynError; + status = asynError; } else{ if(dataType == NDUInt8 || dataType == NDInt8) memcpy((unsigned char*) pArray->pData,(unsigned char*) frame->data, imBytes); @@ -401,60 +669,67 @@ asynStatus ADUVC::uvc2NDArray(uvc_frame_t* frame, NDArray* pArray, NDDataType_t uvc_frame_t* rgb = uvc_allocate_frame(frame->width * frame->height *3); if(!rgb){ asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s ERROR: Unable to allocate frame\n", driverName, functionName); - return asynError; - } - // convert any uvc frame format to rgb (to simplify the converson to NDArray, since area detector supports the rgb format) - uvc_frame_format frameFormat = frame->frame_format; - switch(frameFormat){ - case UVC_FRAME_FORMAT_YUYV: - deviceStatus = uvc_any2rgb(frame, rgb); - break; - case UVC_FRAME_FORMAT_UYVY: - deviceStatus = uvc_any2rgb(frame, rgb); - break; - case UVC_FRAME_FORMAT_MJPEG: - deviceStatus = uvc_mjpeg2rgb(frame, rgb); - break; - case UVC_FRAME_FORMAT_RGB: - deviceStatus = uvc_any2rgb(frame, rgb); - break; - default: - asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s ERROR: Unsupported UVC format\n", driverName, functionName); - uvc_free_frame(rgb); - return asynError; - } - if(deviceStatus<0){ - reportUVCError(deviceStatus, functionName); - uvc_free_frame(rgb); - return asynError; + status = asynError; } else{ - if(rgb->data_bytes != imBytes){ - asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s Error invalid frame sizeFrame has %d bytes and array has %d bytes\n", driverName, functionName, (int) rgb->data_bytes, (int) imBytes); - uvc_free_frame(rgb); - return asynError; + // convert any uvc frame format to rgb (to simplify the converson to NDArray, since area detector supports the rgb format) + uvc_frame_format frameFormat = frame->frame_format; + switch(frameFormat){ + case UVC_FRAME_FORMAT_YUYV: + deviceStatus = uvc_any2rgb(frame, rgb); + break; + case UVC_FRAME_FORMAT_UYVY: + deviceStatus = uvc_any2rgb(frame, rgb); + break; + case UVC_FRAME_FORMAT_MJPEG: + deviceStatus = uvc_mjpeg2rgb(frame, rgb); + break; + case UVC_FRAME_FORMAT_RGB: + deviceStatus = uvc_any2rgb(frame, rgb); + break; + default: + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s ERROR: Unsupported UVC format\n", driverName, functionName); + uvc_free_frame(rgb); + status = asynError; } - else{ - memcpy(pArray->pData, rgb->data, imBytes); + if(status != asynError){ + if(deviceStatus<0){ + reportUVCError(deviceStatus, functionName); + status = asynError; + } + else{ + if(rgb->data_bytes != imBytes){ + asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s Error invalid frame sizeFrame has %d bytes and array has %d bytes\n", driverName, functionName, (int) rgb->data_bytes, (int) imBytes); + status = asynError; + } + else{ + memcpy(pArray->pData, rgb->data, imBytes); + } + } + uvc_free_frame(rgb); } } - uvc_free_frame(rgb); } - pArray->pAttributeList->add("DataType", "Data Type", NDAttrInt32, &dataType); - pArray->pAttributeList->add("ColorMode", "Color Mode", NDAttrInt32, &colorMode); - //increment the array counter - int arrayCounter; - getIntegerParam(NDArrayCounter, &arrayCounter); - arrayCounter++; - setIntegerParam(NDArrayCounter, arrayCounter); - //refresh PVs - callParamCallbacks(); - //Sends image to the ArrayDataPV - getAttributes(pArray->pAttributeList); - doCallbacksGenericPointer(pArray, NDArrayData, 0); - //frees up memory + // only push image if the data transfer was successful + if(status == asynSuccess){ + pArray->pAttributeList->add("DataType", "Data Type", NDAttrInt32, &dataType); + pArray->pAttributeList->add("ColorMode", "Color Mode", NDAttrInt32, &colorMode); + //increment the array counter + int arrayCounter; + getIntegerParam(NDArrayCounter, &arrayCounter); + arrayCounter++; + setIntegerParam(NDArrayCounter, arrayCounter); + //refresh PVs + callParamCallbacks(); + //Sends image to the ArrayDataPV + getAttributes(pArray->pAttributeList); + doCallbacksGenericPointer(pArray, NDArrayData, 0); + //frees up memory + } + + // Always free array whether successful or not pArray->release(); - return asynSuccess; + return status; } @@ -479,7 +754,7 @@ void ADUVC::newFrameCallback(uvc_frame_t* frame, void* ptr){ //TODO: Timestamps for images //epicsTimeStamp currentTime; static const char* functionName = "newFrameCallback"; - + getIntegerParam(NDColorMode, &colorMode); getIntegerParam(NDDataType, &dataType); @@ -525,8 +800,8 @@ void ADUVC::newFrameCallback(uvc_frame_t* frame, void* ptr){ setIntegerParam(ADNumImagesCounter, numImages); uvc2NDArray(frame, pArray, (NDDataType_t) dataType, (NDColorMode_t) colorMode, arrayInfo.totalBytes); acquireStop(); - } + // block shot mode stops once numImages reaches the number of desired images else if(operatingMode == ADImageMultiple){ int numImages; @@ -536,11 +811,13 @@ void ADUVC::newFrameCallback(uvc_frame_t* frame, void* ptr){ setIntegerParam(ADNumImagesCounter, numImages); uvc2NDArray(frame, pArray, (NDDataType_t) dataType, (NDColorMode_t) colorMode, arrayInfo.totalBytes); getIntegerParam(ADNumImages, &desiredImages); - if(numImages>=desiredImages){ + + if(numImages>=desiredImages){ acquireStop(); return; } } + //continuous mode runs until user stops acquisition else if(operatingMode == ADImageContinuous){ int numImages; @@ -549,6 +826,7 @@ void ADUVC::newFrameCallback(uvc_frame_t* frame, void* ptr){ setIntegerParam(ADNumImagesCounter, numImages); uvc2NDArray(frame, pArray, (NDDataType_t) dataType, (NDColorMode_t) colorMode, arrayInfo.totalBytes); } + else{ asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s ERROR: Unsupported operating mode\n", driverName, functionName); acquireStop(); @@ -572,6 +850,7 @@ void ADUVC::newFrameCallback(uvc_frame_t* frame, void* ptr){ asynStatus ADUVC::setExposure(int exposureTime){ const char* functionName = "setExposure"; asynStatus status = asynSuccess; + updateStatus("Set Exposure"); deviceStatus = uvc_set_exposure_abs(pdeviceHandle, exposureTime); if(deviceStatus < 0){ reportUVCError(deviceStatus, functionName); @@ -590,7 +869,7 @@ asynStatus ADUVC::setExposure(int exposureTime){ asynStatus ADUVC::setGamma(int gamma){ const char* functionName = "setGamma"; asynStatus status = asynSuccess; - + updateStatus("Set Gamma"); deviceStatus = uvc_set_gamma(pdeviceHandle, gamma); if(deviceStatus < 0){ reportUVCError(deviceStatus, functionName); @@ -609,6 +888,7 @@ asynStatus ADUVC::setGamma(int gamma){ asynStatus ADUVC::setBacklightCompensation(int backlightCompensation){ const char* functionName = "setBacklightCompensation"; asynStatus status = asynSuccess; + updateStatus("Set Backlight Comp."); deviceStatus = uvc_set_backlight_compensation(pdeviceHandle, backlightCompensation); if(deviceStatus < 0){ reportUVCError(deviceStatus, functionName); @@ -627,6 +907,7 @@ asynStatus ADUVC::setBacklightCompensation(int backlightCompensation){ asynStatus ADUVC::setBrightness(int brightness){ const char* functionName = "setBrightness"; asynStatus status = asynSuccess; + updateStatus("Set Brightness"); deviceStatus = uvc_set_brightness(pdeviceHandle, brightness); if(deviceStatus < 0){ reportUVCError(deviceStatus, functionName); @@ -645,6 +926,7 @@ asynStatus ADUVC::setBrightness(int brightness){ asynStatus ADUVC::setContrast(int contrast){ const char* functionName = "setContrast"; asynStatus status = asynSuccess; + updateStatus("Set Contrast"); deviceStatus = uvc_set_contrast(pdeviceHandle, contrast); if(deviceStatus < 0){ reportUVCError(deviceStatus, functionName); @@ -663,6 +945,7 @@ asynStatus ADUVC::setContrast(int contrast){ asynStatus ADUVC::setGain(int gain){ const char* functionName = "setGain"; asynStatus status = asynSuccess; + updateStatus("Set Gain"); deviceStatus = uvc_set_gain(pdeviceHandle, gain); if(deviceStatus < 0){ reportUVCError(deviceStatus, functionName); @@ -681,6 +964,7 @@ asynStatus ADUVC::setGain(int gain){ asynStatus ADUVC::setPowerLineFrequency(int powerLineFrequency){ const char* functionName = "setPowerLineFrequency"; asynStatus status = asynSuccess; + updateStatus("Set Power Line Freq."); deviceStatus = uvc_set_power_line_frequency(pdeviceHandle, powerLineFrequency); if(deviceStatus < 0){ reportUVCError(deviceStatus, functionName); @@ -700,6 +984,7 @@ asynStatus ADUVC::setPowerLineFrequency(int powerLineFrequency){ asynStatus ADUVC::setHue(int hue){ const char* functionName = "setHue"; asynStatus status = asynSuccess; + updateStatus("Set Hue"); deviceStatus = uvc_set_hue(pdeviceHandle, hue); if(deviceStatus < 0){ reportUVCError(deviceStatus, functionName); @@ -718,6 +1003,7 @@ asynStatus ADUVC::setHue(int hue){ asynStatus ADUVC::setSaturation(int saturation){ const char* functionName = "setSaturation"; asynStatus status = asynSuccess; + updateStatus("Set Saturation"); deviceStatus = uvc_set_saturation(pdeviceHandle, saturation); if(deviceStatus < 0){ reportUVCError(deviceStatus, functionName); @@ -736,6 +1022,7 @@ asynStatus ADUVC::setSaturation(int saturation){ asynStatus ADUVC::setSharpness(int sharpness){ const char* functionName = "setSharpness"; asynStatus status = asynSuccess; + updateStatus("Set Sharpness"); deviceStatus = uvc_set_sharpness(pdeviceHandle, sharpness); if(deviceStatus < 0){ reportUVCError(deviceStatus, functionName); @@ -745,7 +1032,6 @@ asynStatus ADUVC::setSharpness(int sharpness){ } - //------------------------------------------------------------------------- // ADDriver function overwrites //------------------------------------------------------------------------- @@ -780,6 +1066,12 @@ asynStatus ADUVC::writeInt32(asynUser* pasynUser, epicsInt32 value){ acquireStop(); } } + else if(function == ADUVC_ApplyFormat && value == 1){ + if(acquiring) + acquireStop(); + applyCameraFormat(); + } + else if(function == ADUVC_CameraFormat) updateCameraFormatDesc(); //switch image mode else if(function == ADImageMode){ if(acquiring == 1) acquireStop(); @@ -927,9 +1219,10 @@ ADUVC::ADUVC(const char* portName, const char* serial, int productID, int framer createParam(ADUVC_UVCComplianceLevelString, asynParamInt32, &ADUVC_UVCComplianceLevel); createParam(ADUVC_ReferenceCountString, asynParamInt32, &ADUVC_ReferenceCount); createParam(ADUVC_FramerateString, asynParamInt32, &ADUVC_Framerate); - createParam(ADUVC_VendorIDString, asynParamInt32, &ADUVC_VendorID); - createParam(ADUVC_ProductIDString, asynParamInt32, &ADUVC_ProductID); createParam(ADUVC_ImageFormatString, asynParamInt32, &ADUVC_ImageFormat); + createParam(ADUVC_CameraFormatString, asynParamInt32, &ADUVC_CameraFormat); + createParam(ADUVC_FormatDescriptionString, asynParamOctet, &ADUVC_FormatDescription); + createParam(ADUVC_ApplyFormatString, asynParamInt32, &ADUVC_ApplyFormat); createParam(ADUVC_BrightnessString, asynParamInt32, &ADUVC_Brightness); createParam(ADUVC_ContrastString, asynParamInt32, &ADUVC_Contrast); createParam(ADUVC_PowerLineString, asynParamInt32, &ADUVC_PowerLine); @@ -948,7 +1241,9 @@ ADUVC::ADUVC(const char* portName, const char* serial, int productID, int framer if(strcmp(serial, "") == 0) setStringParam(ADSerialNumber, "No Serial Detected"); else setStringParam(ADSerialNumber, serial); - setIntegerParam(ADUVC_ProductID, productID); + char pIDBuff[32]; + epicsSnprintf(pIDBuff, 32, "%d", productID); + setStringParam(ADSerialNumber, pIDBuff); //sets libuvc version char uvcVersionString[25]; @@ -975,8 +1270,15 @@ ADUVC::ADUVC(const char* portName, const char* serial, int productID, int framer asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s Connection failed, abort\n", driverName, functionName); } else{ - asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s Acquiring device information\n", driverName, functionName); - getDeviceInformation(); + asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s::%s Acquiring device information\n", driverName, functionName); + readSupportedCameraFormats(); + /* + int i; + for(i = 0; i< SUPPORTED_FORMAT_COUNT; i++){ + printf("%s\n", this->supportedFormats[i].formatDesc); + } + */ + getDeviceInformation(); } // when epics is exited, delete the instance of this class diff --git a/adUVCApp/src/ADUVC.h b/adUVCApp/src/ADUVC.h index 3d74f82..31bd66d 100755 --- a/adUVCApp/src/ADUVC.h +++ b/adUVCApp/src/ADUVC.h @@ -16,34 +16,40 @@ // version numbers #define ADUVC_VERSION 1 -#define ADUVC_REVISION 1 +#define ADUVC_REVISION 2 #define ADUVC_MODIFICATION 0 + +#define SUPPORTED_FORMAT_COUNT 7 + + // includes #include +#include #include "ADDriver.h" -// PV definitions - -#define ADUVC_UVCComplianceLevelString "UVC_COMPLIANCE" //asynInt32 -#define ADUVC_ReferenceCountString "UVC_REFCOUNT" //asynInt32 -#define ADUVC_FramerateString "UVC_FRAMERATE" //asynInt32 -#define ADUVC_VendorIDString "UVC_VENDOR" //asynInt32 -#define ADUVC_ProductIDString "UVC_PRODUCT" //asynInt32 -#define ADUVC_ImageFormatString "UVC_FORMAT" //asynInt32 -#define ADUVC_GammaString "UVC_GAMMA" //asynInt32 -#define ADUVC_BacklightCompensationString "UVC_BACKLIGHT" //asynInt32 -#define ADUVC_BrightnessString "UVC_BRIGHTNESS" //asynInt32 -#define ADUVC_ContrastString "UVC_CONTRAST" //asynInt32 -#define ADUVC_PowerLineString "UVC_POWER" //asynInt32 -#define ADUVC_HueString "UVC_HUE" //asynInt32 -#define ADUVC_SaturationString "UVC_SATURATION" //asynInt32 -#define ADUVC_SharpnessString "UVC_SHARPNESS" //asynInt32 +// PV String definitions +#define ADUVC_UVCComplianceLevelString "UVC_COMPLIANCE" //asynInt32 +#define ADUVC_ReferenceCountString "UVC_REFCOUNT" //asynInt32 +#define ADUVC_FramerateString "UVC_FRAMERATE" //asynInt32 +#define ADUVC_ImageFormatString "UVC_FORMAT" //asynInt32 +#define ADUVC_CameraFormatString "UVC_CAMERA_FORMAT" //asynInt32 +#define ADUVC_FormatDescriptionString "UVC_FORMAT_DESCRIPTION" //asynOctet +#define ADUVC_ApplyFormatString "UVC_APPLY_FORMAT" //asynInt32 +#define ADUVC_GammaString "UVC_GAMMA" //asynInt32 +#define ADUVC_BacklightCompensationString "UVC_BACKLIGHT" //asynInt32 +#define ADUVC_BrightnessString "UVC_BRIGHTNESS" //asynInt32 +#define ADUVC_ContrastString "UVC_CONTRAST" //asynInt32 +#define ADUVC_PowerLineString "UVC_POWER" //asynInt32 +#define ADUVC_HueString "UVC_HUE" //asynInt32 +#define ADUVC_SaturationString "UVC_SATURATION" //asynInt32 +#define ADUVC_SharpnessString "UVC_SHARPNESS" //asynInt32 /* enum for getting format from PV */ typedef enum { + ADUVC_FrameUnsupported = -1, ADUVC_FrameMJPEG = 0, ADUVC_FrameRGB = 1, ADUVC_FrameYUYV = 2, @@ -54,6 +60,18 @@ typedef enum { } ADUVC_FrameFormat_t; +/* Struct for individual supported camera format - Used to auto read modes into dropdown for easier operation */ +typedef struct ADUVC_CamFormat{ + char* formatDesc; + size_t xSize; + size_t ySize; + int framerate; + ADUVC_FrameFormat_t frameFormat; + NDColorMode_t colorMode; + NDDataType_t dataType; +} ADUVC_CamFormat_t; + + /* * Class definition of the ADUVC driver. It inherits from the base ADDriver class @@ -68,7 +86,6 @@ class ADUVC : ADDriver{ // Constructor ADUVC(const char* portName, const char* serial, int productID, int framerate, int xsize, int ysize, int maxBuffers, size_t maxMemory, int priority, int stackSize); - //TODO: add overrides of ADDriver functions // ADDriver overrides virtual asynStatus writeInt32(asynUser* pasynUser, epicsInt32 value); @@ -87,9 +104,10 @@ class ADUVC : ADDriver{ #define ADUVC_FIRST_PARAM ADUVC_UVCComplianceLevel int ADUVC_ReferenceCount; int ADUVC_Framerate; - int ADUVC_VendorID; - int ADUVC_ProductID; int ADUVC_ImageFormat; + int ADUVC_CameraFormat; + int ADUVC_FormatDescription; + int ADUVC_ApplyFormat; int ADUVC_Gamma; int ADUVC_BacklightCompensation; int ADUVC_Brightness; @@ -98,7 +116,7 @@ class ADUVC : ADDriver{ int ADUVC_Hue; int ADUVC_Saturation; int ADUVC_Sharpness; - #define ADUVC_LAST_PARAM ADUVC_Sharpness + #define ADUVC_LAST_PARAM ADUVC_Sharpness private: @@ -111,24 +129,27 @@ class ADUVC : ADDriver{ // UVC Variables //----------------------------------------- - // checks uvc device operations status + // checks uvc device operations status uvc_error_t deviceStatus; - //pointer to device + //pointer to device uvc_device_t* pdevice; - //pointer to device context. generated when connecting + //pointer to device context. generated when connecting uvc_context_t* pdeviceContext; - //pointer to device handle. used for controlling device. Each UVC device can allow for one handle at a time + //pointer to device handle. used for controlling device. Each UVC device can allow for one handle at a time uvc_device_handle_t* pdeviceHandle; - //pointer to device stream controller. used to controll streaming from device + //pointer to device stream controller. used to controll streaming from device uvc_stream_ctrl_t deviceStreamCtrl; - //pointer containing device info, such as vendor, product id + //pointer containing device info, such as vendor, product id uvc_device_descriptor_t* pdeviceInfo; + //array of supported formats that will allow for easy switching of operating modes. + ADUVC_CamFormat_t supportedFormats[SUPPORTED_FORMAT_COUNT]; + //flag that stores if driver is connected to device int connected = 0; @@ -141,22 +162,43 @@ class ADUVC : ADDriver{ // UVC Functions - Logging/Reporting //----------------------------------------- - //function used to report errors in uvc operations + //function used to report errors in uvc operations void reportUVCError(uvc_error_t status, const char* functionName); // reports device and driver info into a log file void report(FILE* fp, int details); + // writes to ADStatus PV + void updateStatus(const char* status); + // ---------------------------------------- // UVC Functions - Connecting to camera //----------------------------------------- - //function used for connecting to a UVC device + //function used for connecting to a UVC device and reading supported camera modes. asynStatus connectToDeviceUVC(int connectionType, const char* serialNumber, int productID); //function used to disconnect from UVC device asynStatus disconnectFromDeviceUVC(); + // ---------------------------------------- + // UVC Functions - Camera Format Detection + //----------------------------------------- + + // functions for reading camera formats, and linking them to the appropriate PVs + asynStatus readSupportedCameraFormats(); + void populateCameraFormat(ADUVC_CamFormat_t* camFormat, uvc_format_desc_t* format_desc, uvc_frame_desc_t* frame_desc); + int selectBestCameraFormats(ADUVC_CamFormat_t* formatBuffer, int numberInterfaces); + + // helper functions + void initEmptyCamFormat(int arrayIndex); + bool formatAlreadySaved(ADUVC_CamFormat_t camFormat); + int compareFormats(ADUVC_CamFormat_t camFormat1, ADUVC_CamFormat_t camFormat2); + + // Functions for applying saved camera mode to PVs + void updateCameraFormatDesc(); + void applyCameraFormat(); + // ---------------------------------------- // UVC Functions - Camera functions //----------------------------------------- @@ -173,22 +215,22 @@ class ADUVC : ADDriver{ asynStatus setSaturation(int saturation); asynStatus setSharpness(int sharpness); - //function that begins image aquisition + //function that begins image aquisition uvc_error_t acquireStart(uvc_frame_format format); - //function that stops aquisition + //function that stops aquisition void acquireStop(); - //function that converts a UVC frame into an NDArray + //function that converts a UVC frame into an NDArray asynStatus uvc2NDArray(uvc_frame_t* frame, NDArray* pArray, NDDataType_t dataType, NDColorMode_t colorMode, size_t imBytes); - //function that gets information from a UVC device + //function that gets information from a UVC device void getDeviceImageInformation(); void getDeviceInformation(); uvc_frame_format getFormatFromPV(); - // static wrapper function for callback. Necessary becuase callback in UVC must be static but we want the driver running the callback + //static wrapper function for callback. Necessary becuase callback in UVC must be static but we want the driver running the callback static void newFrameCallbackWrapper(uvc_frame_t* frame, void* ptr); }; diff --git a/adUVCSupport/include/libuvc/libuvc_internal.h b/adUVCSupport/include/libuvc/libuvc_internal.h index 829b294..88ca418 100755 --- a/adUVCSupport/include/libuvc/libuvc_internal.h +++ b/adUVCSupport/include/libuvc/libuvc_internal.h @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include "utlist.h" /** Converts an unaligned four-byte little-endian integer into an int32 */ diff --git a/docs/assets/ADUVCBase.png b/docs/assets/ADUVCBase.png index c44b084..88d7c2e 100644 Binary files a/docs/assets/ADUVCBase.png and b/docs/assets/ADUVCBase.png differ diff --git a/docs/index.html b/docs/index.html index 43609d2..38c5d68 100644 --- a/docs/index.html +++ b/docs/index.html @@ -49,8 +49,10 @@

Usage:

Once the driver is installed, you may start the ioc by entering the IOC folder and running 'sudo ./startEPICS.sh'. Sudo access is important because access to UVC devices is limited to users with root permissions. You may then integrate ADUVC into your area detector screen setup. Below is an image of the ADUVC base control screen. It extends the standard ADBase screen with UVC controls for various camera functionality, - such as brightness, exposure, backlight compenstation, saturation, etc. Currently the driver only supports 8-bit RGB images (most UVC cameras - only allow for this anyway), but support for more formats is planned in R1-1. + such as brightness, exposure, backlight compenstation, saturation, etc. The driver supports a variety of UVC formats, though the two that are used + the most often are MJPEG and Uncompressed. Because acquisition modes are locked down in most UVC cameras, as of R1-2 there has been added a section + to the screen that allows for selecting from up to the 7 most useful acquisition modes supported by the camera. If the camera has fewer than 7 modes, + the remaining ones will show as Unused.

ADUVC base screen

The ADUVC Base screen. Based on the ADBase screen with added controls specific to ADUVC.

@@ -58,6 +60,32 @@

Usage:

Release Notes:

+ +

Release Notes

+

R1-2 (11-June-2019)

+
    +
  • +

    Key detector features implemented:

    +
      +
    • Camera modes now read into structs at startup
    • +
    • Valid camera mode structs selectable from dropdown in CSS - improves usability
    • +
    • Autosave functionality tested and working.
    • +
    • More extensive status messages/detector feedback
    • +
    +
  • +
  • +

    Key fixes and improvements

    +
      +
    • Removed unused PVs (ADUVC_VendorID, ADUVC_ProductID)
    • +
    • Fixed memory leak caused by early return from frame conversion function on error
    • +
    • Added Makefiles to the support modules
    • +
    • Documentation updates
    • +
    • Added information on using camera with Virtual Box
    • +
    • Removed newlines from status messages for better readability.
    • +
    • CSS screen updated
    • +
    +
  • +

R1-1 (28-January-2019)

  • @@ -133,14 +161,13 @@

    R0-1 (Beta) (5-November-2018)

  • No custom screens (uses ADBase screen)
- - - + +

Issues and pull requests:

- To submit an issue or a pull request for ADUVC, please do so at the source fork on Github. + To submit an issue or a pull request for ADUVC, please do so at the source fork on Github.

Important links:

    diff --git a/iocs/adUVCIOC/iocBoot/iocADUVC/ADUVC_settings.req b/iocs/adUVCIOC/iocBoot/iocADUVC/ADUVC_settings.req deleted file mode 100755 index e69de29..0000000 diff --git a/iocs/adUVCIOC/iocBoot/iocADUVC/auto_settings.req b/iocs/adUVCIOC/iocBoot/iocADUVC/auto_settings.req index 4ce897a..d001d6b 100755 --- a/iocs/adUVCIOC/iocBoot/iocADUVC/auto_settings.req +++ b/iocs/adUVCIOC/iocBoot/iocADUVC/auto_settings.req @@ -1,9 +1,3 @@ -file "ADUVC_settings.req", P=$(P), R=cam1: -file "ADBase_settings.req", P=$(P), R=cam1: -file "NDPluginBase_settings.req", P=$(P), R=image1: -file "NDStdArrays_settings.req", P=$(P), R=image1: -file "commonPlugin_settings.req", P=$(P) -file "NDPluginFile_settings.req", P=$(P), R=PW1: -file "NDPluginPipeWriter_settings.req", P=$(P), R=PW1: -file "NDPluginFile_settings.req", P=$(P), R=IMM1: -file "NDFileIMM_settings.req", P=$(P), R=IMM1: +file "ADUVC_settings.req", P=$(P), R=cam1: +file "NDStdArrays_settings.req", P=$(P), R=image1: +file "commonPlugin_settings.req", P=$(P) diff --git a/iocs/adUVCIOC/iocBoot/iocADUVC/st.cmd b/iocs/adUVCIOC/iocBoot/iocADUVC/st.cmd index 165009d..bb95185 100755 --- a/iocs/adUVCIOC/iocBoot/iocADUVC/st.cmd +++ b/iocs/adUVCIOC/iocBoot/iocADUVC/st.cmd @@ -44,39 +44,30 @@ epicsEnvSet("EPICS_CA_MAX_ARRAY_BYTES", 20000000) # */ # If searching for device by serial number, put 0 and 0 for vendor/productID -# ADUVCConfig(const char* portName, const char* serial, int vendorID, int productID, int framerate, int xsize, int ysize, int maxBuffers, size_t maxMemory, int priority, int stackSize) -#ADUVCConfig("$(PORT)", "10e536e9e4c4ee70", 0, "$(FRAMERATE)", "$(XSIZE)", "$(YSIZE)", 0, 0, 0, 0) +# ADUVCConfig(const char* portName, const char* serial, int productID, int framerate, int xsize, int ysize, int maxBuffers, size_t maxMemory, int priority, int stackSize) +#ADUVCConfig("$(PORT)", "10e536e9e4c4ee70", 0, $(FRAMERATE), $(XSIZE), $(YSIZE), 0, 0, 0, 0) #epicsThreadSleep(2) # If searching for device by product ID put "" or empty string for serial number ADUVCConfig("$(PORT)", "", 25344, $(FRAMERATE), $(XSIZE), $(YSIZE), 0, 0, 0, 0) -#ADUVCConfig("$(PORT)", "", 49490, $(FRAMERATE), $(XSIZE), $(YSIZE), 0, 0, 0, 0) epicsThreadSleep(2) asynSetTraceIOMask($(PORT), 0, 2) -#asynSetTraceMask($(PORT),0,0xff) +#asynSetTraceMask($(PORT), 0, 0xff) dbLoadRecords("$(ADCORE)/db/ADBase.template", "P=$(PREFIX),R=cam1:,PORT=$(PORT),ADDR=0,TIMEOUT=1") dbLoadRecords("$(ADUVC)/db/ADUVC.template","P=$(PREFIX),R=cam1:,PORT=$(PORT),ADDR=0,TIMEOUT=1") + # # Create a standard arrays plugin, set it to get data from Driver. -#int NDStdArraysConfigure(const char *portName, int queueSize, int blockingCallbacks, const char *NDArrayPort, int NDArrayAddr, int maxBuffers, size_t maxMemory, +# int NDStdArraysConfigure(const char *portName, int queueSize, int blockingCallbacks, const char *NDArrayPort, int NDArrayAddr, int maxBuffers, size_t maxMemory, # int priority, int stackSize, int maxThreads) NDStdArraysConfigure("Image1", 3, 0, "$(PORT)", 0) -#dbLoadRecords("$(ADCORE)/db/NDPluginBase.template","P=$(PREFIX),R=image1:,PORT=Image1,ADDR=0,TIMEOUT=1,NDARRAY_PORT=$(PORT),NDARRAY_ADDR=0") -#dbLoadRecords("$(ADCORE)/db/NDStdArrays.template", "P=$(PREFIX),R=image1:,PORT=Image1,ADDR=0,TIMEOUT=1,TYPE=Int16,SIZE=16,FTVL=SHORT,NELEMENTS=802896") dbLoadRecords("$(ADCORE)/db/NDStdArrays.template", "P=$(PREFIX),R=image1:,PORT=Image1,ADDR=0,NDARRAY_PORT=$(PORT),TIMEOUT=1,TYPE=Int16,FTVL=SHORT,NELEMENTS=6000000") + # # Load all other plugins using commonPlugins.cmd < $(ADCORE)/iocBoot/commonPlugins.cmd -# -#Note mpi control pipe out & in reversed. Names are from the view of the MPI program. -#NDPipeWriterConfigure("PipeWriter1", 15000, 0, "$(PORT)", "/local/xpcscmdout", "/local/xpcscmdin", 0, 0, 0, 0,0) -#dbLoadRecords("$(ADCORE)/db/NDPluginPipeWriter.template", "P=$(PREFIX),R=PW1:, PORT=PipeWriter1,ADDR=0,TIMEOUT=1,NDARRAY_PORT=$(PORT),CMD_IN_PORT=PW_CMD_IN,CMD_OUT_PORT=PW_CMD_OUT") - -#Note Local plugin to run the IMM plugin writer -#NDFileIMMConfigure("IMM1", 15000, 0, "$(PORT)", 0, 0, 0) -#dbLoadRecords("$(ADCORE)/db/NDFileIMM.template", "P=$(PREFIX),R=IMM1:,PORT=IMM1,ADDR=0,TIMEOUT=1,NDARRAY_PORT=$(PORT)") set_requestfile_path("$(ADUVC)/adUVCApp/Db")