Example #1
0
File: seq_if.c Project: ukaea/epics
static void pv_call_failure(DBCHAN *dbch, PVMETA *meta, pvStat status)
{
	meta->status = status;
	meta->severity = pvSevrERROR;
	meta->message = pvVarGetMess(dbch->pvid);
}
Example #2
0
File: seq_if.c Project: ukaea/epics
/*
 * Assign/Connect to a channel.
 * Assign to a zero-length string ("") disconnects/de-assigns,
 * in safe mode, creates an anonymous PV.
 */
epicsShareFunc pvStat seq_pvAssign(SS_ID ss, CH_ID chId, const char *pvName)
{
	PROG	*sp = ss->prog;
	CHAN	*ch = sp->chan + chId;
	pvStat	status = pvStatOK;
	DBCHAN	*dbch;

	if (!pvName) pvName = "";

	DEBUG("Assign %s to \"%s\"\n", ch->varName, pvName);

	epicsMutexMustLock(sp->lock);

	dbch = ch->dbch;

	if (dbch)	/* was assigned to a named PV */
	{
		ch->dbch = 0;

		epicsMutexUnlock(sp->lock);

		status = pvVarDestroy(&dbch->pvid);

		epicsMutexMustLock(sp->lock);

		sp->assignCount--;

		if (dbch->connected)	/* see connection handler */
		{
			dbch->connected = FALSE;
			sp->connectCount--;

			/* Must not call seq_camonitor(ch, FALSE), it would give an
			error because channel is already dead. pvVarDestroy takes
                        care that the monid inside the pvid gets invalidated. */

			/* Note ch->monitored remains on because it is a configuration
			value that belongs to the variable and newly created channels
			for the same variable should inherit this configuration. */
		}

		if (status != pvStatOK)
		{
			errlogSevPrintf(errlogFatal, "pvAssign(var %s, pv %s): pvVarDestroy() failure: "
				"%s\n", ch->varName, dbch->dbName, pvVarGetMess(dbch->pvid));
		}

		free(dbch->dbName);
	}

	if (pvName[0] == 0)	/* new name is empty -> free resources */
	{
		if (dbch) {
			free(dbch);
		}
	}
	else		/* new name is non-empty -> create resources */
	{
		if (!dbch)
		{
			dbch = new(DBCHAN);
			if (!dbch)
			{
				errlogSevPrintf(errlogFatal, "pvAssign: calloc failed\n");
				epicsMutexUnlock(sp->lock);
				return pvStatERROR;
			}
		}
		dbch->dbName = epicsStrDup(pvName);
		if (!dbch->dbName)
		{
			errlogSevPrintf(errlogFatal, "pvAssign: epicsStrDup failed\n");
			free(dbch);
			epicsMutexUnlock(sp->lock);
			return pvStatERROR;
		}
		ch->dbch = dbch;

		status = pvVarCreate(
			sp->pvSys,		/* PV system context */
			dbch->dbName,		/* DB channel name */
			seq_conn_handler,	/* connection handler routine */
			seq_event_handler,	/* event handler routine */
			ch,			/* user ptr is CHAN structure */
			&dbch->pvid);		/* ptr to pvid */
		if (status != pvStatOK)
		{
			errlogSevPrintf(errlogFatal, "pvAssign(var %s, pv %s): pvVarCreate() failure: "
				"%s\n", ch->varName, dbch->dbName, pvVarGetMess(dbch->pvid));
			free(ch->dbch->dbName);
			free(ch->dbch);
		}
		else
		{
			sp->assignCount++;
		}
	}

	epicsMutexUnlock(sp->lock);

	return status;
}
Example #3
0
File: seq_if.c Project: ukaea/epics
/*
 * Put a variable's value to a PV, with timeout.
 */
epicsShareFunc pvStat seq_pvPutTmo(SS_ID ss, CH_ID chId, enum compType compType, double tmo)
{
	PROG	*sp = ss->prog;
	CHAN	*ch = sp->chan + chId;
	pvStat	status;
	unsigned count;
	char	*var = valPtr(ch,ss);	/* ptr to value */
	PVREQ	*req;
	DBCHAN	*dbch = ch->dbch;
	PVMETA	*meta = metaPtr(ch,ss);

	DEBUG("pvPut: pv name=%s, var=%p\n", dbch ? dbch->dbName : "<anonymous>", var);

	/* First handle anonymous PV (safe mode only) */
	if (optTest(sp, OPT_SAFE) && !dbch)
	{
		anonymous_put(ss, ch);
		return pvStatOK;
	}
	if (!dbch)
	{
		errlogSevPrintf(errlogMajor,
			"pvPut(%s): user error (not assigned to a PV)\n",
			ch->varName
		);
		return pvStatERROR;
	}

	/* Check for channel connected */
	status = check_connected(dbch, meta);
	if (status != pvStatOK) return status;

	/* Determine whether to perform synchronous, asynchronous, or
	   plain put ((+a) option was never honored for put, so DEFAULT
	   means fire-and-forget) */
	status = check_pending(pvEventPut, ss, ss->putReq + chId, ch->varName,
		dbch, meta, compType, tmo);
	if (status != pvStatOK)
		return status;

	/* Determine number of elements to put (don't try to put more
	   than db count) */
	count = dbch->dbCount;

	/* Perform the PV put operation (either non-blocking or with a
	   callback routine specified) */
	if (compType == DEFAULT)
	{
		status = pvVarPutNoBlock(
				&dbch->pvid,		/* PV id */
				ch->type->putType,	/* data type */
				count,			/* element count */
				(pvValue *)var);	/* data value */
		if (status != pvStatOK)
		{
			pv_call_failure(dbch, meta, status);
			errlogSevPrintf(errlogFatal, "pvPut(var %s, pv %s): pvVarPutNoBlock() failure: %s\n",
				ch->varName, dbch->dbName, pvVarGetMess(dbch->pvid));
			return status;
		}
	}
	else
	{
		/* Allocate and initialize a pv request */
		req = (PVREQ *)freeListMalloc(sp->pvReqPool);
		req->ss = ss;
		req->ch = ch;

		assert(ss->putReq[chId] == NULL);
		ss->putReq[chId] = req;

		status = pvVarPutCallback(
				&dbch->pvid,		/* PV id */
				ch->type->putType,	/* data type */
				count,			/* element count */
				(pvValue *)var,		/* data value */
				req);			/* user arg */
		if (status != pvStatOK)
		{
			pv_call_failure(dbch, meta, status);
			errlogSevPrintf(errlogFatal, "pvPut(var %s, pv %s): pvVarPutCallback() failure: %s\n",
				ch->varName, dbch->dbName, pvVarGetMess(dbch->pvid));
			ss->putReq[chId] = NULL;	/* cancel the request */
			freeListFree(sp->pvReqPool, req);
			check_connected(dbch, meta);
			return status;
		}

		if (compType == SYNC)			/* wait for completion */
		{
			pvSysFlush(sp->pvSys);
			status = wait_complete(pvEventPut, ss, ss->putReq + chId, dbch, meta, tmo);
			if (status != pvStatOK)
				return status;
		}
	}
	return pvStatOK;
}
Example #4
0
File: seq_if.c Project: ukaea/epics
/*
 * Get value from a channel, with timeout.
 */
epicsShareFunc pvStat seq_pvGetTmo(SS_ID ss, CH_ID chId, enum compType compType, double tmo)
{
	PROG		*sp = ss->prog;
	CHAN		*ch = sp->chan + chId;
	pvStat		status;
	PVREQ		*req;
	DBCHAN		*dbch = ch->dbch;
	PVMETA		*meta = metaPtr(ch,ss);

	/* Anonymous PV and safe mode, just copy from shared buffer.
	   Note that completion is always immediate, so no distinction
	   between SYNC and ASYNC needed. See also pvGetComplete. */
	if (optTest(sp, OPT_SAFE) && !dbch)
	{
		/* Copy regardless of whether dirty flag is set or not */
		ss_read_buffer(ss, ch, FALSE);
		return pvStatOK;
	}
	/* No named PV and traditional mode => user error */
	if (!dbch)
	{
		errlogSevPrintf(errlogMajor,
			"pvGet(%s): user error (not assigned to a PV)\n",
			ch->varName
		);
		return pvStatERROR;
	}

	if (compType == DEFAULT)
	{
		compType = optTest(sp, OPT_ASYNC) ? ASYNC : SYNC;
	}

	status = check_pending(pvEventGet, ss, ss->getReq + chId, ch->varName,
		dbch, meta, compType, tmo);
	if (status != pvStatOK)
		return status;

	/* Allocate and initialize a pv request */
	req = (PVREQ *)freeListMalloc(sp->pvReqPool);
	req->ss = ss;
	req->ch = ch;

	assert(ss->getReq[chId] == NULL);
	ss->getReq[chId] = req;

	/* Perform the PV get operation with a callback routine specified.
	   Requesting more than db channel has available is ok. */
	status = pvVarGetCallback(
			&dbch->pvid,		/* PV id */
			ch->type->getType,	/* request type */
			ch->count,		/* element count */
			req);			/* user arg */
	if (status != pvStatOK)
	{
		pv_call_failure(dbch, meta, status);
		errlogSevPrintf(errlogFatal,
			"pvGet(var %s, pv %s): pvVarGetCallback() failure: %s\n",
			ch->varName, dbch->dbName, pvVarGetMess(dbch->pvid));
		ss->getReq[chId] = NULL;	/* cancel the request */
		freeListFree(sp->pvReqPool, req);
		check_connected(dbch, meta);
		return status;
	}

	/* Synchronous: wait for completion */
	if (compType == SYNC)
	{
		pvSysFlush(sp->pvSys);
		status = wait_complete(pvEventGet, ss, ss->getReq + chId, dbch, meta, tmo);
		if (status != pvStatOK)
			return status;
		if (optTest(sp, OPT_SAFE))
			/* Copy regardless of whether dirty flag is set or not */
			ss_read_buffer(ss, ch, FALSE);
	}

	return pvStatOK;
}
Example #5
0
/*
 * Assign/Connect to a channel.
 * Assign to a zero-length string ("") disconnects/de-assigns,
 * in safe mode, creates an anonymous PV.
 */
epicsShareFunc pvStat epicsShareAPI seq_pvAssign(SS_ID ss, VAR_ID varId, const char *pvName)
{
	SPROG	*sp = ss->sprog;
	CHAN	*ch = sp->chan + varId;
	pvStat	status = pvStatOK;
	DBCHAN	*dbch = ch->dbch;

	if (!pvName) pvName = "";

	DEBUG("Assign %s to \"%s\"\n", ch->varName, pvName);

	epicsMutexMustLock(sp->programLock);

	if (dbch)	/* was assigned to a named PV */
	{
		status = pvVarDestroy(dbch->pvid);
		dbch->pvid = NULL;

		sp->assignCount -= 1;

		if (dbch->connected)	/* see connection handler */
		{
			dbch->connected = FALSE;
			sp->connectCount--;

			/* Must not call seq_monitor(ch, FALSE), it would give an
			error because channel is already dead. Instead mark that no
			subscriptions are active so when seq_monitor gets called when
			we re-create the channel, it actually does something. */

			dbch->monid = NULL;

			/* Note ch->monitored remains on because it is a configuration
			value that belongs to the variable and newly created channels
			for the same variable should inherit this configuration. */

		}

		if (status != pvStatOK)
		{
			errlogSevPrintf(errlogFatal, "pvAssign(var %s, pv %s): pvVarDestroy() failure: "
				"%s\n", ch->varName, dbch->dbName, pvVarGetMess(dbch->pvid));
		}
		free(dbch->dbName);
	}

	if (pvName == NULL || pvName[0] == 0)	/* new name is empty -> free resources */
	{
		if (dbch) {
			free(ch->dbch->ssMetaData);
			free(ch->dbch);
		}
	}
	else		/* new name is non-empty -> create resources */
	{
		if (!dbch)
		{
			dbch = new(DBCHAN);
			if (!dbch)
			{
				errlogSevPrintf(errlogFatal, "pvAssign: calloc failed\n");
				return pvStatERROR;
			}
		}
		dbch->dbName = epicsStrDup(pvName);
		if (!dbch->dbName)
		{
			errlogSevPrintf(errlogFatal, "pvAssign: epicsStrDup failed\n");
			free(dbch);
			return pvStatERROR;
		}
		if ((sp->options & OPT_SAFE) && sp->numSS > 0)
		{
			dbch->ssMetaData = newArray(PVMETA, sp->numSS);
			if (!dbch->ssMetaData)
			{
				errlogSevPrintf(errlogFatal, "pvAssign: calloc failed\n");
				free(dbch->dbName);
				free(dbch);
				return pvStatERROR;
			}
		}
		ch->dbch = dbch;
		sp->assignCount++;
		status = pvVarCreate(
			sp->pvSys,		/* PV system context */
			dbch->dbName,		/* DB channel name */
			seq_conn_handler,	/* connection handler routine */
			ch,			/* user ptr is CHAN structure */
			sp->debug,		/* debug level (inherited) */
			&dbch->pvid);		/* ptr to pvid */
		if (status != pvStatOK)
		{
			errlogSevPrintf(errlogFatal, "pvAssign(var %s, pv %s): pvVarCreate() failure: "
				"%s\n", ch->varName, dbch->dbName, pvVarGetMess(dbch->pvid));
			if (ch->dbch->ssMetaData)
				free(ch->dbch->ssMetaData);
			free(ch->dbch->dbName);
			free(ch->dbch);
		}
	}

	epicsMutexUnlock(sp->programLock);

	return status;
}
Example #6
0
/*
 * Get value from a channel.
 * TODO: add optional timeout argument.
 */
epicsShareFunc pvStat epicsShareAPI seq_pvGet(SS_ID ss, VAR_ID varId, enum compType compType)
{
	SPROG		*sp = ss->sprog;
	CHAN		*ch = sp->chan + varId;
	pvStat		status;
	PVREQ		*req;
	epicsEventId	getSem = ss->getSemId[varId];
	DBCHAN		*dbch = ch->dbch;
	PVMETA		*meta = metaPtr(ch,ss);
	double		tmo = seq_sync_timeout;

	/* Anonymous PV and safe mode, just copy from shared buffer.
	   Note that completion is always immediate, so no distinction
	   between SYNC and ASYNC needed. See also pvGetComplete. */
	if ((sp->options & OPT_SAFE) && !dbch)
	{
		/* Copy regardless of whether dirty flag is set or not */
		ss_read_buffer(ss, ch, FALSE);
		return pvStatOK;
	}
	/* No named PV and traditional mode => user error */
	if (!dbch)
	{
		errlogSevPrintf(errlogMajor,
			"pvGet(%s): user error (variable not assigned)\n",
			ch->varName
		);
		return pvStatERROR;
	}

	if (compType == DEFAULT)
	{
		compType = (sp->options & OPT_ASYNC) ? ASYNC : SYNC;
	}

	if (compType == SYNC)
	{
		double before, after;
		pvTimeGetCurrentDouble(&before);
		switch (epicsEventWaitWithTimeout(getSem, tmo))
		{
		case epicsEventWaitOK:
			status = check_connected(dbch, meta);
			if (status) return epicsEventSignal(getSem), status;
			pvTimeGetCurrentDouble(&after);
			tmo -= (after - before);
			break;
		case epicsEventWaitTimeout:
			errlogSevPrintf(errlogMajor,
				"pvGet(ss %s, var %s, pv %s): failed (timeout "
				"waiting for other get requests to finish)\n",
				ss->ssName, ch->varName, dbch->dbName
			);
			return pvStatERROR;
		case epicsEventWaitError:
			/* try to recover */
			ss->getReq[varId] = NULL;
			epicsEventSignal(getSem);
			errlogSevPrintf(errlogFatal,
				"pvGet: epicsEventWaitWithTimeout() failure\n");
			return pvStatERROR;
		}
	}
	else if (compType == ASYNC)
	{
		switch (epicsEventTryWait(getSem))
		{
		case epicsEventWaitOK:
			if (ss->getReq[varId] != NULL)
			{
				/* previous request timed out but user
				   did not call pvGetComplete */
				ss->getReq[varId] = NULL;
			}
			status = check_connected(dbch, meta);
			if (status) return epicsEventSignal(getSem), status;
			break;
		case epicsEventWaitTimeout:
			errlogSevPrintf(errlogMajor,
				"pvGet(ss %s, var %s, pv %s): user error "
				"(there is already a get pending for this variable/"
				"state set combination)\n",
				ss->ssName, ch->varName, dbch->dbName
			);
			return pvStatERROR;
		case epicsEventWaitError:
			/* try to recover */
			ss->getReq[varId] = NULL;
			epicsEventSignal(getSem);
			errlogSevPrintf(errlogFatal,
				"pvGet: epicsEventTryWait() failure\n");
			return pvStatERROR;
		}
	}

	/* Allocate and initialize a pv request */
	req = (PVREQ *)freeListMalloc(sp->pvReqPool);
	req->ss = ss;
	req->ch = ch;

	assert(ss->getReq[varId] == NULL);
	ss->getReq[varId] = req;

	/* Perform the PV get operation with a callback routine specified.
	   Requesting more than db channel has available is ok. */
	status = pvVarGetCallback(
			dbch->pvid,		/* PV id */
			ch->type->getType,	/* request type */
			ch->count,		/* element count */
			seq_get_handler,	/* callback handler */
			req);			/* user arg */
	if (status != pvStatOK)
	{
		meta->status = pvStatERROR;
		meta->severity = pvSevrMAJOR;
		meta->message = "get failure";
		errlogSevPrintf(errlogFatal, "pvGet(var %s, pv %s): pvVarGetCallback() failure: %s\n",
			ch->varName, dbch->dbName, pvVarGetMess(dbch->pvid));
		ss->getReq[varId] = NULL;
		freeListFree(sp->pvReqPool, req);
		epicsEventSignal(getSem);
		check_connected(dbch, meta);
		return status;
	}

	/* Synchronous: wait for completion */
	if (compType == SYNC)
	{
		epicsEventWaitStatus event_status;

		pvSysFlush(sp->pvSys);
		event_status = epicsEventWaitWithTimeout(getSem, tmo);
		ss->getReq[varId] = NULL;
		epicsEventSignal(getSem);
		switch (event_status)
		{
		case epicsEventWaitOK:
			status = check_connected(dbch, meta);
			if (status) return status;
			if (sp->options & OPT_SAFE)
				/* Copy regardless of whether dirty flag is set or not */
				ss_read_buffer(ss, ch, FALSE);
			break;
		case epicsEventWaitTimeout:
			meta->status = pvStatTIMEOUT;
			meta->severity = pvSevrMAJOR;
			meta->message = "get completion timeout";
			return meta->status;
		case epicsEventWaitError:
			meta->status = pvStatERROR;
			meta->severity = pvSevrMAJOR;
			meta->message = "get completion failure";
			return meta->status;
		}
	}

	return pvStatOK;
}
Example #7
0
/*
 * Put a variable's value to a PV.
 */
epicsShareFunc pvStat epicsShareAPI seq_pvPut(SS_ID ss, VAR_ID varId, enum compType compType)
{
	SPROG	*sp = ss->sprog;
	CHAN	*ch = sp->chan + varId;
	pvStat	status;
	unsigned count;
	char	*var = valPtr(ch,ss);	/* ptr to value */
	PVREQ	*req;
	DBCHAN	*dbch = ch->dbch;
	PVMETA	*meta = metaPtr(ch,ss);
	epicsEventId	putSem = ss->putSemId[varId];
	double	tmo = seq_sync_timeout;

	DEBUG("pvPut: pv name=%s, var=%p\n", dbch ? dbch->dbName : "<anonymous>", var);

	/* First handle anonymous PV (safe mode only) */
	if ((sp->options & OPT_SAFE) && !dbch)
	{
		anonymous_put(ss, ch);
		return pvStatOK;
	}
	if (!dbch)
	{
		errlogSevPrintf(errlogMajor,
			"pvPut(%s): user error (variable not assigned)\n",
			ch->varName
		);
		return pvStatERROR;
	}

	/* Check for channel connected */
	status = check_connected(dbch, meta);
	if (status) return status;

	/* Determine whether to perform synchronous, asynchronous, or
	   plain put ((+a) option was never honored for put, so DEFAULT
	   means non-blocking and therefore implicitly asynchronous) */
	if (compType == SYNC)
	{
		double before, after;
		pvTimeGetCurrentDouble(&before);
		switch (epicsEventWaitWithTimeout(putSem, tmo))
		{
		case epicsEventWaitOK:
			pvTimeGetCurrentDouble(&after);
			tmo -= (after - before);
			break;
		case epicsEventWaitTimeout:
			errlogSevPrintf(errlogMajor,
				"pvPut(ss %s, var %s, pv %s): failed (timeout "
				"waiting for other put requests to finish)\n",
				ss->ssName, ch->varName, dbch->dbName
			);
			return pvStatERROR;
		case epicsEventWaitError:
			/* try to recover */
			ss->putReq[varId] = NULL;
			epicsEventSignal(putSem);
			errlogSevPrintf(errlogFatal,
				"pvPut: epicsEventWaitWithTimeout() failure\n");
			return pvStatERROR;
		}
	}
	else if (compType == ASYNC)
	{
		switch (epicsEventTryWait(putSem))
		{
		case epicsEventWaitOK:
			if (ss->putReq[varId] != NULL)
			{
				/* previous request timed out but user
				   did not call pvPutComplete */
				ss->putReq[varId] = NULL;
			}
			break;
		case epicsEventWaitTimeout:
			meta->status = pvStatERROR;
			meta->severity = pvSevrMAJOR;
			meta->message = "already one put pending";
			status = meta->status;
			errlogSevPrintf(errlogMajor,
				"pvPut(ss %s, var %s, pv %s): user error "
				"(there is already a put pending for this variable/"
				"state set combination)\n",
				ss->ssName, ch->varName, dbch->dbName
			);
			return pvStatERROR;
		case epicsEventWaitError:
			/* try to recover */
			ss->putReq[varId] = NULL;
			epicsEventSignal(putSem);
			errlogSevPrintf(errlogFatal,
				"pvPut: epicsEventTryWait() failure\n");
			return pvStatERROR;
		}
	}

	/* Determine number of elements to put (don't try to put more
	   than db count) */
	count = dbch->dbCount;

	/* Perform the PV put operation (either non-blocking or with a
	   callback routine specified) */
	if (compType == DEFAULT)
	{
		status = pvVarPutNoBlock(
				dbch->pvid,		/* PV id */
				ch->type->putType,	/* data type */
				count,			/* element count */
				(pvValue *)var);	/* data value */
		if (status != pvStatOK)
		{
			errlogSevPrintf(errlogFatal, "pvPut(var %s, pv %s): pvVarPutNoBlock() failure: %s\n",
				ch->varName, dbch->dbName, pvVarGetMess(dbch->pvid));
			return status;
		}
	}
	else
	{
		/* Allocate and initialize a pv request */
		req = (PVREQ *)freeListMalloc(sp->pvReqPool);
		req->ss = ss;
		req->ch = ch;

		assert(ss->putReq[varId] == NULL);
		ss->putReq[varId] = req;

		status = pvVarPutCallback(
				dbch->pvid,		/* PV id */
				ch->type->putType,	/* data type */
				count,			/* element count */
				(pvValue *)var,		/* data value */
				seq_put_handler,	/* callback handler */
				req);			/* user arg */
		if (status != pvStatOK)
		{
			ss->putReq[varId] = NULL;
			errlogSevPrintf(errlogFatal, "pvPut(var %s, pv %s): pvVarPutCallback() failure: %s\n",
				ch->varName, dbch->dbName, pvVarGetMess(dbch->pvid));
			freeListFree(sp->pvReqPool, req);
			epicsEventSignal(putSem);
			check_connected(dbch, meta);
			return status;
		}
	}

	/* Synchronous: wait for completion (10s timeout) */
	if (compType == SYNC)
	{
		epicsEventWaitStatus event_status;

		pvSysFlush(sp->pvSys);
		event_status = epicsEventWaitWithTimeout(putSem, tmo);
		ss->putReq[varId] = NULL;
		epicsEventSignal(putSem);
		switch (event_status)
		{
		case epicsEventWaitOK:
			status = check_connected(dbch, meta);
			if (status) return status;
			break;
		case epicsEventWaitTimeout:
			meta->status = pvStatTIMEOUT;
			meta->severity = pvSevrMAJOR;
			meta->message = "put completion timeout";
			return meta->status;
			break;
		case epicsEventWaitError:
			meta->status = pvStatERROR;
			meta->severity = pvSevrMAJOR;
			meta->message = "put completion failure";
			return meta->status;
			break;
		}
	}

	return pvStatOK;
}