518 lines
14 KiB
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;
|
|
}
|
|
/*-------------------------------------------------------------------------------*/
|