/*-------------------------------------------------------------------------- S P S Implementation file for the SICS module handling Siemens SPS controllers at SinQ. copyright: see copyright.h I cannot imagine that somebody wants this. It is highly special for SINQ. It handles a protocoll tailor made by Siemens for Sinq. I cannot imagine that somebody wants something from Siemens anyway. Mark Koennnecke, Juli 1998 Feature added: bipa command, Mark Koennecke, May 2009 Reworked to use rs232controller rather then the older serialport code Mark Koennecke, November 2009 ---------------------------------------------------------------------------*/ #include #include #include #include #include "sics.h" #include "rs232controller.h" #include "sics.h" #include "lld.h" #include "bit.h" #include "sps.i" #include "sps.h" /*--------------------- some internal defines ----------------------------*/ #define SPSOFFLINE -10 #define COMMDEAD -11 #define COMMRECONNECT -12 #define COMMTMO -13 /*-------------------------------------------------------------------------*/ static float SimRandom(void) { float fVal; fVal = ((float) rand() / (float) RAND_MAX) * 100.0; return fVal; } /*--------------------- static functions ---------------------------------*/ static int init(pSPS self) { int iRet; assert(self); assert(self->pHost); prs232 rs232; /* check if in simulation mode */ if (self->iMode) return 1; rs232 = createRS232(self->pHost,self->iPort); self->pData = rs232; iRet = initRS232(rs232); if(iRet != 1){ self->iLastError = COMMDEAD; return 0; } setRS232SendTerminator(rs232,"\r\n"); setRS232ReplyTerminator(rs232,"\n"); setRS232Timeout(rs232,20000); /* setRS232Debug(rs232,1); */ return 1; } /*-------------------------------------------------------------------------*/ static int SPSCommand(pSPS self, char *pCommand, char *pReply, int iReplyLen) { int iRet, i; char pError[132]; prs232 rs232; assert(self); assert(pCommand); assert(pReply); rs232 = (prs232)self->pData; /* try at least three times to get the command through before giving up */ for (i = 0; i < 3; i++) { if(availableRS232(rs232)){ memset(pReply,0, iReplyLen); iRet = iReplyLen; readRS232(rs232, pReply, &iRet); /* printf("Dirt on the SPS cable: %d bytes, text = %s\n", iRet, pReply); */ } iRet = transactRS232(rs232, pCommand,strlen(pCommand), pReply, iReplyLen); if (iRet < 0) { switch (iRet) { case TIMEOUT: self->iLastError = COMMTMO; continue; break; default: self->iLastError = iRet; getRS232Error(iRet,pError,131); Log(ERROR,"dev", "SPS-TROUBLE: %s", pError); closeRS232(rs232); iRet = initRS232(rs232); if (iRet == 0) { return 0; } continue; break; } } return 1; } return 0; } /*--------------------------------------------------------------------------*/ int SPSSetButton(pSPS self, SConnection * pCon, int iByte, int iBit) { Permission perm; char pCommand[30]; char pBueffel[256]; int iRet, iTest, i; assert(self); assert(pCon); /* first check permissions */ iRet = LLDnodePtr2First(self->lPermissions); iTest = 0; while (iRet != 0) { LLDnodeDataTo(self->lPermissions, &perm); if (perm.iByte == iByte) { if (perm.iBit == iBit) { iTest = SCMatchRights(pCon, perm.iPrivilege); break; } } iRet = LLDnodePtr2Next(self->lPermissions); } if (!iTest) { sprintf(pBueffel, "ERROR: you have no permission to flip %d.%d", iByte, iBit); SCWrite(pCon, pBueffel, eError); return 0; } /* if in sim mode, return true */ if (self->iMode) { return 1; } /* format command and send a command to set a bit through the drain in order to simulate a button press */ sprintf(pCommand, "S%3.3d%1.1d", iByte, iBit); iRet = SPSCommand(self, pCommand, pBueffel, 255); if (!iRet) { SCWrite(pCon, pBueffel, eError); SCWrite(pCon, "ERROR: Sending flip command failed", eError); return 0; } return 1; } /*--------------------------------------------------------------------------*/ int SPSGetStatus(pSPS self, int iStatus, int *iSet) { int iRet, i, i2, iVal; unsigned char pByte[16]; char pBueffel[256], pNum[10]; char *pPtr; unsigned char cBit, cMask; assert(self); if ((iStatus < 0) || (iStatus >= 128)) { return -1; } /* if in simulation mode, return 1 */ if (self->iMode) { *iSet = 1; return 1; } /* send an R command down to the SPS */ iRet = SPSCommand(self, "R", pBueffel, 255); if (!iRet) { return 0; } /* decode the reply into the Byte array */ pPtr = strchr(pBueffel, 'R'); if (pPtr == NULL) { return -2; } pPtr++; for (i = 0; i < 16; i++) { /* skip the whitespace */ pPtr++; pNum[0] = *pPtr; pPtr++; pNum[1] = *pPtr; pPtr++; pNum[2] = *pPtr; pPtr++; pNum[3] = '\0'; pByte[i] = atoi(pNum); } /* test the bit */ i = iStatus / 8; cBit = pByte[i]; i2 = iStatus % 8; cMask = 1 << i2; /* printf("Byte %d, Value %d, Bit %d, Value: %d\n", i,cBit, i2, (cBit & cMask)); */ if ((cBit & cMask) > 0) { *iSet = 1; } else { *iSet = 0; } return 1; } /*------------------------------------------------------------------------*/ static void byteToString(unsigned char byte, char txt[9]) { int i; for(i = 0; i < 8; i++){ if(byte & (1 << i) ){ txt[i] = '1'; } else { txt[i] = '0'; } } } /*------------------------------------------------------------------------*/ static int SPSBitPattern(pSPS self, SConnection *pCon) { int iRet, i; char pBueffel[256], *pPtr = NULL, pNum[10], pText[9]; unsigned char byte; pDynString result = NULL; if(self->iMode){ SCWrite(pCon,"00000 0001000 00001000 00010000", eValue); return 1; } /* send an R command down to the SPS */ iRet = SPSCommand(self, "R", pBueffel, 255); if (!iRet) { SCWrite(pCon,"ERROR: communication error in SPSBitPattern", eError); return 0; } result = CreateDynString(128,128); if(result == NULL){ SCWrite(pCon,"ERROR: out of memory in SPSBitpattern", eError); return 0; } /* decode the reply into the Byte array */ pPtr = strchr(pBueffel, 'R'); if (pPtr == NULL) { return -2; } pPtr++; for (i = 0; i < 16; i++) { /* skip the whitespace */ pPtr++; pNum[0] = *pPtr; pPtr++; pNum[1] = *pPtr; pPtr++; pNum[2] = *pPtr; pPtr++; pNum[3] = '\0'; byte = atoi(pNum); memset(pText,0,9*sizeof(char)); byteToString(byte, pText); DynStringConcat(result, pText); if(((i+1) % 4) == 0){ DynStringConcatChar(result,'\n'); } else { DynStringConcatChar(result,' '); } } SCWrite(pCon,GetCharArray(result), eValue); DeleteDynString(result); return 1; } /*------------------------------------------------------------------------- This is a special feature for SANS at SINQ. SANS has a collimator and the length of the collimator can only be deduced from the SPS status message by checking each of nine segments separately. For efficiency this is coded into a special function for SANS. This also needs a special function for testing bits. Mark Koennecke, April 1999 */ static int TestBit(unsigned char cByte, int iBit) { unsigned char cMask; assert(iBit >= 0); assert(iBit < 8); cMask = 1 << iBit; if ((cMask & cByte) > 0) { return 1; } else { return 0; } } /*-----------------------------------------------------------------------*/ int SPSGetSANS(pSPS self, float *fLength) { int iRet, i, i2, iVal; unsigned char pByte[16]; char pBueffel[256], pNum[10]; char *pPtr; unsigned char cBit, cMask; float fLang; assert(self); /* if in simulation mode, return a random number */ if (self->iMode) { *fLength = SimRandom(); return 1; } /* send an R command down to the SPS */ iRet = SPSCommand(self, "R", pBueffel, 255); if (!iRet) { return 0; } /* decode the reply into the Byte array */ pPtr = strchr(pBueffel, 'R'); if (pPtr == NULL) { return -2; } pPtr++; for (i = 0; i < 16; i++) { /* skip the whitespace */ pPtr++; pNum[0] = *pPtr; pPtr++; pNum[1] = *pPtr; pPtr++; pNum[2] = *pPtr; pPtr++; pNum[3] = '\0'; pByte[i] = atoi(pNum); } fLang = 189.; if (TestBit(pByte[0], 1) > 0) { fLang = 18.; } /* coll 1 15m */ if (TestBit(pByte[0], 3) > 0) { fLang = 15.; } /* coll 2 11 m */ if (TestBit(pByte[0], 7) > 0) { fLang = 11.; } /* coll 3 8 m */ if (TestBit(pByte[1], 3) > 0) { fLang -= 3.; } /* coll 4 6m */ if (TestBit(pByte[1], 7) > 0) { fLang -= 2.; } /* coll 5 4.5 m */ if (TestBit(pByte[2], 3) > 0) { fLang -= 1.5; } /* coll 6 3 m */ if (TestBit(pByte[2], 7) > 0) { fLang -= 1.5; } /* coll 7 */ if (TestBit(pByte[3], 3) > 0) { fLang -= 1.; } /* coll 8 */ if (TestBit(pByte[3], 6) > 0) { fLang -= 1.; } if (TestBit(pByte[3], 7) > 0) { fLang -= 0.6; } *fLength = fLang; return 1; } /*--------------------------------------------------------------------------*/ int SPSGetADC(pSPS self, int iWhich, int *iValue) { int iADC[8]; int i, iRet; char *pPtr, pBueffel[256], pNum[10]; assert(self); /* check iWhich */ if ((iWhich < 0) || (iWhich > 7)) { return 0; } /* give a random number in simulation mode */ if (self->iMode) { *iValue = (int) SimRandom(); return 1; } /* send an A command to the box */ iRet = SPSCommand(self, "A", pBueffel, 255); if (!iRet) { return 0; } /* decode the result */ pPtr = strchr(pBueffel, 'A'); if (pPtr == NULL) { /* a silly answer was returned */ Log(ERROR,"dev","%s:%s", "SPS: Silly answer in SPSGetADC",pBueffel); return 0; } pPtr++; for (i = 0; i < 8; i++) { pPtr++; /* skip whitespace */ strncpy(pNum, pPtr, 5); /* strlcpy probably wrong here */ pNum[5] = '\0'; pPtr += 5; iADC[i] = atoi(pNum); } /* now give the value */ *iValue = iADC[iWhich]; return 1; } /*-----------------------------------------------------------------------*/ void SPSAddPermission(pSPS self, int iByte, int iBit, int iRights) { Permission perm; assert(self); perm.iByte = iByte; perm.iBit = iBit; perm.iPrivilege = iRights; LLDnodeAppendFrom(self->lPermissions, &perm); } /*-------------------------------------------------------------------------*/ void RemoveSPS(void *pData) { pSPS self = NULL; prs232 rs232 = NULL; self = (pSPS) pData; if (!self) return; rs232 = (prs232)self->pData; if (self->pHost) { free(self->pHost); } if(rs232 != NULL){ closeRS232(rs232); KillRS232(rs232); } if (self->pDes) { DeleteDescriptor(self->pDes); } LLDdelete(self->lPermissions); free(self); } /*---------------------------------------------------------------------------*/ int SPSFactory(SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]) { int iRet, iVal; pSPS pNew = NULL; char pBueffel[256]; assert(pCon); assert(pSics); /* we need at least two extra arguments: name and either host or sim */ if (argc < 3) { SCWrite(pCon, "ERROR: Insufficient number of arguments for installing SPS", eError); return 0; } /* allocate a new SPS */ pNew = (pSPS) malloc(sizeof(SPS)); if (!pNew) { SCWrite(pCon, "ERROR: no memory to allocate SPS", eError); return 0; } memset(pNew, 0, sizeof(SPS)); pNew->pDes = CreateDescriptor("SPS"); pNew->lPermissions = LLDcreate(sizeof(Permission)); if ((!pNew->pDes) || (pNew->lPermissions < 0)) { SCWrite(pCon, "ERROR: no memory to allocate SPS", eError); return 0; } pNew->pHost = strdup(argv[2]); /* check if we go for sim */ strtolower(argv[2]); if (strcmp(argv[2], "sim") == 0) { pNew->iMode = 1; } else { /* we are installing a real one and need tons more arguments */ if (argc < 5) { SCWrite(pCon, "ERROR: Insufficient number of arguments for installing SPS", eError); RemoveSPS(pNew); return 0; } /* get port number */ iRet = Tcl_GetInt(pSics->pTcl, argv[3], &iVal); if (iRet != TCL_OK) { snprintf(pBueffel,255, "ERROR: expected integer argument for port, got %s", argv[3]); SCWrite(pCon, pBueffel, eError); RemoveSPS(pNew); return 0; } pNew->iPort = iVal; /* get channel number */ iRet = Tcl_GetInt(pSics->pTcl, argv[4], &iVal); if (iRet != TCL_OK) { snprintf(pBueffel,255, "ERROR: expected integer argument for channel, got %s", argv[4]); SCWrite(pCon, pBueffel, eError); RemoveSPS(pNew); return 0; } pNew->iChannel = iVal; } /* initialise */ iRet = init(pNew); if (!iRet) { SCWrite(pCon, "ERROR: Failure to initialise SPS controller", eError); RemoveSPS(pNew); return 0; } /* install command */ iRet = AddCommand(pSics, argv[1], SPSAction, RemoveSPS, pNew); if (!iRet) { snprintf(pBueffel,255, "ERROR: duplicate SPS command %s NOT created", argv[1]); SCWrite(pCon, pBueffel, eError); RemoveSPS(pNew); } return 1; } /*-------------------------------------------------------------------------*/ int SPSAction(SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]) { pSPS self = NULL; char pBueffel[256]; int iByte, iBit, iSet; int iRet, i; float fLang; self = (pSPS) pData; assert(self); /* we need at least 3 arguments */ if (argc < 2) { snprintf(pBueffel,255, "ERROR: need at least two arguments to %s", argv[0]); SCWrite(pCon, pBueffel, eError); return 0; } /* do something according to key word */ strtolower(argv[1]); if (strcmp(argv[1], "push") == 0) { /* operate a button */ /* four arguments needed */ if (argc < 4) { snprintf(pBueffel,255, "ERROR: need at least two arguments to %s push", argv[0]); SCWrite(pCon, pBueffel, eError); return 0; } /* convert arguments */ iRet = Tcl_GetInt(pSics->pTcl, argv[2], &iByte); if (iRet != TCL_OK) { snprintf(pBueffel,255, "ERROR: expected integer argument for byte, got %s", argv[2]); SCWrite(pCon, pBueffel, eError); return 0; } iRet = Tcl_GetInt(pSics->pTcl, argv[3], &iBit); if (iRet != TCL_OK) { snprintf(pBueffel,255, "ERROR: expected integer argument for bit, got %s", argv[3]); SCWrite(pCon, pBueffel, eError); return 0; } /* try it */ iRet = SPSSetButton(self, pCon, iByte, iBit); if (!iRet) { SCWrite(pCon, "ERROR: Pushing button failed", eError); return 0; } else { SCSendOK(pCon); return 1; } } else if (strcmp(argv[1], "status") == 0) { /* status bits */ /* which bit ? */ if (argc < 3) { snprintf(pBueffel,255, "ERROR: need at least two arguments to %s push", argv[0]); SCWrite(pCon, pBueffel, eError); return 0; } iRet = Tcl_GetInt(pSics->pTcl, argv[2], &iByte); if (iRet != TCL_OK) { snprintf(pBueffel, 255, "ERROR: expected integer argument for bit, got %s", argv[2]); SCWrite(pCon, pBueffel, eError); return 0; } iRet = SPSGetStatus(self, iByte, &iSet); if (iRet <= 0) { snprintf(pBueffel,255, "ERROR: failed to read status bit %d", iByte); SCWrite(pCon, pBueffel, eError); return 0; } sprintf(pBueffel, "%s.status.%3.3d = %d", argv[0], iByte, iSet); SCWrite(pCon, pBueffel, eValue); return 1; } else if (strcmp(argv[1], "stat2") == 0) { /* status bits */ if (argc < 4) { snprintf(pBueffel,255, "ERROR: need at least two arguments to %s stat2", argv[0]); SCWrite(pCon, pBueffel, eError); return 0; } /* which bit ? */ iRet = Tcl_GetInt(pSics->pTcl, argv[2], &iByte); if (iRet != TCL_OK) { snprintf(pBueffel,255, "ERROR: expected integer argument for bit, got %s", argv[2]); SCWrite(pCon, pBueffel, eError); return 0; } iRet = Tcl_GetInt(pSics->pTcl, argv[3], &iBit); if (iRet != TCL_OK) { snprintf(pBueffel, 255, "ERROR: expected integer argument for bit, got %s", argv[3]); SCWrite(pCon, pBueffel, eError); return 0; } if ((iByte <= 0) || (iBit < 0) || (iBit > 7)) { SCWrite(pCon, "ERROR: arguments out of range for stat2", eError); return 0; } iRet = SPSGetStatus(self, ((iByte - 1) * 8 + iBit), &iSet); if (iRet <= 0) { sprintf(pBueffel, "ERROR: failed to read status bit %d", iByte); SCWrite(pCon, pBueffel, eError); return 0; } sprintf(pBueffel, "%s.status.%3.3d.%1.1d = %d", argv[0], iByte, iBit, iSet); SCWrite(pCon, pBueffel, eValue); return 1; } else if (strcmp(argv[1], "adc") == 0) { /* ADC values */ /* which ADC ? */ iRet = Tcl_GetInt(pSics->pTcl, argv[2], &iByte); if (iRet != TCL_OK) { snprintf(pBueffel,255, "ERROR: expected integer argument for ADC, got %s", argv[2]); SCWrite(pCon, pBueffel, eError); return 0; } iRet = SPSGetADC(self, iByte, &iSet); if (!iRet) { sprintf(pBueffel, "ERROR: failed to read ADC %d", iByte); SCWrite(pCon, pBueffel, eError); return 0; } sprintf(pBueffel, "%s.ADC.%1.1d = %d", argv[0], iByte, iSet); SCWrite(pCon, pBueffel, eValue); return 1; } else if (strcmp(argv[1], "perm") == 0) { /* only managers may set permissions */ if (!SCMatchRights(pCon, usMugger)) { SCWrite(pCon, "ERROR: Security: Only mangagers may configure SPS buttons", eError); return 0; } /* we need lots of parameters */ if (argc < 5) { snprintf(pBueffel,255, "ERROR: need at least three arguments to %s perm", argv[0]); SCWrite(pCon, pBueffel, eError); return 0; } /* convert arguments */ iRet = Tcl_GetInt(pSics->pTcl, argv[2], &iByte); if (iRet != TCL_OK) { snprintf(pBueffel,255, "ERROR: expected integer argument for byte, got %s", argv[2]); SCWrite(pCon, pBueffel, eError); return 0; } iRet = Tcl_GetInt(pSics->pTcl, argv[3], &iBit); if (iRet != TCL_OK) { snprintf(pBueffel,255, "ERROR: expected integer argument for bit, got %s", argv[3]); SCWrite(pCon, pBueffel, eError); return 0; } /* try to convert user code */ strtolower(argv[4]); iSet = 0; iSet = decodeSICSPriv(argv[4]); if (iSet < 0) { sprintf(pBueffel, "ERROR: %s is not valid user code", argv[4]); SCWrite(pCon, pBueffel, eError); return 0; } /* do it, finally */ SPSAddPermission(self, iByte, iBit, iSet); SCSendOK(pCon); return 1; } /* -------- sans collimator */ else if (strcmp(argv[1], "colli") == 0) { iRet = SPSGetSANS(self, &fLang); if (!iRet) { SCWrite(pCon, "ERROR: cannot read SANS collimator", eError); return 0; } else { sprintf(pBueffel, "SANS.collimator = %f", fLang); SCWrite(pCon, pBueffel, eValue); return 1; } } /* -------- bit pattern*/ else if (strcmp(argv[1], "bipa") == 0) { return SPSBitPattern(self,pCon); } sprintf(pBueffel, "ERROR: %s does not not understand subcommand %s", argv[0], argv[1]); SCWrite(pCon, pBueffel, eError); return 0; }