Files
sics/sctcomtask.c

518 lines
14 KiB
C

/**
* This is another approach at using the scriptcontext system. It introduces
* the concept of a communication task which is executed against a device serializer,
* accessed via a given ScriptContext. Clients can:
* - Create CommTasks
* - Figure out their state
* - Retrieve reply data
* - wait for a ComTask to finish.
*
* The purpose sctcomtask will keep a cache of pending and finished communications.
* Old runs will be deleted periodically. Nevertheless the cache can be listed in order
* to figure out what happened.
*
* The intended use is for C-code or scripts to interact in a serial manner with
* the asynchronous communication system implemented in devser and ascon. As a
* standalone implementation this would share tons of code with scriptcontext, thus
* this has been implemented as an add on module to scriptcontext.
*
* copyright: see file COPYRIGHT
*
* Mark Koennecke, December 2012
*/
#include <string.h>
#include "sctcomtask.h"
#include "scriptcontext.h"
#include "sicsobj.h"
#include "sicsutil.h"
#include "sicshipadaba.h"
static int mamaID = 0L;
extern char *ParText(Hdb * cmdNode, char *name,
int nPar, char *defaultValue);
/*----------------------------------------------------------------*/
typedef struct{
char *sendData;
char *replyData;
int replyDataLength;
int ID;
double startTime;
double endTime;
ComTaskState state;
}ComTask, *pComTask;
/*----------------------------------------------------------------*/
struct ComTaskManager{
DevSer *devser;
unsigned int halfQueueLength;
ComTask *TaskQueue;
unsigned int iPtr;
};
/*-----------------------------------------------------------------*/
static void KillComTaskData(ComTask *queue, int length)
{
int i;
for(i = 0; i < length; i++){
if(queue[i].sendData != NULL){
free(queue[i].sendData);
queue[i].sendData = NULL;
}
if(queue[i].replyData != NULL){
free(queue[i].replyData);
queue[i].replyData = NULL;
}
}
}
/*-----------------------------------------------------------------*/
static char *ComTaskHandler(void *data, char *replyData, int commError)
{
pComTask task = (pComTask)data;
assert(task != NULL);
if(replyData == NULL){
task->state = eRunning;
return task->sendData;
} else {
if(task->replyData != NULL){
free(task->replyData);
}
task->replyData = strdup(replyData);
task->replyDataLength = strlen(replyData);
task->endTime = DoubleTime();
task->state = eFinished;
return NULL;
}
}
/*-----------------------------------------------------------------*/
static int ComTaskMatch(void *data1, void *data2)
{
pComTask task1 = (pComTask)data1;
pComTask task2 = (pComTask)data2;
assert(task1 && task2);
if(task1->ID == task2->ID){
return 1;
} else {
return 0;
}
}
/*----------------------------------------------------------------*/
static char *stateName[] = {
"waiting",
"running",
"finished",
"unknown"
};
static char* StateToText(ComTaskState state)
{
return stateName[(int)state];
}
/*----------------------------------------------------------------*/
static char* ComTaskInfo(void *data)
{
char *info = NULL;
char buf[] = {"NA"};
char *reply;
int length = 256;
pComTask task = (pComTask)data;
assert(data != NULL);
if(task->sendData != NULL){
length += strlen(task->sendData);
}
if(task->replyData != NULL){
length += strlen(task->replyData);
}
info = calloc(length,sizeof(char));
if(info == NULL){
return info;
}
if(task->replyData == NULL){
reply = buf;
} else {
reply = task->replyData;
}
snprintf(info,length,"%s:%s:%lf:%s:%lf", StateToText(task->state),
task->sendData,task->startTime, reply, task->endTime);
return info;
}
/*----------------------------------------------------------------*/
int StartComTask(ComTaskManager *manager, char *priority, char *sendData)
{
int ID, i;
unsigned int ptr;
DevPrio prio;
assert(manager != NULL && priority != NULL);
ID = mamaID;
mamaID++;
if(mamaID < 0){
mamaID = 0;
}
if(manager->iPtr >= 2*manager->halfQueueLength){
KillComTaskData(manager->TaskQueue,manager->halfQueueLength);
memmove(manager->TaskQueue,&manager->TaskQueue[manager->halfQueueLength],
manager->halfQueueLength*sizeof(ComTask));
/**
* The pointers need to be cleared, not freed, in order to
* prevent double freeing when reusing them.
*/
for(i = manager->halfQueueLength; i < 2*manager->halfQueueLength; i++){
manager->TaskQueue[i].sendData = NULL;
manager->TaskQueue[i].replyData = NULL;
}
manager->iPtr = manager->halfQueueLength;
}
ptr = manager->iPtr;
manager->iPtr++;
manager->TaskQueue[ptr].ID = ID;
manager->TaskQueue[ptr].startTime = DoubleTime();
manager->TaskQueue[ptr].endTime = 0.;
manager->TaskQueue[ptr].state = eWaiting;
if(manager->TaskQueue[ptr].sendData != NULL){
free(manager->TaskQueue[ptr].sendData);
}
manager->TaskQueue[ptr].sendData = strdup(sendData);
if(manager->TaskQueue[ptr].replyData != NULL){
free(manager->TaskQueue[ptr].replyData);
}
manager->TaskQueue[ptr].replyData = NULL;
prio = DevText2Prio(priority);
DevQueue(manager->devser,
&manager->TaskQueue[ptr],
prio,
ComTaskHandler,
ComTaskMatch,
NULL,
ComTaskInfo);
return ID;
}
/*-----------------------------------------------------------------*/
static int StartComTaskCmd(pSICSOBJ ccmd, SConnection * con,
Hdb * cmdNode, Hdb * par[], int nPar)
{
char *priority, *sendData;
long lID;
ComTaskManager *manni = NULL;
if(nPar < 2) {
SCWrite(con,"ERROR: need priorty and send data as parameters",eError);
return 0;
}
priority = ParText(cmdNode,"priority",nPar,"read");
sendData = ParText(cmdNode,"send",nPar,"None");
manni = (ComTaskManager *)ccmd->pPrivate;
lID = StartComTask(manni, priority, sendData);
SCPrintf(con,eValue,"comtaskid = %ld", lID);
return 1;
}
/*-----------------------------------------------------------------*/
static int ComTaskCompare(const void *data1, const void *data2)
{
pComTask task1 = (pComTask)data1;
pComTask task2 = (pComTask)data2;
assert(task1 && task2);
if(task1->ID < task2->ID){
return -1;
} else if(task1->ID > task2->ID){
return 1;
} else {
return 0;
}
}
/*------------------------------------------------------------------*/
ComTaskState GetComTaskState(ComTaskManager *manager, int comTaskID)
{
ComTask key;
ComTask *task = NULL;
key.ID = comTaskID;
task = bsearch(&key,manager->TaskQueue,manager->iPtr,
sizeof(ComTask), ComTaskCompare);
if(task == NULL){
return eUnknown;
} else {
return task->state;
}
}
/*-----------------------------------------------------------------*/
static int GetComTaskStateCmd(pSICSOBJ ccmd, SConnection * con,
Hdb * cmdNode, Hdb * par[], int nPar)
{
int lID;
ComTaskManager *manni = NULL;
ComTaskState state;
if(nPar < 1) {
SCWrite(con,"ERROR: need task ID parameter",eError);
return 0;
}
lID = par[0]->value.v.intValue;
manni = (ComTaskManager *)ccmd->pPrivate;
state = GetComTaskState(manni,lID);
SCPrintf(con,eValue,"%d = %s", lID, StateToText(state));
return 1;
}
/*------------------------------------------------------------------*/
const char *GetComTaskReply(ComTaskManager *manager,
int comTaskID, int *length)
{
ComTask key;
ComTask *task = NULL;
key.ID = comTaskID;
task = bsearch(&key,manager->TaskQueue,manager->iPtr,
sizeof(ComTask), ComTaskCompare);
if(task == NULL || task->state != eFinished){
return NULL;
} else {
return (const char *)task->replyData;
}
}
/*-----------------------------------------------------------------*/
static int GetComTaskReplyCmd(pSICSOBJ ccmd, SConnection * con,
Hdb * cmdNode, Hdb * par[], int nPar)
{
long lID;
int length;
ComTaskManager *manni = NULL;
const char *reply;
if(nPar < 1) {
SCWrite(con,"ERROR: need task ID parameter",eError);
return 0;
}
lID = par[0]->value.v.intValue;
manni = (ComTaskManager *)ccmd->pPrivate;
reply = GetComTaskReply(manni,lID,&length);
if(reply == NULL){
SCPrintf(con,eError,"No reply for %ld found", lID);
return 0;
} else {
SCPrintf(con,eValue,"%ld = %s", lID, reply);
}
return 1;
}
/*------------------------------------------------------------*/
void ComTaskWait(ComTaskManager *manager, int comTaskID)
{
ComTask key;
ComTask *task = NULL;
key.ID = comTaskID;
task = bsearch(&key,manager->TaskQueue,manager->iPtr,
sizeof(ComTask), ComTaskCompare);
if(task == NULL){
return;
}
/**
* Hmmmmhhhh. It might be better to start a SICS task here and
* wait for it terminate. But this would essentially also call
* TaskYield or, worse, Taskwait.
*/
while(task->state != eFinished){
TaskYield(pServ->pTasker);
}
}
/*-----------------------------------------------------------------*/
static int ComTaskWaitCmd(pSICSOBJ ccmd, SConnection * con,
Hdb * cmdNode, Hdb * par[], int nPar)
{
int lID;
ComTaskManager *manni = NULL;
if(nPar < 1) {
SCWrite(con,"ERROR: need task ID parameter",eError);
return 0;
}
lID = par[0]->value.v.intValue;
manni = (ComTaskManager *)ccmd->pPrivate;
ComTaskWait(manni,lID);
SCWrite(con,"Done",eValue);
return 1;
}
/*-----------------------------------------------------------------*/
static int ComTaskListAllCmd(pSICSOBJ ccmd, SConnection * con,
Hdb * cmdNode, Hdb * par[], int nPar)
{
ComTaskManager *manni = NULL;
pDynString list = NULL;
int i;
char *info;
manni = (ComTaskManager *)ccmd->pPrivate;
list = CreateDynString(256,256);
if(list == NULL){
SCWrite(con,"ERROR: out of memory listing ComTasks", eError);
return 0;
}
for(i = 0; i < manni->iPtr; i++){
info = ComTaskInfo((void *)&manni->TaskQueue[i]);
DynStringConcat(list,info);
DynStringConcatChar(list,'\n');
free(info);
}
SCWrite(con,GetCharArray(list),eValue);
DeleteDynString(list);
return 1;
}
/*--------------------------------------------------------------------*/
static int ComTaskListPendingCmd(pSICSOBJ ccmd, SConnection * con,
Hdb * cmdNode, Hdb * par[], int nPar)
{
ComTaskManager *manni = NULL;
pDynString list = NULL;
int i;
char *info;
manni = (ComTaskManager *)ccmd->pPrivate;
list = CreateDynString(256,256);
if(list == NULL){
SCWrite(con,"ERROR: out of memory listing ComTasks", eError);
return 0;
}
for(i = 0; i < manni->iPtr; i++){
if(manni->TaskQueue[i].state != eFinished){
info = ComTaskInfo((void *)&manni->TaskQueue[i]);
DynStringConcat(list,info);
DynStringConcatChar(list,'\n');
free(info);
}
}
SCWrite(con,GetCharArray(list),eValue);
DeleteDynString(list);
return 1;
}
/*-----------------------------------------------------------------*/
static ComTaskManager *MakeComTaskManager(int length)
{
ComTaskManager *manni = NULL;
manni = calloc(1, sizeof(ComTaskManager));
if(manni != NULL){
manni->halfQueueLength=length;
manni->TaskQueue = calloc(length*2,sizeof(ComTask));
if(manni->TaskQueue == NULL){
free(manni);
return NULL;
}
}
return manni;
}
/*-----------------------------------------------------------------*/
static void KillComTaskManager(void *data)
{
ComTaskManager *manni = (ComTaskManager *)data;
if(manni == NULL){
return;
}
if(manni->TaskQueue != NULL){
free(manni->TaskQueue);
}
KillComTaskData(manni->TaskQueue,manni->halfQueueLength*2);
free(manni);
}
/*-----------------------------------------------------------------*/
int SctMakeComTask(SConnection * con, SicsInterp * sics,
void *object, int argc, char *argv[])
{
SICSOBJ *ccmd;
pHdb cmd = NULL;
ComTaskManager *manni = NULL;
int length = 64;
if (argc < 3) {
SCWrite(con,"ERROR: need at least comtaskmanager name and sctcontroller as arguments",
eError);
return 0;
}
ccmd = (SICSOBJ *)FindCommandData(sics,argv[2],"SctController");
if(ccmd == NULL){
SCPrintf(con,eError,"ERROR: SctController %s not found",argv[2]);
return 0;
}
if(argc > 3) {
length = atoi(argv[3]);
}
manni = MakeComTaskManager(length);
if(manni == NULL){
SCWrite(con,"ERROR: out of memory allocating ComTaskmanager",eError);
return 0;
}
manni->devser = SctGetDevser(ccmd);
assert(manni->devser != NULL);
ccmd = MakeSICSOBJv(argv[1], "SctComTask", HIPNONE, usSpy);
assert(ccmd);
ccmd->pPrivate = manni;
ccmd->KillPrivate = KillComTaskManager;
AddCommand(pServ->pSics, argv[1], InterInvokeSICSOBJ, KillSICSOBJ, ccmd);
RegisterSICSOBJKillCmd(ccmd, argv[1]);
SetDescriptorKey(ccmd->pDes, "creationCommand", "0");
cmd = AddSICSHdbPar(ccmd->objectNode,
"start", usUser, MakeSICSFunc(StartComTaskCmd));
AddSICSHdbPar(cmd, "priority", usUser, MakeHdbText(""));
AddSICSHdbPar(cmd, "send", usUser, MakeHdbText("None"));
cmd = AddSICSHdbPar(ccmd->objectNode,
"state", usUser, MakeSICSFunc(GetComTaskStateCmd));
AddSICSHdbPar(cmd, "id", usUser, MakeHdbInt(0));
cmd = AddSICSHdbPar(ccmd->objectNode,
"reply", usUser, MakeSICSFunc(GetComTaskReplyCmd));
AddSICSHdbPar(cmd, "id", usUser, MakeHdbInt(0));
cmd = AddSICSHdbPar(ccmd->objectNode,
"wait", usUser, MakeSICSFunc(ComTaskWaitCmd));
AddSICSHdbPar(cmd, "id", usUser, MakeHdbInt(0));
cmd = AddSICSHdbPar(ccmd->objectNode,
"listall", usSpy, MakeSICSFunc(ComTaskListAllCmd));
cmd = AddSICSHdbPar(ccmd->objectNode,
"listpend", usSpy, MakeSICSFunc(ComTaskListPendingCmd));
return 1;
}
/*-------------------------------------------------------------------------------*/