Hi,
I got a chance to test the interactions of connect/disconnect between two
Prosilica GigE cameras within one Linux IOC. For your consideration, the attached patches
are based on areaDetectorR1-9-1, and asyn4-21. In addition to the bug fix, which was
contributed on May 16, it fixes the following bugs:
1) Originally, only the 1st camera responded to the connect/disconnect callback function
due to one of the rules for the function PvLinkCallbackRegister(), which is
"The same callback function with the same event may not be registered twice".
Thus, the IOC did not know the connect/disconnect of other cameras (e.g. the 2nd camera).
The prosilica.diff patch fixes this problem by registering a static cameraLinkCallback()
function only once. It handles the connect/disconnect of all the cameras in the IOC.
2) Originally, if more than one camera is not connected at the start of the IOC,
it would be difficult to tell which camera is reconnected because the IDs
of the unconnected cameras might be unknown. Thus, I added 'uniqueIP' as a private variable
in the 'C++ class prosilica' to keep track of the IP addresses of each cameras in the system
in case the unique ID is unknown. By comparing the unique ID or the IP address, one can match
the newly connected camera with the right one in the EPICS database (i.e. PVs).
The patch should work if there are more than two cameras. Hope that this helps.
Cheers,
Kate Feng
________________________________________
From: [email protected] [[email protected]] on behalf of Kate Feng [[email protected]]
Sent: Thursday, May 16, 2013 2:14 PM
To: [email protected]
Subject: asynDriver R4-21: a possible patch for asynRecord.c: test on GigE camera
Hi,
I am using the asynDriver R4-21 to run Mark River's areaDetectorR1-9-1
software on a Prosilica (i.e. Allied) GigE camera. Everything works
very well
except when the camera becomes offline (e.g. the Ethernet cable for the
Prosilica GigE camera is disconnected). Thankfully, the medm screen
indicated
the "disconnect" status when the Ethernet cable for the Prosilica GE
camera is
disconnected. However, one could still hit the 'acquire start/stop' and
other buttons
via the medm GUI and access the PVs.
One would wish to have a more detectable effect so that one could not
access
the camera's PVs, when the camera became offline. The attached patch
will disable
the PVs access, if the Ethernet cable for the GigE camera is
disconnected. If
the camera becomes online via Ethernet, the PVs will be accessible again.
The patch is tested with the GigE camera.
Cheers,
Kate Feng
--- prosilicaSrc/prosilica.cpp.orig 2013-02-17 10:15:03.000000000 -0500
+++ prosilicaSrc/prosilica.cpp 2013-06-06 09:40:56.347820082 -0400
@@ -42,10 +42,7 @@
#define MAX_PVAPI_FRAMES 2 /**< Number of frame buffers for PvApi */
#define MAX_PACKET_SIZE 8228
-
-/* Static functions to interface with PvAPI */
-static void PVDECL GlobalCameraLinkCallback(void* Context, tPvInterface Interface,
- tPvLinkEvent Event, unsigned long UniqueId);
+
/** Driver for Prosilica GigE and CameraLink cameras using their PvApi library */
class prosilica : public ADDriver {
@@ -66,7 +63,7 @@
/* These are called from C and so must be public */
/* This is called by the AVT driver when the connection status of a camera changes */
- asynStatus cameraLinkCallback(tPvInterface Interface, tPvLinkEvent Event, unsigned long UniqueId);
+ static void PVDECL cameraLinkCallback(void* Context, tPvInterface Interface, tPvLinkEvent Event, unsigned long UniqueId);
void frameCallback(tPvFrame *pFrame);
/* Removes the PvAPI callback functions and disconnects the camera */
static void shutdown(void *arg);
@@ -124,6 +121,7 @@
/* These items are specific to the Prosilica driver */
tPvHandle PvHandle; /* GenericPointer for the Prosilica PvAPI library */
char *cameraId; /* This can be a uniqueID, IP name, or IP address */
+ unsigned long uniqueIP;
unsigned long uniqueId;
tPvCameraInfoEx PvCameraInfo;
tPvFrame *PvFrames;
@@ -264,6 +262,10 @@
#define PSStrobe1CtlDurationString "PS_STROBE_1_CTL_DURATION"/* (asynInt32, r/w) Strobe 1 controlled duration */
#define PSStrobe1DurationString "PS_STROBE_1_DURATION" /* (asynFloat64, r/w) Strobe 1 duration */
+#define MAX_PROSILICA_CAM 20
+static int numOfProsilicaCam=0;
+static prosilica *pProsilicaDriver[MAX_PROSILICA_CAM];
+
void prosilica::shutdown (void* arg) {
prosilica *p = (prosilica*)arg;
@@ -275,21 +277,25 @@
int status;
static const char *functionName = "~prosilica";
+ prosilica *p = (prosilica*) this;
this->lock();
disconnectCamera();
this->unlock();
- status = PvLinkCallbackUnRegister(GlobalCameraLinkCallback, ePvLinkAdd);
- if (status) {
- asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
+
+ if (p->uniqueId== pProsilicaDriver[0]->uniqueId) {
+ status = PvLinkCallbackUnRegister(p->cameraLinkCallback, ePvLinkAdd);
+ if (status) {
+ asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s: error calling PvLinkCallbackUnRegister for ePvLinkAdd, status=%d\n",
driverName, functionName, status);
- }
- status = PvLinkCallbackUnRegister(GlobalCameraLinkCallback, ePvLinkRemove);
- if (status) {
- asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
+ }
+ status = PvLinkCallbackUnRegister(p->cameraLinkCallback, ePvLinkRemove);
+ if (status) {
+ asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s:%s: error calling PvLinkCallbackUnRegister for ePvLinkRemove, status=%d\n",
driverName, functionName, status);
+ }
}
if (PvApiInitialized) {
@@ -297,50 +303,67 @@
PvUnInitialize();
PvApiInitialized = false;
}
-}
-
-
-// callback function called on seperate thread when a registered camera event received
-void PVDECL GlobalCameraLinkCallback(void* Context,
- tPvInterface Interface,
- tPvLinkEvent Event,
- unsigned long UniqueId) {
-
- prosilica *p = (prosilica*)Context;
- if ( p ) p->cameraLinkCallback(Interface, Event, UniqueId);
+
}
// Changes the connection status of the camera based on information from the AVT library
-asynStatus prosilica::cameraLinkCallback( tPvInterface Interface, tPvLinkEvent Event, unsigned long UniqueId ) {
+void PVDECL prosilica::cameraLinkCallback(void* Context, tPvInterface Interface, tPvLinkEvent Event, unsigned long UniqueId ) {
asynStatus status = asynSuccess;
+ int i, found=0;
//static const char *functionName = "cameraLinkCallback";
-
- this->lock();
- switch( Event ) {
+ unsigned long UniqueIp=0;
+
+ for (i=0; i <numOfProsilicaCam; i++) {
+ pProsilicaDriver[i]->lock();
+ switch( Event ) {
case ePvLinkAdd: {
- // We cannot check to see if the UniqueId matches ours, because the camera may have been
- // specified by IP address or IP name and may never have connected yet, so we don't know its
- // uniqueId.
- // So instead whenever any camera comes online and we are not connected we try to connect to it,
- // in hopes that it is our camera.
- if (this->PvHandle == 0) {
- status = this->connectCamera();
+ // We need to check to see if the UniqueId matches ours. If the camera have been
+ // specified by IP address or IP name and may never have connected yet, we can find out
+ // if the IP address of this recently connected camera matches with ours.
+ if ( !pProsilicaDriver[i]->uniqueId) {
+ if ( !UniqueIp) { /* If we did not find out the IP address of this recently connected camera yet */
+ tPvIpSettings ipSettings;
+
+ PvCameraIpSettingsGet(UniqueId, &ipSettings);
+ UniqueIp = ipSettings.CurrentIpAddress;
+ }
+ if (UniqueIp == pProsilicaDriver[i]->uniqueIP) {
+ status = pProsilicaDriver[i]->connectCamera();
+ if (status)
+ printf("Camera uniqueIP 0x%lx connectCamera() error status %d\n",pProsilicaDriver[i]->uniqueIP, status);
+ else
+ found=1;
+ }
+ }
+ else {
+ if ( UniqueId == pProsilicaDriver[i]->uniqueId) {
+ status = pProsilicaDriver[i]->connectCamera();
+ if (status)
+ printf("Camera uniqueId 0x%lx connectCamera() error status %d\n",pProsilicaDriver[i]->uniqueId, status);
+ else
+ found=1;
+ }
}
break;
}
case ePvLinkRemove: {
- if( UniqueId == this->uniqueId ) {
+ if( UniqueId == pProsilicaDriver[i]->uniqueId ) {
// the camera has disconnected
- status = this->disconnectCamera();
+ status = pProsilicaDriver[i]->disconnectCamera();
+ if (status)
+ printf("Camera uniqueId 0x%lx disconnectCamera() error status %d\n",pProsilicaDriver[i]->uniqueId, status);
+ else
+ found=1;
}
break;
}
default:
break;
+ }
+ pProsilicaDriver[i]->unlock();
+ if (found) break;
}
- this->unlock();
- return status;
}
@@ -1076,7 +1099,7 @@
/* First disconnect from the camera */
disconnectCamera();
- /* Determine if we have been passed a uniqueID (all characters in cameraId are digits),
+ /* Determine if we have been passed an uniqueID (all characters in cameraId are digits),
* or an IP address (anything else) */
isUniqueId = true;
for (i=0; i<(int)strlen(this->cameraId); i++) {
@@ -1101,6 +1124,7 @@
driverName, functionName, this->cameraId);
return asynError;
}
+ this->uniqueIP = (unsigned long) ipAddr.s_addr;
status = PvCameraInfoByAddrEx(ipAddr.s_addr, &this->PvCameraInfo, NULL, sizeof(this->PvCameraInfo));
if (status) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
@@ -1582,35 +1606,46 @@
driverName, functionName, status);
return;
}
+
+ tPvErr errCode;
+ prosilica *p= (prosilica*)this;
+
+ // register camera connection callback
+ if ((errCode = PvLinkCallbackRegister(p->cameraLinkCallback,ePvLinkAdd,(void*)this)) != ePvErrSuccess)
+ printf("PvLinkCallbackRegister err: %u\n", errCode);
+
+ // register camera disconnection callback
+ if((errCode = PvLinkCallbackRegister(p->cameraLinkCallback,ePvLinkRemove,(void*)this)) != ePvErrSuccess)
+ printf("PvLinkCallbackRegister err: %u\n", errCode);
+
PvApiInitialized = 1;
}
-
- tPvErr errCode;
- // register camera connection callback
- if((errCode = PvLinkCallbackRegister(GlobalCameraLinkCallback,ePvLinkAdd,(void*)this)) != ePvErrSuccess)
- printf("PvLinkCallbackRegister err: %u\n", errCode);
-
- // register camera disconnection callback
- if((errCode = PvLinkCallbackRegister(GlobalCameraLinkCallback,ePvLinkRemove,(void*)this)) != ePvErrSuccess)
- printf("PvLinkCallbackRegister err: %u\n", errCode);
-
- /* Need to wait a short while for the PvAPI library to find the cameras (0.2 seconds is not long enough in 1.24) */
- epicsThreadSleep(1.0);
-
- /* Try to connect to the camera.
- * It is not a fatal error if we cannot now, the camera may be off or owned by
- * someone else. It may connect later. */
- this->lock();
- status = connectCamera();
- this->unlock();
- if (status) {
- printf("%s:%s: cannot connect to camera %s, manually connect when available.\n",
+ if (numOfProsilicaCam < MAX_PROSILICA_CAM) {
+ /* Need to wait a short while for the PvAPI library to find the cameras (0.2 seconds is not long enough in 1.24) */
+ epicsThreadSleep(1.0);
+
+ /* Try to connect to the camera.
+ * It is not a fatal error if we cannot now, the camera may be off or owned by
+ * someone else. It may connect later. */
+ this->lock();
+ status = connectCamera();
+ this->unlock();
+ /* <skf> Add pointer to the prosilica driver (e,g, camera's uniqueId) for the PvLinkCallback even if it is not connected at this point */
+ pProsilicaDriver[numOfProsilicaCam++] = (prosilica*)this;
+
+ if (status) {
+ printf("%s:%s: cannot connect to camera %s, manually connect when available.\n",
driverName, functionName, cameraId);
- return;
+
+ return;
+ }
+ /* Register the shutdown function for epicsAtExit */
+ epicsAtExit(shutdown, (void*)this);
+ }
+ else {
+ printf("The number of cameras configured is more than %d.\n", MAX_PROSILICA_CAM);
+ exit(0);
}
-
- /* Register the shutdown function for epicsAtExit */
- epicsAtExit(shutdown, (void*)this);
}
/* Code for iocsh registration */
--- asynRecord/asynRecord.c.orig 2012-12-21 17:34:45.000000000 -0500
+++ asynRecord/asynRecord.c 2013-06-05 14:20:31.000000000 -0400
@@ -874,12 +874,33 @@
asynPrint(pasynUser, ASYN_TRACE_FLOW,
"%s: exception %d\n",
pasynRec->name, (int) exception);
- if(callLock)
- dbScanLock((dbCommon *) pasynRec);
+
+ switch(exception) {
+ case asynExceptionConnect:
+ if (((pasynRec->udf) && callLock) || ((pasynRec->cnct) && callLock))
+ dbScanLock((dbCommon *) pasynRec);
+ break;
+ default:
+ if (callLock) dbScanLock((dbCommon *) pasynRec);
+ break;
+ }
/* There has been a change in connect or enable status */
monitorStatus(pasynRec);
- if(callLock)
- dbScanUnlock((dbCommon *) pasynRec);
+
+ switch(exception) {
+ case asynExceptionConnect:
+ if ((pasynRec->cnct) && callLock) {
+ dbScanUnlock((dbCommon *) pasynRec);
+ if (pasynRec->udf) pasynRec->udf=0;
+ printf("Asyn record %s is reconnected\n", pasynRec->name);
+ }
+ else
+ printf("Asyn record %s is disconnected\n", pasynRec->name);
+ break;
+ default:
+ if (callLock) dbScanUnlock((dbCommon *) pasynRec);
+ break;
+ }
}
static void queueTimeoutCallbackProcess(asynUser * pasynUser)
@@ -1246,6 +1267,7 @@
"queueRequest failed\n");
pasynManager->memFree(pmsg, sizeof(*pmsg));
pasynManager->freeAsynUser(pasynUserEos);
+ pasynRec->udf=1;
}
}
pasynRec->pcnct = 1;
- Replies:
- RE: asynDriver R4-21: a possible patch for asynRecord.c: test on GigE camera Mark Rivers
- References:
- asynDriver R4-21: a possible patch for asynRecord.c: test on GigE camera Kate Feng
- Navigate by Date:
- Prev:
max length char for an ENUM Touchard Dominique
- Next:
Re: medm/motif compilation on Fedora 16+ Andrew Johnson
- Index:
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
<2013>
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
- Navigate by Thread:
- Prev:
asynDriver R4-21: a possible patch for asynRecord.c: test on GigE camera Kate Feng
- Next:
RE: asynDriver R4-21: a possible patch for asynRecord.c: test on GigE camera Mark Rivers
- Index:
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
<2013>
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
|