diff --git a/ascon.c b/ascon.c index b0d6c609..dcd23272 100644 --- a/ascon.c +++ b/ascon.c @@ -150,9 +150,11 @@ static void AsconConnect(Ascon * a) ret = CreateSocketAdress(&adr, a->hostport, port); *colon = ':'; if (ret == 0) { + a->ip = 0; AsconError(a, "bad host specification", 0); return; } + a->ip = adr.sin_addr.s_addr; oldopts = fcntl(a->fd, F_GETFL, 0); fcntl(a->fd, F_SETFL, oldopts | O_NONBLOCK); ret = @@ -853,6 +855,14 @@ char *AsconHostport(Ascon *a) return a->hostport; } +unsigned long AsconIP(Ascon *a) +{ + if (a==NULL) { + return 0; + } + return a->ip; +} + double AsconGetSetTimeout(Ascon *a, double timeout, int setmode) { if (setmode) { a->timeout = timeout; diff --git a/ascon.h b/ascon.h index 2571fd8c..9d457da6 100644 --- a/ascon.h +++ b/ascon.h @@ -109,6 +109,13 @@ int AsconLastState(Ascon *a); */ char *AsconHostport(Ascon *a); +/** + * \brief return IP address + * \param a The Ascon to query + * \return the IP address + */ +unsigned long AsconIP(Ascon *a); + /** * \brief set or get timeout * \param a the Ascon diff --git a/ascon.i b/ascon.i index 9cadeb95..f4da9ad9 100644 --- a/ascon.i +++ b/ascon.i @@ -65,6 +65,7 @@ struct Ascon { char *sendTerminator; /**< terminator for sending messages */ char *replyTerminator; /**< (std) terminator list for reply. NULL is the special case CR, LF or CR/LF */ char *hostport; /**< host:port to connect */ + in_addr_t ip; /**< the ip address */ pDynString errmsg; /**< error message */ double start; /**< unix time when read was started */ int noResponse; /**< no response expected */ diff --git a/binprot.c b/binprot.c index 9313230d..22dbfca4 100644 --- a/binprot.c +++ b/binprot.c @@ -72,9 +72,12 @@ * chksum-crc: simple 8bit checksum */ +#define SIMULATE_ERROR 0 +static int simulate_error = 0; + typedef enum {intType, hexType, floatType, strType, skipType, codeType, checkType, crcType, - dumpType, endType, errorType} BinDataType; // items on this line stay at end + dumpType, endType, errorType} BinDataType; /* items on this line stay at end */ typedef enum {modbusCrc, kellerCrc, rsportCrc, syconCrc, chksumCrc} CrcAlgorithm; @@ -83,7 +86,8 @@ typedef struct { CrcAlgorithm algo; int crcStart; pDynString inp; - char *nextFmt; // position of next format token + char *nextFmt; /* position of next format token */ + char *readFmt; /* position of first read format */ pDynString result; int expectedChars; BinDataType type; @@ -94,6 +98,8 @@ typedef struct { int syconState; unsigned char chksum; pDynString data; + int try_again_count; + int try_again_max; } BinPrivate; #define MAXDUMP 999 @@ -383,6 +389,15 @@ void BinShuffle(char *input, int order) { break; } } + +/*----------------------------------------------------------------------------*/ +void BinResetWrite(Ascon *a) { + BinPrivate *p = a->private; + p->nextFmt = p->readFmt; + a->wrPos = 0; + a->lineCount = 0; + a->state = AsconWriting; +} /*----------------------------------------------------------------------------*/ int BinHandler(Ascon *a) { int res; @@ -408,6 +423,7 @@ int BinHandler(Ascon *a) { /* exchange buffers */ dyn = p->inp; p->inp = a->wrBuffer; + p->try_again_count = p->try_again_max; a->wrBuffer = dyn; DynStringClear(dyn); str = GetCharArray(p->inp); @@ -507,10 +523,10 @@ int BinHandler(Ascon *a) { l += 4; break; case strType: - ls = strlen(item); - if (ls > size) { - item[size] = '\0'; - } + ls = strlen(item); + if (ls > size) { + item[size] = '\0'; + } DynStringConcat(dyn, item); if (size == NUL_TERMINATED) { DynStringConcatChar(dyn, 0); @@ -520,7 +536,7 @@ int BinHandler(Ascon *a) { DynStringConcatChar(dyn, 0); } l += size; - } + } break; } } @@ -530,7 +546,8 @@ int BinHandler(Ascon *a) { BinToSycon(dyn); } - p->nextFmt = str + pos; + p->readFmt = str + pos; + p->nextFmt = p->readFmt; p->type = hexType; /* initialize to anything < dumpType */ do { res = BinReadItem(a); @@ -540,15 +557,11 @@ int BinHandler(Ascon *a) { return 1; } } while (res == 1); - p->nextFmt = str + pos; - a->wrPos = 0; + BinResetWrite(a); if (GetDynStringLength(a->wrBuffer) == 0) { /* prevent to swallow "garbage" */ a->state = AsconWriteDone; - } else { - a->state = AsconWriting; } - a->lineCount = 0; return 1; case AsconReadStart: DynStringClear(p->result); @@ -558,10 +571,16 @@ int BinHandler(Ascon *a) { break; case AsconReading: res = AsconBaseHandler(a); + if (a->state == AsconTimeout && p->try_again_count > 0) { + p->try_again_count--; + DynStringCopy(a->errmsg, "timeout"); + BinResetWrite(a); + return 0; + } if (res == 0) { if (GetDynStringLength(a->rdBuffer) == 0) { /* wait for the first byte - or timeout */ - return res; + return 0; } if (p->type >= dumpType) { l = GetDynStringLength(a->rdBuffer); @@ -578,15 +597,23 @@ int BinHandler(Ascon *a) { DynStringConcat(a->errmsg, GetCharArray(p->result)); a->state = AsconFailed; } else { - a->state = AsconReadDone; + if (p->try_again_count != p->try_again_max) { + Log(INFO, "com", "sock0:recovered from (%s) after: %s", GetCharArray(a->errmsg), GetCharArray(p->inp)); + } + a->state = AsconReadDone; } /* exchange buffers */ dyn = a->rdBuffer; a->rdBuffer = p->result; p->result = dyn; + if (a->state == AsconFailed && p->try_again_count > 0) { + p->try_again_count--; + BinResetWrite(a); + return 0; + } return 1; } - return res; + return 0; } if (p->crcAlgorithm == syconCrc) { switch (p->syconState) { @@ -745,13 +772,23 @@ int BinHandler(Ascon *a) { p->expectedChars--; if ((unsigned char)a->lastChar != p->iValue) { i = GetDynStringLength(a->rdBuffer) - 1; - snprintf(item, sizeof item, "BINERR: response[%d]==%2.2x != %2.2x ", i, (unsigned char)a->lastChar, (unsigned char)p->iValue); + snprintf(item, sizeof item, "BINERR: [%d]==%2.2x != %2.2x ", i, (unsigned char)a->lastChar, (unsigned char)p->iValue); DynStringCopy(a->errmsg, item); p->type = errorType; p->dumpFrom = 0; /* skip to end */ p->nextFmt = ""; } +#if SIMULATE_ERROR > 0 + if (p->try_again_max > 0 && simulate_error++ > SIMULATE_ERROR) { + DynStringCopy(a->errmsg, "simulate "); + p->type = errorType; + p->dumpFrom = 0; + /* skip to end */ + p->nextFmt = ""; + simulate_error = 0; + } +#endif } if (p->expectedChars <= 0) { BinReadItem(a); @@ -773,6 +810,7 @@ static int BinInit(Ascon * a, SConnection * con, int argc, char *argv[]) { BinPrivate *p; +/* args: 2 , 3 , 4 /* argv[2] may be modbus-crc (default), keller-crc, rsport-crc or sycon-crc */ if (argc < 2) { @@ -807,6 +845,12 @@ static int BinInit(Ascon * a, SConnection * con, int argc, char *argv[]) } else { a->timeout = 2.0; /* sec */ } + if (argc > 4) { + p->try_again_max = atoi(argv[4]); + } else { + p->try_again_max = 0; + } + return 1; } diff --git a/devser.c b/devser.c index 2baf4755..e1d58946 100644 --- a/devser.c +++ b/devser.c @@ -580,6 +580,10 @@ char *DevHostport(DevSer *devser) { return AsconHostport(devser->ascon); } +unsigned long DevIP(DevSer *devser) { + return AsconIP(devser->ascon); +} + char *DevStatus(DevSer *devser) { char *str, *pos; static char buf[64]; diff --git a/devser.h b/devser.h index b2e48c93..9ea76805 100644 --- a/devser.h +++ b/devser.h @@ -203,6 +203,13 @@ void DevAsconStatistics(DevSer *self, double *avg, */ char *DevHostport(DevSer *devser); +/** + * \brief return IP address + * \param a The device serializer to query + * \return the IP address + */ +unsigned long DevIP(DevSer *devser); + /** * \brief return status of device server ("offline", "unconnected", "") * \param devser The device serializer to query diff --git a/drive.c b/drive.c index 563de10a..74ecc2f3 100644 --- a/drive.c +++ b/drive.c @@ -121,17 +121,17 @@ int Drive(SConnection * pCon, SicsInterp * pInter, char *name, float fNew) if (iRet == DEVINT) { if (SCGetInterrupt(pCon) == eAbortOperation) { SCSetInterrupt(pCon, eContinue); - snprintf(pBueffel, 511, "Driving %s aborted at %9.3f", name, fPos); + snprintf(pBueffel, 511, "Driving %s aborted at %.6g", name, fPos); SCWrite(pCon, pBueffel, eError); } return 0; } else if (iRet == DEVDONE) { - snprintf(pBueffel, 511, "Driving %s to %9.3f done", name, fPos); + snprintf(pBueffel, 511, "Driving %s to %.6g done", name, fPos); SCWrite(pCon, pBueffel, eValue); return 1; } else { snprintf(pBueffel, 511, - "Driving %s finished with problems, position: %9.3f", name, + "Driving %s finished with problems, position: %.6g", name, fPos); SCWrite(pCon, pBueffel, eValue); return 1; @@ -341,7 +341,7 @@ int DriveWrapper(SConnection * pCon, SicsInterp * pSics, void *pData, /* print positions */ for (i = 1; i < argc; i += 2) { - snprintf(pBueffel,511, "New %s position: %9.3f", argv[i], + snprintf(pBueffel,511, "New %s position: %.6g", argv[i], findPosition(pSics, pCon, argv[i])); SCWrite(pCon, pBueffel, eValue); } diff --git a/loglisten.c b/loglisten.c index b77542cd..3de67bb2 100644 --- a/loglisten.c +++ b/loglisten.c @@ -133,13 +133,19 @@ int AnalyzeCommand(char *command) { Tcl_SplitList(NULL, command, &argc, &argv); arg0 = 0; - if (argc > 0 && strcmp(argv[0], "fulltransact") == 0) { + if (argc > 0 && (strcmp(argv[0], "fulltransact") == 0 + || strcmp(argv[0], "transact") == 0)) { arg0 = 1; } if (argc <= arg0 + 1) { writable = 0; /* single word -> guess read only */ goto Free; } + if (strcmp(argv[arg0], "sicsdescriptor") == 0 + || strcmp(argv[arg0], "_tcl") == 0) { + writable = 0; + goto Free; + } pDes = FindCommandDescriptor(pServ->pSics, argv[arg0]); if (pDes == NULL) goto Free; if (pDes->parNode == NULL && GetDescriptorKey(pDes, "pardef") == NULL) goto Free; diff --git a/logsetup.c b/logsetup.c index aceb023e..c5da6a9d 100644 --- a/logsetup.c +++ b/logsetup.c @@ -31,7 +31,8 @@ static hdbCallbackReturn LoggerUpdateCallback(pHdb node, value = *(mm->v); time(&now); - if (now > LoggerLastTime(logger)) { /* never write more than once per second */ + /* testwise >= */ + if (now >= LoggerLastTime(logger)) { /* never write more than once per second */ if (GetHdbProp(node, "geterror") == NULL) { str = formatValue(value, node); LoggerWrite(logger, time(NULL), LoggerPeriod(logger), GetCharArray(str)); diff --git a/make_gen b/make_gen index f6a017c3..5674ddd7 100644 --- a/make_gen +++ b/make_gen @@ -45,7 +45,8 @@ SOBJ = network.o ifile.o conman.o SCinter.o splitter.o passwd.o \ singlenb.o simindex.o simidx.o uselect.o singletas.o motorsec.o \ rwpuffer.o asynnet.o background.o countersec.o hdbtable.o velosec.o \ histmemsec.o sansbc.o sicsutil.o strlutil.o genbinprot.o trace.o\ - singlebinb.o taskobj.o sctcomtask.o tasmono.o multicountersec.o lscprot.o \ + singlebinb.o taskobj.o sctcomtask.o tasmono.o multicountersec.o\ + lscprot.o secopprot.o\ messagepipe.o sicsget.o remoteobject.o pmacprot.o charbychar.o binprot.o \ cnvrt.o tclClock.o tclDate.o tclUnixTime.o stack_trace.o logv2.o outcode.o diff --git a/ofac.c b/ofac.c index 2f6d6ee9..56a1f8d2 100644 --- a/ofac.c +++ b/ofac.c @@ -52,6 +52,7 @@ static void InitGeneral(void) INIT(AddPMACProtocoll); INIT(AddCharByCharProtocoll); INIT(AddLscProtocol); + INIT(AddSecopProtocol); INIT(MakeTrace); INIT(InitTaskOBJ); INIT(RemoteObjectInit); @@ -121,6 +122,7 @@ static void InitIniCommands(SicsInterp * pInter) PCMD("SICSType", SICSType); PCMD("Sics_Exitus", SicsExit); PCMD("silent", SICSSilent); + PCMD("json2tcl", SICSjson2tcl); PCMD("status", UserStatus); PCMD("TclReplaceDrivable", TclReplaceDrivable); PCMD("transact", TransactAction); diff --git a/script.c b/script.c index 73a5303a..df584b58 100644 --- a/script.c +++ b/script.c @@ -43,7 +43,6 @@ #include #include #include -#include #include "fortify.h" #include "sics.h" #include "fupa.h" @@ -579,3 +578,66 @@ int SICSSilent(SConnection *pCon, SicsInterp *pSics, void *pData, free(cmd); return 1; } +/*------------------------------------------------------------------------*/ +Tcl_Obj *json2tcl(struct json_object *jobj) { + char buf[100]; + int i, len; + Tcl_Obj **list; + Tcl_Obj *dict, *kobj, *vobj; + Tcl_Obj *val; + const char *key; + const char *str; + + switch (json_object_get_type(jobj)) { + case json_type_boolean: + return Tcl_NewBooleanObj(json_object_get_boolean(jobj)); + case json_type_int: + return Tcl_NewLongObj(json_object_get_int(jobj)); + case json_type_double: + return Tcl_NewDoubleObj(json_object_get_double(jobj)); + case json_type_string: + str = json_object_get_string(jobj); + return Tcl_NewStringObj((char *)str, json_object_get_string_len(jobj)); + case json_type_array: + len = json_object_array_length(jobj); + list = calloc(sizeof *list, len); + for (i=0; i", eError); + return 0; + } + jobj = json_tokener_parse(argv[1]); + if (!jobj) { + SCWrite(pCon, "ERROR: no valid JSON", eError); + return 0; + } + /* the result is written only to the interpreter, no output to the client */ + Tcl_SetObjResult(pSics->pTcl, json2tcl(jobj)); + json_object_put(jobj); + return 1; +} diff --git a/script.h b/script.h index 82859058..e7ea5c01 100644 --- a/script.h +++ b/script.h @@ -10,6 +10,9 @@ #ifndef SICSSCRIPT #define SICSSCRIPT +#include +#include + /* -------------------------- Interrupts -----------------------------------*/ int GetSICSInterrupt(SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]); @@ -51,4 +54,9 @@ int SICSDescriptor(SConnection * pCon, SicsInterp * pSics, void *pData, /*----------------------------------------------------------------------*/ int SICSSilent(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); +/*----------------------------------------------------------------------*/ +Tcl_Obj *json2tcl(struct json_object *jobj); +/*----------------------------------------------------------------------*/ +int SICSjson2tcl(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); #endif diff --git a/scriptcontext.c b/scriptcontext.c index cdc9545f..dcb38896 100644 --- a/scriptcontext.c +++ b/scriptcontext.c @@ -283,7 +283,11 @@ int SctCommand(SConnection * con, SicsInterp * sics, void *object, if (argc == 3) { node = FindHdbNode(NULL, argv[2], con); } - GetHdbPath(node->mama, value, sizeof value); + if (node) { + GetHdbPath(node->mama, value, sizeof value); + } else { + value[0] = 0; + } /* returns an empty string on error */ SCWrite(con, value, eValue); return 1; @@ -1733,6 +1737,22 @@ static int SctHostport(pSICSOBJ ccmd, SConnection * con, return 1; } +static int SctIpAddress(pSICSOBJ ccmd, SConnection * con, + Hdb * cmdNode, Hdb * par[], int nPar) +{ + SctController *c; + uint32_t ip; + + c = (SctController *) ccmd->pPrivate; + ip = DevIP(c->devser); + SCPrintf(con, eValue, "%d.%d.%d.%d", + (int)(ip & 0xff), + (int)((ip >> 8) & 0xff), + (int)((ip >> 16) & 0xff), + (int)((ip >> 24) & 0xff)); + return 1; +} + static int SctTimeout(pSICSOBJ ccmd, SConnection * con, Hdb * cmdNode, Hdb * par[], int nPar) { @@ -1909,7 +1929,7 @@ static int SctMakeController(SConnection * con, SicsInterp * sics, assert(controller); controller->debugConn = NULL; - ccmd = MakeSICSOBJv(objName, "SctController", HIPNONE, usSpy); + ccmd = MakeSICSOBJv(objName, "SctController", HIPTEXT, usSpy); controller->node = ccmd->objectNode; controller->conn = SCCreateDummyConnection(pServ->pSics); @@ -2006,6 +2026,9 @@ static int SctMakeController(SConnection * con, SicsInterp * sics, cmd = AddSICSHdbPar(controller->node, "hostport", usSpy, MakeSICSFunc(SctHostport)); + cmd = AddSICSHdbPar(controller->node, + "ip", usSpy, MakeSICSFunc(SctIpAddress)); + cmd = AddSICSHdbPar(controller->node, "status", usSpy, MakeSICSFunc(SctStatus)); diff --git a/secopprot.c b/secopprot.c new file mode 100644 index 00000000..a141872c --- /dev/null +++ b/secopprot.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include "ascon.h" +#include "ascon.i" +#include "script.h" + +/* + * protocol for SECoP + * + * Markus Zolliker June 2017 + */ + +/*----------------------------------------------------------------------------*/ +int SecopProtHandler(Ascon *a) { + int ret; + char *result; + char *space; + char *json; + Tcl_Obj *tcllist[3], *tclobj; + int l; + struct json_object *jobj; + + switch (a->state) { + case AsconWriting: + /* do not skip garbage (might be a message!) */ + l = GetDynStringLength(a->wrBuffer) - a->wrPos; + ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, l); + if (ret < 0) { + AsconError(a, "ASC4", errno); /* sets state to AsconFailed */ + } else { + a->wrPos += ret; + if (a->wrPos >= GetDynStringLength(a->wrBuffer)) { + a->state = AsconWriteDone; + } + } + return 1; + case AsconWriteStart: + if (GetDynStringLength(a->wrBuffer) == 0) { + /* do not send empty message */ + a->state = AsconWriteDone; + return 1; + } + break; + } + ret = AsconStdHandler(a); + switch (a->state) { + case AsconReading: + if (ret) { + if (a->timeout < 1) { + /* extend timeout to 1 sec during a message */ + a->start = DoubleTime() + 1 - a->timeout; + } + } + break; + case AsconReadDone: + result = GetCharArray(a->rdBuffer); + space = strchr(result, ' '); + if (space) { + json = strchr(space+1, ' '); + if (json) { + json++; + jobj = json_tokener_parse(json); + if (jobj) { + tcllist[0] = Tcl_NewStringObj(result, space - result); + tcllist[1] = Tcl_NewStringObj(space + 1, json - space - 2); + tcllist[2] = json2tcl(jobj); + tclobj = Tcl_NewListObj(3, tcllist); + DynStringCopy(a->rdBuffer, Tcl_GetString(tclobj)); + json_object_put(jobj); /* free jobj */ + } + } + } + } + return ret; +} + +/*----------------------------------------------------------------------------*/ +void AddSecopProtocol() { + static AsconProtocol secopprot; + secopprot.name = "SECoP"; + secopprot.handler = SecopProtHandler; + secopprot.init = AsconStdInit; + AsconInsertProtocol(&secopprot); +}