The Action Routine

 

File actions.c

This is where all the work is done. Firstly note that the routine is passed a pointer to the action structure in the action queue. If there is data, this is available in another block of memory on the pointer p->actdata with the length of the data in p->datalen. These two regions are dynamically allocated from the shared data segment and must be freed under all circumstances. It is not acceptable to have any memory leaks from this segment. The usual procedure is to do:

	shm_free(p->actdata);
	RemoveAction(p);

Secondly, the action routine ends by simply returning (this is all that is necessary to remove the thread). If the action was called from a user command, the status is transmitted back via a special message sent by the routine UIReturn(int mpx, int errnum, char *errmsg). The mpx parameter is taken from the mpx member of the action structure. An error number of 0 means OK when it is acceptable to use NULL as the error message. If a real error occurred, use a negative number for the error and an appropriate message.

Here is the action routine ro_callmod():


	static void
	ro_callmod(struct Action *p)
	{
		int i, nsamp;
		int sc, sclength;
		int rval, dlen, rtype, mpx;
		struct mod_data *mp;
		struct nvmapentry *np;
		short *sp;
		char cbuf[64];

		sp = (short *)p->actdata;
		mpx = p->mpx; /* needed by UIReturn() */
		nsamp = p->datalen / sizeof(short);

The first thing to do is to ask the timing module for the Super Cycle length. This is accomplished by sending a message to the timing module and receiving the data back.

	if(SendModule(TMcfd,NULL,0,TM_GETSCLEN, &rtype, 0) < 0) {
		shm_free(p->actdata);
		RemoveAction(p);
		UIReturn(mpx,-200,"FAILED to send to Timing Module");
		return;
	}
	dlen = sizeof(int);
	if(RecvModule(TMrfd, &rval, &dlen, rtype, 0) < 0) {
		shm_free(p->actdata);
		RemoveAction(p);
		UIReturn(mpx, -201, "FAILED to get Timing Module ");
		return;
	}
	sclength = rval; /* This is the length */
	/* Check the length that we have received. If this
	 * has never been set it will return -1. If we get this
	 * we had better give up.
	 */
	if(sclength == -1) {
		shm_free(p->actdata);
		RemoveAction(p);
		UIReturn(mpx, -224, "SC length cannot be found");
		return;
	}

Having got the super cycle length, we can now construct a message to send off to the module. The module message has the structure:

	struct mod_data {
		int nsamples; 		/* Number of (short) samples */
		short inpdata[1]; 	/* start of the actual data */
	};

By declaring the array like this, we can allocate memory for the size of the structure plus the size of our data. This will automatically create an array of the correct size.

	dlen = sizeof(struct mod_data) + (2 * nsamp);
	mp = (struct mod_data *)malloc(dlen);
	mp->nsamples = nsamp;
	memcpy(mp->inpdata, p->actdata, p->datalen);

Now we have created the message, the action structure can be disposed of. The message can be sent off to the module using the routine SendModule() and giving it the message, the length of the message and the Module command. Module commands are defined in rocs.h and consist of a 2 letter module identifier (MO_ in this case) followed by the command word.

We wait for the return from the module with the companion routine RecvModule(). Note that the message queues to and from the modules will have been opened in dispatch.c and the message queue id’s available in MOcfd and Morfd (MO for module and ‘c’ for command, ‘r’ for reply).

	/* We can now get rid if the action stuff */
	shm_free(p->actdata);
	RemoveAction(p);
	/* Send the command to the module */
	if(SendModule(MOcfd,mp,dlen,MO_CALLMOD,&rtype, 0) < 0) {
		free(mp);
		UIReturn(mpx, -209, "FAILED to send to Module");
		return;
	}
	free(mp);
	dlen = sizeof(int);
	if(RecvModule(MOrfd, &rval, &dlen, rtype, 0) < 0) {
		UIReturn(mpx, -204, "FAILED to get Module return");
	return;
	}

Modules usually return a negative number to signify an error and 0 for success. If this was the case, we will construct a suitable error message and return it.

	if(rval != 0) {
		sprintf(cbuf, "Module returned %d", rval);
		UIReturn(-222, cbuf);
		return;
	}

Finally, the module placed its results in a shared data segment. We can do some post processing in the data, in this case by adding the super cycle length, before we return. Note that we indicate success to the user with a return error number of 0.

	/* fill in the SC length before we return */
	adp->sclength = sclength;
	UIReturn(mpx, 0, NULL);
}

This concludes the action processing.

Real Time Actions

What we have seen in the previous section is a User action. This was created as a result of a user command and is executed Now. Another class of actions are Real Time actions. These are placed in the action queue, often at system startup by rocsfe.c and are triggered by specific events. When the action has finished, usually the action structure is left on the queue to be executed again when the event occurs (although it could be removed if this was strictly a one-shot action. Such actions are Mugef Start actions or Mopos BeamIn/BeamOut actions.

To prevent an action from being re-executed before the first execution is complete, the action dispatcher Ors a 1 into the top bit of the logevent member of the action structure before creating the action thread. Thus if this event occurs again, the dispatcher won't re-execute the action. Normally, this goes unnoticed as the User actions remove the action structure before they finish. Real Time actions on the other must remove this bit by doing:

p->logevent &= ~0x80000000;

to re-enable the action just before exiting.