mirror of
https://github.com/netdata-be/libnodave
synced 2025-10-13 00:42:50 +08:00
7111 lines
198 KiB
C
7111 lines
198 KiB
C
/*
|
|
Part of Libnodave, a free communication libray for Siemens S7 200/300/400 via
|
|
the MPI adapter 6ES7 972-0CA22-0XAC
|
|
or MPI adapter 6ES7 972-0CA23-0XAC
|
|
or TS adapter 6ES7 972-0CA33-0XAC
|
|
or MPI adapter 6ES7 972-0CA11-0XAC,
|
|
IBH/MHJ-NetLink or CPs 243, 343 and 443
|
|
|
|
(C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2002..2005
|
|
|
|
S5 basic communication parts (C) Andrew Rostovtsew 2004.
|
|
|
|
Libnodave is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Library General Public License as published by
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
any later version.
|
|
|
|
Libnodave is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with Libnodave; see the file COPYING. If not, write to
|
|
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
#include "nodave.h"
|
|
#include <stdio.h>
|
|
#include "log2.h"
|
|
#include <string.h>
|
|
|
|
|
|
//#define DEBUG_CALLS // Define this and recompile to get parameters and results
|
|
// of each function call printed. I could have made this an
|
|
// option bit in daveDebug, but most applications will never,never
|
|
// need it. Still they would have to do the jumps...
|
|
// This option is just useful when developing bindings to new
|
|
// programming languages.
|
|
/**
|
|
Library specific:
|
|
**/
|
|
#ifdef LINUX
|
|
#define HAVE_UNISTD
|
|
#define HAVE_SELECT
|
|
#define DECL2
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_UNISTD
|
|
#include <unistd.h>
|
|
#define daveWriteFile(a,b,c,d) d=write(a,b,c)
|
|
#endif
|
|
|
|
#ifdef AVR
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
int daveDebug=0;
|
|
|
|
#ifdef BCCWIN
|
|
#include <winsock2.h>
|
|
#include "openS7online.h" // We can use the Siemens transport dlls only on Windows
|
|
|
|
void setTimeOut(daveInterface * di, int tmo) {
|
|
COMMTIMEOUTS cto;
|
|
#ifdef DEBUG_CALLS
|
|
LOG3("setTimeOut(di:%p, time:%d)\n", di,tmo);
|
|
FLUSH;
|
|
#endif
|
|
// if(di->fd.connectionType==daveSerialConnection) {
|
|
GetCommTimeouts(di->fd.rfd, &cto);
|
|
cto.ReadIntervalTimeout=0;
|
|
cto.ReadTotalTimeoutMultiplier=0;
|
|
cto.ReadTotalTimeoutConstant=tmo/1000;
|
|
SetCommTimeouts(di->fd.rfd,&cto);
|
|
// } else if(di->fd.connectionType==daveTcpConnection) {
|
|
// }
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef HAVE_SELECT
|
|
int DECL2 stdwrite(daveInterface * di, char * buffer, int length) {
|
|
if (daveDebug & daveDebugByte)
|
|
_daveDump("I send", (uc*)buffer, length);
|
|
return write(di->fd.wfd, buffer,length);
|
|
}
|
|
|
|
int DECL2 stdread(daveInterface * di, char * buffer, int length) {
|
|
fd_set FDS;
|
|
struct timeval t;
|
|
int i;
|
|
t.tv_sec = di->timeout / 1000000;
|
|
t.tv_usec = (di->timeout % 1000000);
|
|
FD_ZERO(&FDS);
|
|
FD_SET(di->fd.rfd, &FDS);
|
|
i=0;
|
|
if(select(di->fd.rfd + 1, &FDS, NULL, NULL, &t)>0) {
|
|
i=read(di->fd.rfd, buffer, length);
|
|
}
|
|
// if (daveDebug & daveDebugByte)
|
|
// _daveDump("got",buffer,i);
|
|
return i;
|
|
}
|
|
#endif
|
|
|
|
#ifdef BCCWIN
|
|
|
|
int DECL2 stdread(daveInterface * di,
|
|
char * buffer,
|
|
int length) {
|
|
unsigned long i;
|
|
ReadFile(di->fd.rfd, buffer, length, &i, NULL);
|
|
// if (daveDebug & daveDebugByte)
|
|
// _daveDump("got",buffer,i);
|
|
return i;
|
|
}
|
|
|
|
int DECL2 stdwrite(daveInterface * di, char * buffer, int length) {
|
|
unsigned long i;
|
|
if (daveDebug & daveDebugByte)
|
|
_daveDump("I send",buffer,length);
|
|
// EscapeCommFunction(di->fd.rfd, CLRRTS); // patch from Keith Harris. He says:
|
|
//******* this is what microwin does (needed for usb-serial)
|
|
WriteFile(di->fd.rfd, buffer, length, &i,NULL);
|
|
// patch from Andrea. He says:
|
|
// In this way the PC Adapter connected to CPU313C hangs waiting for RTS line before answering back.
|
|
// Added the following to regain answers:
|
|
// EscapeCommFunction(di->fd.rfd, SETRTS);
|
|
return i;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
Setup a new interface structure from an initialized serial interface's handle and a name.
|
|
*/
|
|
daveInterface * DECL2 daveNewInterface(_daveOSserialType nfd, char * nname, int localMPI, int protocol, int speed){
|
|
daveInterface * di=(daveInterface *) calloc(1, sizeof(daveInterface));
|
|
#ifdef DEBUG_CALLS
|
|
LOG7("daveNewInterface(fd.rfd:%d fd.wfd:%d name:%s local MPI:%d protocol:%d PB speed:%d)\n",
|
|
nfd.rfd,nfd.wfd,nname, localMPI, protocol, speed);
|
|
FLUSH;
|
|
#endif
|
|
if (di) {
|
|
// di->name=nname;
|
|
strncpy(di->realName,nname,20);
|
|
di->name=di->realName;
|
|
di->fd=nfd;
|
|
di->localMPI=localMPI;
|
|
di->protocol=protocol;
|
|
di->timeout=1000000; /* 1 second */
|
|
di->nextConnection=0x14;
|
|
di->speed=speed;
|
|
|
|
di->getResponse=_daveGetResponseISO_TCP;
|
|
di->ifread=stdread;
|
|
di->ifwrite=stdwrite;
|
|
di->initAdapter=_daveReturnOkDummy;
|
|
di->connectPLC=_daveReturnOkDummy2;
|
|
di->disconnectPLC=_daveReturnOkDummy2;
|
|
di->disconnectAdapter=_daveReturnOkDummy;
|
|
di->listReachablePartners=_daveListReachablePartnersDummy;
|
|
switch (protocol) {
|
|
case daveProtoMPI:
|
|
di->initAdapter=_daveInitAdapterMPI1;
|
|
di->connectPLC=_daveConnectPLCMPI1;
|
|
di->disconnectPLC=_daveDisconnectPLCMPI;
|
|
di->disconnectAdapter=_daveDisconnectAdapterMPI;
|
|
di->exchange=_daveExchangeMPI;
|
|
di->sendMessage=_daveSendMessageMPI;
|
|
di->getResponse=_daveGetResponseMPI;
|
|
di->listReachablePartners=_daveListReachablePartnersMPI;
|
|
break;
|
|
|
|
case daveProtoMPI2:
|
|
case daveProtoMPI4:
|
|
di->initAdapter=_daveInitAdapterMPI2;
|
|
di->connectPLC=_daveConnectPLCMPI2;
|
|
di->disconnectPLC=_daveDisconnectPLCMPI;
|
|
di->disconnectAdapter=_daveDisconnectAdapterMPI;
|
|
di->exchange=_daveExchangeMPI;
|
|
di->sendMessage=_daveSendMessageMPI;
|
|
di->getResponse=_daveGetResponseMPI;
|
|
di->listReachablePartners=_daveListReachablePartnersMPI;
|
|
di->nextConnection=0x3;
|
|
break;
|
|
|
|
case daveProtoMPI3:
|
|
di->initAdapter=_daveInitAdapterMPI3;
|
|
di->connectPLC=_daveConnectPLCMPI3;
|
|
di->disconnectPLC=_daveDisconnectPLCMPI3;
|
|
di->disconnectAdapter=_daveDisconnectAdapterMPI3;
|
|
di->exchange=_daveExchangeMPI3;
|
|
di->sendMessage=_daveSendMessageMPI3;
|
|
di->getResponse=_daveGetResponseMPI3;
|
|
di->listReachablePartners=_daveListReachablePartnersMPI3;
|
|
di->nextConnection=0x3;
|
|
break;
|
|
|
|
case daveProtoISOTCP:
|
|
case daveProtoISOTCP243:
|
|
case daveProtoISOTCPR: // routing over MPI network
|
|
di->getResponse=_daveGetResponseISO_TCP;
|
|
di->connectPLC=_daveConnectPLCTCP;
|
|
di->exchange=_daveExchangeTCP;
|
|
break;
|
|
|
|
case daveProtoPPI:
|
|
di->getResponse=_daveGetResponsePPI;
|
|
di->exchange=_daveExchangePPI;
|
|
di->connectPLC=_daveConnectPLCPPI;
|
|
di->timeout=150000; /* 0.15 seconds */
|
|
break;
|
|
case daveProtoMPI_IBH:
|
|
di->exchange=_daveExchangeIBH;
|
|
di->connectPLC=_daveConnectPLC_IBH;
|
|
di->disconnectPLC=_daveDisconnectPLC_IBH;
|
|
di->sendMessage=_daveSendMessageMPI_IBH;
|
|
di->getResponse=_daveGetResponseMPI_IBH;
|
|
di->listReachablePartners=_daveListReachablePartnersMPI_IBH;
|
|
break;
|
|
case daveProtoPPI_IBH:
|
|
di->exchange=_daveExchangePPI_IBH;
|
|
di->connectPLC=_daveConnectPLCPPI;
|
|
di->sendMessage=_daveSendMessageMPI_IBH;
|
|
di->getResponse=_daveGetResponsePPI_IBH;
|
|
di->listReachablePartners=_daveListReachablePartnersMPI_IBH;
|
|
break;
|
|
case daveProtoS7online:
|
|
di->exchange=_daveExchangeS7online;
|
|
di->connectPLC=_daveConnectPLCS7online;
|
|
di->sendMessage=_daveSendMessageS7online;
|
|
di->getResponse=_daveGetResponseS7online;
|
|
di->listReachablePartners=_daveListReachablePartnersS7online;
|
|
// di->disconnectAdapter=_daveDisconnectAdapterS7online;
|
|
break;
|
|
case daveProtoAS511:
|
|
di->connectPLC=_daveConnectPLCAS511;
|
|
di->disconnectPLC=_daveDisconnectPLCAS511;
|
|
di->exchange=_daveFakeExchangeAS511;
|
|
di->sendMessage=_daveFakeExchangeAS511;
|
|
break;
|
|
case daveProtoNLpro:
|
|
di->initAdapter=_daveInitAdapterNLpro;
|
|
di->connectPLC=_daveConnectPLCNLpro;
|
|
di->disconnectPLC=_daveDisconnectPLCNLpro;
|
|
di->disconnectAdapter=_daveDisconnectAdapterNLpro;
|
|
di->exchange=_daveExchangeNLpro;
|
|
di->sendMessage=_daveSendMessageNLpro;
|
|
di->getResponse=_daveGetResponseNLpro;
|
|
di->listReachablePartners=_daveListReachablePartnersNLpro;
|
|
break;
|
|
|
|
}
|
|
#ifdef BCCWIN
|
|
setTimeOut(di, di->timeout);
|
|
#endif
|
|
}
|
|
return di;
|
|
}
|
|
|
|
daveInterface * DECL2 davePascalNewInterface(_daveOSserialType* nfd, char * nname, int localMPI, int protocol, int speed){
|
|
#ifdef DEBUG_CALLS
|
|
LOG7("davePascalNewInterface(fd.rfd:%d fd.wfd:%d name:%s local MPI:%d protocol:%d PB speed:%d)\n",
|
|
nfd->rfd,nfd->wfd,nname, localMPI, protocol, speed);
|
|
FLUSH;
|
|
#endif
|
|
return daveNewInterface(*nfd,nname, localMPI, protocol, speed);
|
|
}
|
|
|
|
/*
|
|
debugging:
|
|
set debug level by setting variable daveDebug. Debugging is split into
|
|
several topics. Output goes to stderr.
|
|
The file descriptor is written after the module name, so you may
|
|
distinguish messages from multiple connections.
|
|
|
|
naming: all stuff begins with dave... to avoid conflicts with other
|
|
namespaces. Things beginning with _dave.. are not intended for
|
|
public use.
|
|
*/
|
|
|
|
|
|
|
|
void DECL2 daveSetDebug(int nDebug) {
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveSetDebug(%d)\n",nDebug);
|
|
FLUSH;
|
|
#endif
|
|
daveDebug=nDebug;
|
|
}
|
|
|
|
int DECL2 daveGetDebug() {
|
|
#ifdef DEBUG_CALLS
|
|
LOG1("daveGetDebug()\n");
|
|
FLUSH;
|
|
#endif
|
|
return daveDebug;
|
|
}
|
|
/**
|
|
C# interoperability:
|
|
**/
|
|
void DECL2 daveSetTimeout(daveInterface * di, int tmo) {
|
|
#ifdef DEBUG_CALLS
|
|
LOG3("daveSetTimeOut(di:%p, time:%d)\n", di,tmo);
|
|
#endif
|
|
di->timeout=tmo;
|
|
#ifdef BCCWIN
|
|
setTimeOut(di,tmo);
|
|
#endif
|
|
}
|
|
|
|
int DECL2 daveGetTimeout(daveInterface * di) {
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveGetTimeOut(di:%p)\n",di);
|
|
FLUSH;
|
|
#endif
|
|
return di->timeout;
|
|
}
|
|
|
|
char * DECL2 daveGetName(daveInterface * di) {
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveGetName(di:%p)\n",di);
|
|
FLUSH;
|
|
#endif
|
|
return di->name;
|
|
}
|
|
|
|
|
|
int DECL2 daveGetMPIAdr(daveConnection * dc) {
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveGetMPIAdr(dc:%p)\n",dc);
|
|
FLUSH;
|
|
#endif
|
|
return dc->MPIAdr;
|
|
}
|
|
|
|
int DECL2 daveGetAnswLen(daveConnection * dc) {
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveGetAnswLen(dc:%p)\n",dc);
|
|
FLUSH;
|
|
#endif
|
|
return dc->AnswLen;
|
|
}
|
|
|
|
int DECL2 daveGetMaxPDULen(daveConnection * dc) {
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveGetMaxPDULen(dc:%p)\n",dc);
|
|
FLUSH;
|
|
#endif
|
|
return dc->maxPDUlength;
|
|
}
|
|
|
|
/**
|
|
PDU handling:
|
|
**/
|
|
|
|
/*
|
|
set up the header. Needs valid header pointer
|
|
*/
|
|
void DECL2 _daveInitPDUheader(PDU * p, int type) {
|
|
memset(p->header, 0, sizeof(PDUHeader));
|
|
if (type==2 || type==3)
|
|
p->hlen=12;
|
|
else
|
|
p->hlen=10;
|
|
p->param=p->header+p->hlen;
|
|
((PDUHeader*)p->header)->P=0x32;
|
|
((PDUHeader*)p->header)->type=type;
|
|
p->dlen=0;
|
|
p->plen=0;
|
|
p->udlen=0;
|
|
p->data=NULL;
|
|
p->udata=NULL;
|
|
}
|
|
|
|
/*
|
|
add parameters after header, adjust pointer to data.
|
|
needs valid header
|
|
*/
|
|
void DECL2 _daveAddParam(PDU * p,uc * param,us len) {
|
|
#ifdef ARM_FIX
|
|
us tmplen;
|
|
#endif
|
|
p->plen=len;
|
|
memcpy(p->param, param, len);
|
|
#ifdef ARM_FIX
|
|
tmplen=daveSwapIed_16(len);
|
|
memcpy(&(((PDUHeader*)p->header)->plen),&tmplen,sizeof(us));
|
|
#else
|
|
((PDUHeader*)p->header)->plen=daveSwapIed_16(len);
|
|
#endif
|
|
p->data=p->param+len;
|
|
p->dlen=0;
|
|
}
|
|
|
|
/*
|
|
add data after parameters, set dlen
|
|
needs valid header,parameters
|
|
*/
|
|
void DECL2 _daveAddData(PDU * p,void * data,int len) {
|
|
#ifdef ARM_FIX
|
|
us tmplen;
|
|
#endif
|
|
uc * dn= p->data+p->dlen;
|
|
p->dlen+=len;
|
|
memcpy(dn, data, len);
|
|
#ifdef ARM_FIX
|
|
tmplen=daveSwapIed_16(p->dlen);
|
|
memcpy(&(((PDUHeader*)p->header)->dlen),&tmplen,sizeof(us));
|
|
#else
|
|
((PDUHeader*)p->header)->dlen=daveSwapIed_16(p->dlen);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
add values after value header in data, adjust dlen and data count.
|
|
needs valid header,parameters,data,dlen
|
|
*/
|
|
void DECL2 _daveAddValue(PDU * p,void * data,int len) {
|
|
us dCount;
|
|
uc * dtype;
|
|
dtype=p->data+p->dlen-4+1; /* position of first byte in the 4 byte sequence */
|
|
#ifdef ARM_FIX
|
|
memcpy(&dCount, (p->data+p->dlen-4+2), sizeof(us));
|
|
#else
|
|
dCount=* ((us *)(p->data+p->dlen-4+2)); /* changed for multiple write */
|
|
#endif
|
|
dCount=daveSwapIed_16(dCount);
|
|
if (daveDebug & daveDebugPDU)
|
|
LOG2("dCount: %d\n", dCount);
|
|
if (*dtype==4) { /* bit data, length is in bits */
|
|
dCount+=8*len;
|
|
} else if (*dtype==9) { /* byte data, length is in bytes */
|
|
dCount+=len;
|
|
} else if (* dtype==3) { /* bit data, length is in bits */
|
|
dCount+=len;
|
|
} else {
|
|
if (daveDebug & daveDebugPDU)
|
|
LOG2("unknown data type/length: %d\n", *dtype);
|
|
}
|
|
if (p->udata==NULL) p->udata=p->data+4;
|
|
p->udlen+=len;
|
|
if (daveDebug & daveDebugPDU)
|
|
LOG2("dCount: %d\n", dCount);
|
|
dCount=daveSwapIed_16(dCount);
|
|
#ifdef ARM_FIX
|
|
memcpy((p->data+p->dlen-4+2), &dCount, sizeof(us));
|
|
#else
|
|
*((us *)(p->data+p->dlen-4+2))=dCount;
|
|
#endif
|
|
_daveAddData(p, data, len);
|
|
}
|
|
|
|
/*
|
|
add data in user data. Add a user data header, if not yet present.
|
|
*/
|
|
void DECL2 _daveAddUserData(PDU * p, uc * da, int len) {
|
|
uc udh[]={0xff,9,0,0};
|
|
if (p->dlen==0) {
|
|
if (daveDebug & daveDebugPDU)
|
|
LOG1("adding user data header.\n");
|
|
_daveAddData(p, udh, sizeof(udh));
|
|
}
|
|
_daveAddValue(p, da, len);
|
|
}
|
|
|
|
void DECL2 davePrepareReadRequest(daveConnection * dc, PDU *p) {
|
|
uc pa[]= {daveFuncRead,0};
|
|
#ifdef DEBUG_CALLS
|
|
LOG3("davePrepareReadRequest(dc:%p PDU:%p)\n", dc, p);
|
|
FLUSH;
|
|
#endif
|
|
p->header=dc->msgOut+dc->PDUstartO;
|
|
_daveInitPDUheader(p,1);
|
|
_daveAddParam(p, pa, sizeof(pa));
|
|
}
|
|
|
|
PDU * DECL2 daveNewPDU() {
|
|
PDU * p;
|
|
p=(PDU*)malloc(sizeof(PDU));
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveNewPDU() = %p\n",p);
|
|
FLUSH;
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
|
|
void DECL2 davePrepareWriteRequest(daveConnection * dc, PDU *p) {
|
|
uc pa[]= {daveFuncWrite, 0};
|
|
#ifdef DEBUG_CALLS
|
|
LOG3("davePrepareWriteRequest(dc:%p PDU:%p)\n", dc, p);
|
|
FLUSH;
|
|
#endif
|
|
p->header=dc->msgOut+dc->PDUstartO;
|
|
_daveInitPDUheader(p,1);
|
|
_daveAddParam(p, pa, sizeof(pa));
|
|
p->dlen=0;
|
|
}
|
|
|
|
void DECL2 daveAddToReadRequest(PDU *p, int area, int DBnum, int start, int byteCount, int isBit) {
|
|
uc pa[]= {
|
|
0x12, 0x0a, 0x10,
|
|
0x02, /* 1=single bit, 2=byte, 4=word */
|
|
0,0, /* length in bytes */
|
|
0,0, /* DB number */
|
|
0, /* area code */
|
|
0,0,0 /* start address in bits */
|
|
};
|
|
#ifdef ARM_FIX
|
|
us tmplen;
|
|
#endif
|
|
if ((area==daveAnaIn) || (area==daveAnaOut) /*|| (area==daveP)*/) {
|
|
pa[3]=4;
|
|
start*=8; /* bits */
|
|
} else if ((area==daveTimer) || (area==daveCounter)||(area==daveTimer200) || (area==daveCounter200)) {
|
|
pa[3]=area;
|
|
} else {
|
|
if(isBit) {
|
|
pa[3]=1;
|
|
} else {
|
|
start*=8; /* bit address of byte */
|
|
}
|
|
}
|
|
|
|
pa[4]=byteCount / 256;
|
|
pa[5]=byteCount & 0xff;
|
|
pa[6]=DBnum / 256;
|
|
pa[7]=DBnum & 0xff;
|
|
pa[8]=area;
|
|
pa[11]=start & 0xff;
|
|
pa[10]=(start / 0x100) & 0xff;
|
|
pa[9]=start / 0x10000;
|
|
|
|
p->param[1]++;
|
|
memcpy(p->param+p->plen, pa, sizeof(pa));
|
|
p->plen+=sizeof(pa);
|
|
|
|
#ifdef ARM_FIX
|
|
tmplen=daveSwapIed_16(p->plen);
|
|
memcpy(&(((PDUHeader*)p->header)->plen), &tmplen, sizeof(us));
|
|
#else
|
|
((PDUHeader*)p->header)->plen=daveSwapIed_16(p->plen);
|
|
#endif
|
|
p->data=p->param+p->plen;
|
|
p->dlen=0;
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(p);
|
|
}
|
|
}
|
|
|
|
void DECL2 daveAddVarToReadRequest(PDU *p, int area, int DBnum, int start, int byteCount) {
|
|
#ifdef DEBUG_CALLS
|
|
LOG6("daveAddVarToReadRequest(PDU:%p area:%s area number:%d start address:%d byte count:%d)\n",
|
|
p, daveAreaName(area), DBnum, start, byteCount);
|
|
FLUSH;
|
|
#endif
|
|
|
|
daveAddToReadRequest(p, area, DBnum, start, byteCount, 0);
|
|
}
|
|
|
|
void DECL2 daveAddBitVarToReadRequest(PDU *p, int area, int DBnum, int start, int byteCount) {
|
|
daveAddToReadRequest(p, area, DBnum, start, byteCount, 1);
|
|
}
|
|
|
|
void DECL2 daveAddToWriteRequest(PDU *p, int area, int DBnum, int start, int byteCount,
|
|
void * buffer,
|
|
uc * da,
|
|
int dasize,
|
|
uc * pa,
|
|
int pasize
|
|
) {
|
|
uc saveData[1024];
|
|
#ifdef ARM_FIX
|
|
us tmplen;
|
|
#endif
|
|
if ((area==daveTimer) || (area==daveCounter)||(area==daveTimer200) || (area==daveCounter200)) {
|
|
pa[3]=area;
|
|
pa[4]=((byteCount+1)/2) / 0x100;
|
|
pa[5]=((byteCount+1)/2) & 0xff;
|
|
} else if ((area==daveAnaIn) || (area==daveAnaOut)) {
|
|
pa[3]=4;
|
|
pa[4]=((byteCount+1)/2) / 0x100;
|
|
pa[5]=((byteCount+1)/2) & 0xff;
|
|
} else {
|
|
pa[4]=byteCount / 0x100;
|
|
pa[5]=byteCount & 0xff;
|
|
}
|
|
pa[6]=DBnum / 256;
|
|
pa[7]=DBnum & 0xff;
|
|
pa[8]=area;
|
|
pa[11]=start & 0xff;
|
|
pa[10]=(start / 0x100) & 0xff;
|
|
pa[9]=start / 0x10000;
|
|
if(p->dlen%2) {
|
|
_daveAddData(p, da, 1);
|
|
}
|
|
p->param[1]++;
|
|
if(p->dlen){
|
|
memcpy(saveData, p->data, p->dlen);
|
|
memcpy(p->data+pasize, saveData, p->dlen);
|
|
}
|
|
memcpy(p->param+p->plen, pa, pasize);
|
|
p->plen+=pasize;
|
|
#ifdef ARM_FIX
|
|
tmplen=daveSwapIed_16(p->plen);
|
|
memcpy(&(((PDUHeader*)p->header)->plen), &tmplen, sizeof(us));
|
|
#else
|
|
((PDUHeader*)p->header)->plen=daveSwapIed_16(p->plen);
|
|
#endif
|
|
p->data=p->param+p->plen;
|
|
_daveAddData(p, da, dasize);
|
|
_daveAddValue(p, buffer, byteCount);
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(p);
|
|
}
|
|
}
|
|
|
|
void DECL2 daveAddVarToWriteRequest(PDU *p, int area, int DBnum, int start, int byteCount, void * buffer) {
|
|
uc da[]= {0,4,0,0,};
|
|
uc pa[]= {
|
|
0x12, 0x0a, 0x10,
|
|
0x02, /* unit (for count?, for consistency?) byte */
|
|
0,0, /* length in bytes */
|
|
0,0, /* DB number */
|
|
0, /* area code */
|
|
0,0,0 /* start address in bits */
|
|
};
|
|
#ifdef DEBUG_CALLS
|
|
LOG7("daveAddVarToWriteRequest(PDU:%p area:%s area number:%d start address:%d byte count:%d buffer:%p)\n",
|
|
p, daveAreaName(area), DBnum, start, byteCount, buffer);
|
|
FLUSH;
|
|
#endif
|
|
|
|
daveAddToWriteRequest(p, area, DBnum, 8*start, byteCount,buffer,da,sizeof(da),pa,sizeof(pa));
|
|
}
|
|
|
|
|
|
void DECL2 daveAddBitVarToWriteRequest(PDU *p, int area, int DBnum, int start, int byteCount, void * buffer) {
|
|
uc da[]= {0,3,0,0,};
|
|
uc pa[]= {
|
|
0x12, 0x0a, 0x10,
|
|
0x01, /* single bit */
|
|
0,0, /* insert length in bytes here */
|
|
0,0, /* insert DB number here */
|
|
0, /* change this to real area code */
|
|
0,0,0 /* insert start address in bits */
|
|
};
|
|
#ifdef DEBUG_CALLS
|
|
LOG7("daveAddBitVarToWriteRequest(PDU:%p area:%s area number:%d start address:%d byte count:%d buffer:%p)\n",
|
|
p, daveAreaName(area), DBnum, start, byteCount, buffer);
|
|
FLUSH;
|
|
#endif
|
|
|
|
daveAddToWriteRequest(p, area, DBnum, start, byteCount,buffer,da,sizeof(da),pa,sizeof(pa));
|
|
}
|
|
|
|
/*
|
|
Get the eror code from a PDU, if one.
|
|
*/
|
|
int DECL2 daveGetPDUerror(PDU * p) {
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveGetPDUerror(PDU:%p\n", p);
|
|
FLUSH;
|
|
#endif
|
|
if (p->header[1]==2 || p->header[1]==3) {
|
|
return daveGetU16from(p->header+10);
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Sets up pointers to the fields of a received message.
|
|
*/
|
|
int DECL2 _daveSetupReceivedPDU(daveConnection * dc, PDU * p) {
|
|
int res; /* = daveResCannotEvaluatePDU; */
|
|
p->header=dc->msgIn+dc->PDUstartI;
|
|
res=0;
|
|
if (p->header[1]==2 || p->header[1]==3) {
|
|
p->hlen=12;
|
|
res=256*p->header[10]+p->header[11];
|
|
} else {
|
|
p->hlen=10;
|
|
}
|
|
p->param=p->header+p->hlen;
|
|
|
|
p->plen=256*p->header[6] + p->header[7];
|
|
p->data=p->param+p->plen;
|
|
p->dlen=256*p->header[8] + p->header[9];
|
|
p->udlen=0;
|
|
p->udata=NULL;
|
|
if (daveDebug & daveDebugPDU)
|
|
_daveDumpPDU(p);
|
|
return res;
|
|
}
|
|
|
|
int DECL2 _daveTestResultData(PDU * p) {
|
|
int res; /*=daveResCannotEvaluatePDU;*/
|
|
if ((p->data[0]==255)&&(p->dlen>4)) {
|
|
res=daveResOK;
|
|
p->udata=p->data+4;
|
|
p->udlen=p->data[2]*0x100+p->data[3];
|
|
if (p->data[1]==4) {
|
|
p->udlen>>=3; /* len is in bits, adjust */
|
|
} else if (p->data[1]==9) {
|
|
/* len is already in bytes, ok */
|
|
} else if (p->data[1]==3) {
|
|
/* len is in bits, but there is a byte per result bit, ok */
|
|
} else {
|
|
if (daveDebug & daveDebugPDU)
|
|
LOG2("fixme: what to do with data type %d?\n",p->data[1]);
|
|
res = daveResUnknownDataUnitSize;
|
|
}
|
|
} else {
|
|
res=p->data[0];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int DECL2 _daveTestReadResult(PDU * p) {
|
|
if (p->param[0]!=daveFuncRead) return daveResUnexpectedFunc;
|
|
return _daveTestResultData(p);
|
|
}
|
|
|
|
int DECL2 _daveTestPGReadResult(PDU * p) {
|
|
int pres=0;
|
|
if (p->param[0]!=0) return daveResUnexpectedFunc;
|
|
if (p->plen==12) pres=(256*p->param[10]+p->param[11]);
|
|
if (pres==0)return _daveTestResultData(p); else return pres;
|
|
}
|
|
|
|
int DECL2 _daveTestWriteResult(PDU * p) {
|
|
int res;/* =daveResCannotEvaluatePDU; */
|
|
if (p->param[0]!=daveFuncWrite) return daveResUnexpectedFunc;
|
|
if ((p->data[0]==255)) {
|
|
res=daveResOK;
|
|
} else
|
|
res=p->data[0];
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(p);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*****
|
|
Utilities:
|
|
****/
|
|
/*
|
|
This is an extended memory compare routine. It can handle don't care and stop flags
|
|
in the sample data. A stop flag lets it return success.
|
|
*/
|
|
int DECL2 _daveMemcmp(us * a, uc *b, size_t len) {
|
|
unsigned int i;
|
|
us * a1=(us*)a;
|
|
uc * b1=(uc*)b;
|
|
for (i=0;i<len;i++){
|
|
if (((*a1)&0xff)!=*b1) {
|
|
if (((*a1)&0x100)!=0x100) {
|
|
// LOG3("want:%02X got:%02X\n",*a1,*b1);
|
|
return i+1;
|
|
}
|
|
if (((*a1)&0x200)==0x200) return 0;
|
|
}
|
|
a1++;
|
|
b1++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Hex dump:
|
|
*/
|
|
void DECL2 _daveDump(char * name, void*b,int len) {//void DECL2 _daveDump(char * name,uc*b,int len) {
|
|
int j;
|
|
LOG2("%s: ",name);
|
|
if (len>daveMaxRawLen) len=daveMaxRawLen; /* this will avoid to dump zillions of chars */
|
|
for (j=0; j<len; j++){
|
|
if((j & 0xf)==0) LOG2("\n%x:",j);
|
|
LOG2("0x%02X,",((uc*)(b))[j]);
|
|
}
|
|
LOG1("\n");
|
|
}
|
|
|
|
/*
|
|
Hex dump PDU:
|
|
*/
|
|
void DECL2 _daveDumpPDU(PDU * p) {
|
|
int i,dl;
|
|
uc * pd;
|
|
_daveDump("PDU header", p->header, p->hlen);
|
|
LOG3("plen: %d dlen: %d\n",p->plen, p->dlen);
|
|
if(p->plen>0) _daveDump("Parameter",p->param,p->plen);
|
|
if(p->dlen>0) _daveDump("Data ",p->data,p->dlen);
|
|
if ((p->plen==2)&&(p->param[0]==daveFuncRead)) {
|
|
pd=p->data;
|
|
for (i=0;i<p->param[1];i++) {
|
|
_daveDump("Data hdr ",pd,4);
|
|
|
|
dl=0x100*pd[2]+pd[3];
|
|
if (pd[1]==4) dl/=8;
|
|
pd+=4;
|
|
_daveDump("Data ",pd,dl);
|
|
if(i<p->param[1]-1) dl=dl+(dl%2); // the PLC places extra bytes at the end of all
|
|
// but last result, if length is not a multiple
|
|
// of 2
|
|
pd+=dl;
|
|
}
|
|
} else if ((p->header[1]==1)&&/*(p->plen==2)&&*/(p->param[0]==daveFuncWrite)) {
|
|
pd=p->data;
|
|
for (i=0;i<p->param[1];i++) {
|
|
_daveDump("Write Data hdr ",pd,4);
|
|
|
|
dl=0x100*pd[2]+pd[3];
|
|
if (pd[1]==4) dl/=8;
|
|
pd+=4;
|
|
_daveDump("Data ",pd,dl);
|
|
if(i<p->param[1]-1) dl=dl+(dl%2); // the PLC places extra bytes at the end of all
|
|
// but last result, if length is not a multiple
|
|
// of 2
|
|
pd+=dl;
|
|
}
|
|
} else {
|
|
/*
|
|
if(p->dlen>0) {
|
|
if(p->udlen==0)
|
|
_daveDump("Data ",p->data,p->dlen);
|
|
else
|
|
_daveDump("Data hdr ",p->data,4);
|
|
}
|
|
if(p->udlen>0) _daveDump("result Data ",p->udata,p->udlen);
|
|
*/
|
|
}
|
|
if ((p->header[1]==2)||(p->header[1]==3)) {
|
|
LOG2("error: %s\n",daveStrerror(daveGetPDUerror(p)));
|
|
}
|
|
}
|
|
|
|
/*
|
|
name Objects:
|
|
*/
|
|
char * DECL2 daveBlockName(uc bn) {
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveBlockName(bn:%d)\n", bn);
|
|
FLUSH;
|
|
#endif
|
|
switch(bn) {
|
|
case daveBlockType_OB: return "OB";
|
|
case daveBlockType_DB: return "DB";
|
|
case daveBlockType_SDB: return "SDB";
|
|
case daveBlockType_FC: return "FC";
|
|
case daveBlockType_SFC: return "SFC";
|
|
case daveBlockType_FB: return "FB";
|
|
case daveBlockType_SFB: return "SFB";
|
|
default:return "unknown block type!";
|
|
}
|
|
}
|
|
|
|
char * DECL2 daveAreaName(uc n) {
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveAreaName(n:%d)\n", n);
|
|
FLUSH;
|
|
#endif
|
|
switch (n) {
|
|
case daveSysInfo: return "System info mem.area of 200 family";
|
|
case daveSysFlags: return "System flags of 200 family";
|
|
case daveAnaIn: return "analog inputs of 200 family";
|
|
case daveAnaOut: return "analog outputs of 200 family";
|
|
|
|
case daveP: return "Peripheral I/O";
|
|
case daveInputs: return "Inputs";
|
|
case daveOutputs: return "Outputs";
|
|
case daveDB: return "DB";
|
|
case daveDI: return "DI (instance data)";
|
|
case daveFlags: return "Flags";
|
|
case daveLocal: return "local data";
|
|
case daveV: return "caller's local data";
|
|
case daveCounter: return "S7 counters";
|
|
case daveTimer: return "S7 timers";
|
|
case daveCounter200: return "IEC counters";
|
|
case daveTimer200: return "IEC timers";
|
|
default:return "unknown area!";
|
|
}
|
|
}
|
|
|
|
/*
|
|
Functions to load blocks from PLC:
|
|
*/
|
|
void DECL2 _daveConstructUpload(PDU *p,char blockType, int blockNr) {
|
|
uc pa[]= {0x1d,
|
|
0,0,0,0,0,0,0,9,0x5f,0x30,0x41,48,48,48,48,49,65};
|
|
pa[11]=blockType;
|
|
sprintf((char*)(pa+12),"%05d",blockNr);
|
|
pa[17]='A';
|
|
_daveInitPDUheader(p,1);
|
|
_daveAddParam(p, pa, sizeof(pa));
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(p);
|
|
}
|
|
}
|
|
|
|
void DECL2 _daveConstructDoUpload(PDU * p, int uploadID) {
|
|
uc pa[]= {0x1e,0,0,0,0,0,0,1};
|
|
pa[7]=uploadID;
|
|
_daveInitPDUheader(p,1);
|
|
_daveAddParam(p, pa, sizeof(pa));
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(p);
|
|
}
|
|
}
|
|
|
|
void DECL2 _daveConstructEndUpload(PDU * p, int uploadID) {
|
|
uc pa[]= {0x1f,0,0,0,0,0,0,1};
|
|
pa[7]=uploadID;
|
|
_daveInitPDUheader(p,1);
|
|
_daveAddParam(p, pa, sizeof(pa));
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(p);
|
|
}
|
|
}
|
|
|
|
uc paInsert[]= { // sended after transmission of a complete block,
|
|
// I guess this makes the CPU link the block into a program.
|
|
0x28,0,0,0,0,0,0,0xFD,0,0x0A,1,0,0x30,0x42,0x30,0x30,0x30,0x30,0x34,0x50, // block type code and number
|
|
0x05,'_','I','N','S','E',
|
|
};
|
|
|
|
uc paMakeRun[]= {
|
|
0x28,0,0,0,0,0,0,0xFD,0,0x00,9,'P','_','P','R','O','G','R','A','M'
|
|
};
|
|
|
|
uc paCompress[]= {
|
|
0x28,0,0,0,0,0,0,0xFD,0,0x00,5,'_','G','A','R','B'
|
|
};
|
|
|
|
uc paMakeStop[]= {
|
|
0x29,0,0,0,0,0,9,'P','_','P','R','O','G','R','A','M'
|
|
};
|
|
|
|
uc paCopyRAMtoROM[]= {
|
|
0x28,0,0,0,0,0,0,0xfd,0,2,'E','P',5,'_','M','O','D','U'
|
|
};
|
|
|
|
int DECL2 daveStop(daveConnection * dc) {
|
|
int res;
|
|
PDU p,p2;
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveStop(dc:%p)\n", dc);
|
|
FLUSH;
|
|
#endif
|
|
if (dc->iface->protocol==daveProtoAS511) {
|
|
return daveStopS5(dc);
|
|
}
|
|
p.header=dc->msgOut+dc->PDUstartO;
|
|
_daveInitPDUheader(&p, 1);
|
|
_daveAddParam(&p, paMakeStop, sizeof(paMakeStop));
|
|
res=_daveExchange(dc, &p);
|
|
if (res==daveResOK) {
|
|
res=_daveSetupReceivedPDU(dc,&p2);
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(&p2);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int DECL2 daveStart(daveConnection*dc) {
|
|
int res;
|
|
PDU p,p2;
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveStart(dc:%p)\n", dc);
|
|
FLUSH;
|
|
#endif
|
|
if (dc->iface->protocol==daveProtoAS511) {
|
|
return daveStartS5(dc);
|
|
}
|
|
p.header=dc->msgOut+dc->PDUstartO;
|
|
_daveInitPDUheader(&p, 1);
|
|
_daveAddParam(&p, paMakeRun, sizeof(paMakeRun));
|
|
res=_daveExchange(dc, &p);
|
|
if (res==daveResOK) {
|
|
res=_daveSetupReceivedPDU(dc, &p2);
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(&p2);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int DECL2 daveCopyRAMtoROM(daveConnection * dc) {
|
|
int res;
|
|
PDU p,p2;
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("davecopyRAMtoROM(dc:%p)\n", dc);
|
|
FLUSH;
|
|
#endif
|
|
p.header=dc->msgOut+dc->PDUstartO;
|
|
_daveInitPDUheader(&p, 1);
|
|
_daveAddParam(&p, paCopyRAMtoROM, sizeof(paCopyRAMtoROM));
|
|
res=_daveExchange(dc, &p);
|
|
if (res==daveResOK) {
|
|
res=_daveSetupReceivedPDU(dc,&p2); /* possible problem, Timeout */
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(&p2);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Build a PDU with user data ud, send it and prepare received PDU.
|
|
*/
|
|
int DECL2 daveBuildAndSendPDU(daveConnection * dc, PDU*p2,uc *pa,int psize, uc *ud,int usize) {
|
|
int res;
|
|
PDU p;
|
|
uc nullData[]={0x0a,0,0,0};
|
|
p.header=dc->msgOut+dc->PDUstartO;
|
|
_daveInitPDUheader(&p, 7);
|
|
_daveAddParam(&p, pa, psize);
|
|
if (ud!=NULL) _daveAddUserData(&p, ud, usize); else
|
|
if (usize!=0) _daveAddData(&p, nullData, 4);
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(&p);
|
|
}
|
|
res=_daveExchange(dc, &p);
|
|
if (daveDebug & daveDebugErrorReporting)
|
|
LOG2("*** res of _daveExchange(): %d\n",res);
|
|
if (res!=daveResOK) return res;
|
|
res=_daveSetupReceivedPDU(dc,p2);
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(p2);
|
|
}
|
|
if (daveDebug & daveDebugErrorReporting)
|
|
LOG2("*** res of _daveSetupReceivedPDU(): %04X\n",res);
|
|
if (res!=daveResOK) return res;
|
|
res=_daveTestPGReadResult(p2);
|
|
if (daveDebug & daveDebugErrorReporting)
|
|
LOG2("*** res of _daveTestPGReadResult(): %04X\n",res);
|
|
return res;
|
|
}
|
|
|
|
int DECL2 daveListBlocksOfType(daveConnection * dc,uc type,daveBlockEntry * buf) {
|
|
int res,i, len;
|
|
PDU p2;
|
|
uc * buffer=(uc*)buf;
|
|
uc pa[]={0,1,18,4,17,67,2,0};
|
|
uc da[]={'0','0'};
|
|
uc pam[]={0,1,18,8,0x12,0x43,2,1,0,0,0,0};
|
|
#ifdef DEBUG_CALLS
|
|
LOG4("ListBlocksOfType(dc:%p type:%d buf:%p)\n", dc, type, buf);
|
|
FLUSH;
|
|
#endif
|
|
da[1]=type;
|
|
res=daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), da, sizeof(da));
|
|
if (res!=daveResOK) return -res;
|
|
len=0;
|
|
while (p2.param[9]!=0) {
|
|
if (buffer!=NULL) memcpy(buffer+len,p2.udata,p2.udlen);
|
|
dc->resultPointer=p2.udata;
|
|
dc->_resultPointer=p2.udata;
|
|
len+=p2.udlen;
|
|
printf("more data\n");
|
|
res=daveBuildAndSendPDU(dc, &p2,pam, sizeof(pam), NULL, 1);
|
|
}
|
|
|
|
|
|
if (res==daveResOK) {
|
|
if (buffer!=NULL) memcpy(buffer+len,p2.udata,p2.udlen);
|
|
dc->resultPointer=p2.udata;
|
|
dc->_resultPointer=p2.udata;
|
|
len+=p2.udlen;
|
|
} else {
|
|
if(daveDebug & daveDebugPrintErrors)
|
|
LOG3("daveListBlocksOfType: %d=%s\n",res, daveStrerror(res));
|
|
}
|
|
dc->AnswLen=len;
|
|
res=len/sizeof(daveBlockEntry);
|
|
for (i=0; i<res; i++) {
|
|
buf[i].number=daveSwapIed_16(buf[i].number);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
doesn't work on S7-200
|
|
*/
|
|
int DECL2 daveGetOrderCode(daveConnection * dc,char * buf) {
|
|
int res=0;
|
|
PDU p2;
|
|
uc pa[]={0,1,18,4,17,68,1,0};
|
|
uc da[]={1,17,0,1}; /* SZL-ID 0x111 index 1 */
|
|
#ifdef DEBUG_CALLS
|
|
LOG3("daveGetOrderCode(dc:%p buf:%p)\n", dc, buf);
|
|
FLUSH;
|
|
#endif
|
|
daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), da, sizeof(da));
|
|
if (buf) {
|
|
memcpy(buf, p2.udata+10, daveOrderCodeSize);
|
|
buf[daveOrderCodeSize]=0;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int DECL2 daveReadSZL(daveConnection * dc, int ID, int index, void * buffer, int buflen) {
|
|
int res,len,cpylen;
|
|
int pa7;
|
|
// int pa6;
|
|
PDU p2;
|
|
uc pa[]={0,1,18,4,17,68,1,0};
|
|
uc da[]={1,17,0,1};
|
|
|
|
uc pam[]={0,1,18,8,18,68,1,1,0,0,0,0};
|
|
// uc dam[]={10,0,0,0};
|
|
|
|
#ifdef DEBUG_CALLS
|
|
LOG5("daveReadSZL(dc:%p, ID:%d, index:%d, buffer:%p)\n", dc, ID, index, buffer);
|
|
FLUSH;
|
|
#endif
|
|
da[0]=ID / 0x100;
|
|
da[1]=ID % 0x100;
|
|
da[2]=index / 0x100;
|
|
da[3]=index % 0x100;
|
|
res=daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), da, sizeof(da));
|
|
|
|
len=0;
|
|
pa7=p2.param[7];
|
|
// pa6=p2.param[6];
|
|
while (p2.param[9]!=0) {
|
|
if (buffer!=NULL) {
|
|
cpylen = p2.udlen;
|
|
if (len + cpylen > buflen) cpylen = buflen - len;
|
|
if (cpylen > 0) memcpy((uc *)buffer+len,p2.udata,cpylen);
|
|
}
|
|
dc->resultPointer=p2.udata;
|
|
dc->_resultPointer=p2.udata;
|
|
len+=p2.udlen;
|
|
pam[7]=pa7;
|
|
// res=daveBuildAndSendPDU(dc, &p2,pam, sizeof(pam), NULL, sizeof(dam));
|
|
res=daveBuildAndSendPDU(dc, &p2,pam, sizeof(pam), NULL, 1);
|
|
}
|
|
|
|
|
|
if (res==daveResOK) {
|
|
if (buffer!=NULL) {
|
|
cpylen = p2.udlen;
|
|
if (len + cpylen > buflen) cpylen = buflen - len;
|
|
if (cpylen > 0) memcpy((uc *)buffer+len,p2.udata,cpylen);
|
|
}
|
|
dc->resultPointer=p2.udata;
|
|
dc->_resultPointer=p2.udata;
|
|
len+=p2.udlen;
|
|
}
|
|
dc->AnswLen=len;
|
|
return res;
|
|
}
|
|
|
|
int DECL2 daveGetBlockInfo(daveConnection * dc, daveBlockInfo *dbi, uc type, int number)
|
|
{
|
|
int res;
|
|
uc pa[]={0,1,18,4,17,67,3,0}; /* param */
|
|
uc da[]={'0',0,'0','0','0','1','0','A'};
|
|
PDU p2;
|
|
sprintf((char*)(da+2),"%05d",number);
|
|
da[1]=type;
|
|
da[7]='A';
|
|
res=daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), da, sizeof(da));
|
|
if ((dbi!=NULL) && (p2.udlen==sizeof(daveBlockInfo))) {
|
|
memcpy(dbi, p2.udata, p2.udlen);
|
|
dbi->number=daveSwapIed_16(dbi->number);
|
|
dbi->length=daveSwapIed_16(dbi->length);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int DECL2 daveListBlocks(daveConnection * dc,daveBlockTypeEntry * buf) {
|
|
int res,i;
|
|
PDU p2;
|
|
uc pa[]={0,1,18,4,17,67,1,0};
|
|
daveBuildAndSendPDU(dc, &p2, pa, sizeof(pa), NULL, 1/*da, sizeof(da)*/);
|
|
res=p2.udlen/sizeof(daveBlockTypeEntry);
|
|
if (buf) {
|
|
memcpy(buf, p2.udata, p2.udlen);
|
|
for (i=0; i<res; i++) {
|
|
buf[i].count=daveSwapIed_16(buf[i].count);
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int DECL2 daveReadManyBytes(daveConnection * dc,int area, int DBnum, int start,int len, void * buffer){
|
|
int res, pos, readLen;
|
|
uc * pbuf;
|
|
pos=0;
|
|
if (buffer==NULL) return daveResNoBuffer;
|
|
pbuf=(uc*) buffer;
|
|
res=daveResInvalidLength; //the only chance to return this is when len<=0
|
|
while (len>0) {
|
|
if (len>dc->maxPDUlength-18) readLen=dc->maxPDUlength-18; else readLen=len;
|
|
res=daveReadBytes(dc,area, DBnum, start, readLen, pbuf);
|
|
if (res!=0) return res;
|
|
len-=readLen;
|
|
start+=readLen;
|
|
pbuf+=readLen;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
Read len bytes from PLC memory area "area", data block DBnum.
|
|
Return the Number of bytes read.
|
|
If a buffer pointer is provided, data will be copied into this buffer.
|
|
If it's NULL you can get your data from the resultPointer in daveConnection long
|
|
as you do not send further requests.
|
|
*/
|
|
int DECL2 daveReadBytes(daveConnection * dc,int area, int DBnum, int start,int len, void * buffer){
|
|
PDU p1,p2;
|
|
int res;
|
|
#ifdef DEBUG_CALLS
|
|
LOG7("daveReadBytes(dc:%p area:%s area number:%d start address:%d byte count:%d buffer:%p)\n",
|
|
dc, daveAreaName(area), DBnum, start,len, buffer);
|
|
FLUSH;
|
|
#endif
|
|
if (dc->iface->protocol==daveProtoAS511) {
|
|
return daveReadS5Bytes(dc, area, DBnum, start, len/*, buffer*/);
|
|
}
|
|
dc->AnswLen=0; // 03/12/05
|
|
dc->resultPointer=NULL;
|
|
dc->_resultPointer=NULL;
|
|
p1.header=dc->msgOut+dc->PDUstartO;
|
|
davePrepareReadRequest(dc, &p1);
|
|
daveAddVarToReadRequest(&p1, area, DBnum, start, len);
|
|
res=_daveExchange(dc, &p1);
|
|
if (res!=daveResOK) return res;
|
|
res=_daveSetupReceivedPDU(dc, &p2);
|
|
if (daveDebug & daveDebugPDU)
|
|
LOG3("_daveSetupReceivedPDU() returned: %d=%s\n", res,daveStrerror(res));
|
|
if (res!=daveResOK) return res;
|
|
|
|
res=_daveTestReadResult(&p2);
|
|
if (daveDebug & daveDebugPDU)
|
|
LOG3("_daveTestReadResult() returned: %d=%s\n", res,daveStrerror(res));
|
|
if (res!=daveResOK) return res;
|
|
|
|
if (p2.udlen==0) {
|
|
return daveResCPUNoData;
|
|
}
|
|
/*
|
|
copy to user buffer and setup internal buffer pointers:
|
|
*/
|
|
if (buffer!=NULL) memcpy(buffer,p2.udata,p2.udlen);
|
|
dc->resultPointer=p2.udata;
|
|
dc->_resultPointer=p2.udata;
|
|
dc->AnswLen=p2.udlen;
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
Read len BITS from PLC memory area "area", data block DBnum.
|
|
Return the Number of bytes read.
|
|
If a buffer pointer is provided, data will be copied into this buffer.
|
|
If it's NULL you can get your data from the resultPointer in daveConnection long
|
|
as you do not send further requests.
|
|
*/
|
|
int DECL2 daveReadBits(daveConnection * dc,int area, int DBnum, int start,int len, void * buffer){
|
|
PDU p1,p2;
|
|
int res;
|
|
#ifdef DEBUG_CALLS
|
|
LOG7("daveReadBits(dc:%p area:%s area number:%d start address:%d byte count:%d buffer:%p)\n",
|
|
dc, daveAreaName(area), DBnum, start,len,buffer);
|
|
FLUSH;
|
|
#endif
|
|
dc->resultPointer=NULL;
|
|
dc->_resultPointer=NULL;
|
|
dc->AnswLen=0;
|
|
p1.header=dc->msgOut+dc->PDUstartO;
|
|
davePrepareReadRequest(dc, &p1);
|
|
daveAddBitVarToReadRequest(&p1, area, DBnum, start, len);
|
|
|
|
res=_daveExchange(dc, &p1);
|
|
if (res!=daveResOK) return res;
|
|
res=_daveSetupReceivedPDU(dc, &p2);
|
|
if (daveDebug & daveDebugPDU)
|
|
LOG3("_daveSetupReceivedPDU() returned: %d=%s\n", res,daveStrerror(res));
|
|
if (res!=daveResOK) return res;
|
|
|
|
res=_daveTestReadResult(&p2);
|
|
if (daveDebug & daveDebugPDU)
|
|
LOG3("_daveTestReadResult() returned: %d=%s\n", res,daveStrerror(res));
|
|
if (res!=daveResOK) return res;
|
|
if (daveDebug & daveDebugPDU)
|
|
LOG2("got %d bytes of data\n", p2.udlen);
|
|
if (p2.udlen==0) {
|
|
return daveResCPUNoData;
|
|
}
|
|
if (buffer!=NULL) {
|
|
if (daveDebug & daveDebugPDU)
|
|
LOG2("copy %d bytes to buffer\n", p2.udlen);
|
|
memcpy(buffer,p2.udata,p2.udlen);
|
|
}
|
|
dc->resultPointer=p2.udata;
|
|
dc->_resultPointer=p2.udata;
|
|
dc->AnswLen=p2.udlen;
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
Execute a predefined read request. Store results into the resultSet structure.
|
|
*/
|
|
int DECL2 daveExecReadRequest(daveConnection * dc, PDU *p, daveResultSet* rl){
|
|
PDU p2;
|
|
uc * q;
|
|
daveResult * cr, *c2;
|
|
int res, i, len, rlen;
|
|
#ifdef DEBUG_CALLS
|
|
LOG4("daveExecReadRequest(dc:%p, PDU:%p, rl:%p\n", dc, p, rl);
|
|
FLUSH;
|
|
#endif
|
|
dc->AnswLen=0; // 03/12/05
|
|
dc->resultPointer=NULL;
|
|
dc->_resultPointer=NULL;
|
|
res=_daveExchange(dc, p);
|
|
if (res!=daveResOK) return res;
|
|
res=_daveSetupReceivedPDU(dc, &p2);
|
|
if (res!=daveResOK) return res;
|
|
res=_daveTestReadResult(&p2);
|
|
if(res!=daveResOK) return res;
|
|
i=0;
|
|
if (rl!=NULL) {
|
|
cr=(daveResult*)calloc(p2.param[1], sizeof(daveResult));
|
|
rl->numResults=p2.param[1];
|
|
rl->results=cr;
|
|
c2=cr;
|
|
q=p2.data;
|
|
rlen=p2.dlen;
|
|
while (i<p2.param[1]) {
|
|
/* printf("result %d: %d %d %d %d\n",i, *q,q[1],q[2],q[3]); */
|
|
if ((*q==255)&&(rlen>4)) {
|
|
len=q[2]*0x100+q[3];
|
|
if (q[1]==4) {
|
|
len>>=3; /* len is in bits, adjust */
|
|
} else if (q[1]==9) {
|
|
/* len is already in bytes, ok */
|
|
} else if (q[1]==3) {
|
|
/* len is in bits, but there is a byte per result bit, ok */
|
|
} else {
|
|
if (daveDebug & daveDebugPDU)
|
|
LOG2("fixme: what to do with data type %d?\n",q[1]);
|
|
}
|
|
} else {
|
|
len=0;
|
|
}
|
|
/* printf("Store result %d length:%d\n", i, len); */
|
|
c2->length=len;
|
|
if(len>0){
|
|
c2->bytes=(uc*)malloc(len);
|
|
memcpy(c2->bytes, q+4, len);
|
|
}
|
|
c2->error=daveUnknownError;
|
|
|
|
if (q[0]==0xFF) {
|
|
c2->error=daveResOK;
|
|
} else
|
|
c2->error=q[0];
|
|
|
|
/* printf("Error %d\n", c2->error); */
|
|
q+=len+4;
|
|
rlen-=len;
|
|
if ((len % 2)==1) {
|
|
q++;
|
|
rlen--;
|
|
}
|
|
c2++;
|
|
i++;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
Execute a predefined write request.
|
|
*/
|
|
int DECL2 daveExecWriteRequest(daveConnection * dc, PDU *p, daveResultSet* rl){
|
|
PDU p2;
|
|
uc * q;
|
|
daveResult * cr, *c2;
|
|
int res, i;
|
|
#ifdef DEBUG_CALLS
|
|
LOG4("daveExecWriteRequest(dc:%p, PDU:%p, rl:%p\n", dc, p, rl);
|
|
FLUSH;
|
|
#endif
|
|
res=_daveExchange(dc, p);
|
|
if(res!=daveResOK) return res;
|
|
res=_daveSetupReceivedPDU(dc, &p2);
|
|
if(res!=daveResOK) return res;
|
|
res=_daveTestWriteResult(&p2);
|
|
if(res!=daveResOK) return res;
|
|
if (rl!=NULL) {
|
|
cr=(daveResult*)calloc(p2.param[1], sizeof(daveResult));
|
|
rl->numResults=p2.param[1];
|
|
rl->results=cr;
|
|
c2=cr;
|
|
q=p2.data;
|
|
i=0;
|
|
while (i<p2.param[1]) {
|
|
/* printf("result %d: %d %d %d %d\n",i, *q,q[1],q[2],q[3]); */
|
|
c2->error=daveUnknownError;
|
|
if (q[0]==0x0A) { /* 300 and 400 families */
|
|
c2->error=daveResItemNotAvailable;
|
|
} else if (q[0]==0x03) { /* 200 family */
|
|
c2->error=daveResItemNotAvailable;
|
|
} else if (q[0]==0x05) {
|
|
c2->error=daveAddressOutOfRange;
|
|
} else if (q[0]==0xFF) {
|
|
c2->error=daveResOK;
|
|
} else if (q[0]==0x07) {
|
|
c2->error=daveWriteDataSizeMismatch;
|
|
}
|
|
/* printf("Error %d\n", c2->error); */
|
|
q++;
|
|
c2++;
|
|
i++;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
int DECL2 daveUseResult(daveConnection * dc, daveResultSet * rl, int n){
|
|
daveResult * dr;
|
|
#ifdef DEBUG_CALLS
|
|
LOG4("daveUseResult(dc:%p, result set:%p, number:%d)\n", dc, rl, n);
|
|
#endif
|
|
if (rl==NULL) {
|
|
#ifdef DEBUG_CALLS
|
|
LOG1("invalid resultSet \n");
|
|
FLUSH;
|
|
#endif
|
|
return daveEmptyResultSetError;
|
|
}
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("result set has %d results\n",rl->numResults);
|
|
FLUSH;
|
|
#endif
|
|
if (rl->numResults==0) return daveEmptyResultSetError;
|
|
if (n>=rl->numResults) return daveEmptyResultSetError;
|
|
dr = &(rl->results[n]);
|
|
if (dr->error!=0) return dr->error;
|
|
if (dr->length<=0) return daveEmptyResultError;
|
|
|
|
dc->resultPointer=dr->bytes;
|
|
dc->_resultPointer=dr->bytes;
|
|
return 0;
|
|
}
|
|
|
|
void DECL2 daveFreeResults(daveResultSet * rl){
|
|
daveResult * r;
|
|
int i;
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveFreeResults(%p)",rl);
|
|
#endif
|
|
if (rl==NULL) {
|
|
#ifdef DEBUG_CALLS
|
|
LOG1("no Results,ready\n");
|
|
#endif
|
|
return; // make it NULL safe
|
|
}
|
|
/* printf("result set: %p\n",rl); */
|
|
for (i=0; i<rl->numResults; i++) {
|
|
r=&(rl->results[i]);
|
|
/* printf("result: %p bytes at:%p\n",r,r->bytes); */
|
|
if (r->bytes!=NULL) free(r->bytes);
|
|
}
|
|
#ifdef DEBUG_CALLS
|
|
LOG2(" free'd %d results\n",rl->numResults);
|
|
#endif
|
|
free(rl->results); // fix from Renato Gartmann
|
|
rl->numResults=0;
|
|
/* free(rl); */ /* This is NOT malloc'd by library but in the application's memory space! */
|
|
}
|
|
|
|
int DECL2 daveGetErrorOfResult(daveResultSet *rs, int number) {
|
|
return rs->results[number].error;
|
|
}
|
|
|
|
/*
|
|
This will setup a new connection structure using an initialized
|
|
daveInterface and PLC's MPI address.
|
|
*/
|
|
daveConnection * DECL2 daveNewConnection(daveInterface * di, int MPI, int rack, int slot) {
|
|
daveConnection * dc=(daveConnection *) calloc(1,sizeof(daveConnection));
|
|
if (dc) {
|
|
dc->iface=di;
|
|
dc->MPIAdr=MPI;
|
|
|
|
dc->rack=rack;
|
|
dc->slot=slot;
|
|
|
|
dc->maxPDUlength=1920; // assume an (unreal?) maximum
|
|
dc->connectionNumber=di->nextConnection; // 1/10/05 trying Andrew's patch
|
|
|
|
dc->PDUnumber=0xFFFE; // just a start value; // test!
|
|
dc->messageNumber=0;
|
|
switch (di->protocol) {
|
|
case daveProtoMPI: /* my first Version of MPI */
|
|
dc->PDUstartO=8; /* position of PDU in outgoing messages */
|
|
dc->PDUstartI=8; /* position of PDU in incoming messages */
|
|
di->ackPos=6; /* position of 0xB0 in ack packet */
|
|
break;
|
|
case daveProtoMPI3: /* Step 7 Version of MPI */
|
|
dc->PDUstartO=8; /* position of PDU in outgoing messages */
|
|
dc->PDUstartI=12; /* position of PDU in incoming messages */
|
|
di->ackPos=10; /* position of 0xB0 in ack packet */
|
|
break;
|
|
case daveProtoMPI2: /* Andrew's Version of MPI */
|
|
case daveProtoMPI4: /* Andrew's Version of MPI with extra STX */
|
|
dc->PDUstartO=6; /* position of PDU in outgoing messages */
|
|
dc->PDUstartI=6; /* position of PDU in incoming messages */
|
|
di->ackPos=4; /* position of 0xB0 in ack packet */
|
|
break;
|
|
|
|
case daveProtoNLpro: /* Deltalogic NetLink Pro */
|
|
dc->PDUstartO=6; /* position of PDU in outgoing messages */
|
|
dc->PDUstartI=8; /* position of PDU in incoming messages */
|
|
di->ackPos=4; /* position of 0xB0 in ack packet */
|
|
break;
|
|
|
|
case daveProtoPPI:
|
|
dc->PDUstartO=3; /* position of PDU in outgoing messages */
|
|
dc->PDUstartI=7; /* position of PDU in incoming messages */
|
|
break;
|
|
|
|
case daveProtoISOTCP:
|
|
case daveProtoISOTCP243:
|
|
dc->PDUstartO=7; /* position of PDU in outgoing messages */
|
|
dc->PDUstartI=7; /* position of PDU in incoming messages */
|
|
di->timeout=1500000;
|
|
break;
|
|
case daveProtoMPI_IBH:
|
|
dc->maxPDUlength=240; // limit for NetLink as reported by AFK
|
|
dc->PDUstartI= sizeof(IBHpacket)+sizeof(MPIheader);
|
|
dc->PDUstartO= sizeof(IBHpacket)+sizeof(MPIheader); // 02/01/2005
|
|
break;
|
|
case daveProtoPPI_IBH:
|
|
dc->maxPDUlength=240; // limit for NetLink as reported by AFK
|
|
dc->PDUstartI=14; // sizeof(IBHpacket)+7;
|
|
dc->PDUstartO=13;// sizeof(IBHpacket)+7; // 02/01/2005
|
|
break;
|
|
|
|
case daveProtoAS511:
|
|
dc->PDUstartI=0;
|
|
dc->PDUstartO=0;
|
|
break;
|
|
|
|
case daveProtoUserTransport:
|
|
dc->PDUstartI=0;
|
|
dc->PDUstartO=0;
|
|
break;
|
|
case daveProtoS7online:
|
|
dc->PDUstartI=80;
|
|
dc->PDUstartO=80;
|
|
break;
|
|
|
|
default:
|
|
dc->PDUstartO=8; /* position of PDU in outgoing messages */
|
|
dc->PDUstartI=8; /* position of PDU in incoming messages */
|
|
fprintf(stderr,"Unknown protocol on interface %s\n",di->name);
|
|
}
|
|
#ifdef BCCWIN
|
|
setTimeOut(di, di->timeout);
|
|
#endif
|
|
}
|
|
return dc;
|
|
}
|
|
|
|
int DECL2 daveWriteManyBytes(daveConnection * dc,int area, int DBnum, int start,int len, void * buffer){
|
|
int res, pos, writeLen;
|
|
uc * pbuf;
|
|
pos=0;
|
|
if (buffer==NULL) return daveResNoBuffer;
|
|
pbuf=(uc*) buffer;
|
|
res=daveResInvalidLength; //the only chance to return this is when len<=0
|
|
while (len>0) {
|
|
if (len>dc->maxPDUlength-28) writeLen=dc->maxPDUlength-28; else writeLen=len;
|
|
res=daveWriteBytes(dc,area, DBnum, start, writeLen, pbuf);
|
|
if (res!=0) return res;
|
|
len-=writeLen;
|
|
start+=writeLen;
|
|
pbuf+=writeLen;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int DECL2 daveWriteBytes(daveConnection * dc,int area, int DB, int start, int len, void * buffer) {
|
|
PDU p1,p2;
|
|
int res;
|
|
if (dc->iface->protocol==daveProtoAS511) {
|
|
return daveWriteS5Bytes(dc, area, DB, start, len, buffer);
|
|
}
|
|
p1.header=dc->msgOut+dc->PDUstartO;
|
|
davePrepareWriteRequest(dc, &p1);
|
|
daveAddVarToWriteRequest(&p1, area, DB, start, len, buffer);
|
|
res=_daveExchange(dc, &p1);
|
|
if(res!=daveResOK) return res;
|
|
res=_daveSetupReceivedPDU(dc, &p2);
|
|
if(res!=daveResOK) return res;
|
|
res=_daveTestWriteResult(&p2);
|
|
return res;
|
|
}
|
|
|
|
int DECL2 daveWriteBits(daveConnection * dc,int area, int DB, int start, int len, void * buffer) {
|
|
PDU p1,p2;
|
|
int res;
|
|
p1.header=dc->msgOut+dc->PDUstartO;
|
|
davePrepareWriteRequest(dc,&p1);
|
|
daveAddBitVarToWriteRequest(&p1, area, DB, start, len, buffer);
|
|
res=_daveExchange(dc, &p1);
|
|
if(res!=daveResOK) return res;
|
|
res=_daveSetupReceivedPDU(dc, &p2);
|
|
if (res!=0) return res;
|
|
res=_daveTestWriteResult(&p2);
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
Simplified single bit set:
|
|
*/
|
|
int DECL2 daveSetBit(daveConnection * dc,int area, int DB, int byteAdr, int bitAdr) {
|
|
int a=1;
|
|
return daveWriteBits(dc, area, DB, 8*byteAdr+bitAdr, 1, &a);
|
|
}
|
|
/*
|
|
Simplified single bit clear:
|
|
*/
|
|
int DECL2 daveClrBit(daveConnection * dc,int area, int DB, int byteAdr, int bitAdr) {
|
|
int a=0;
|
|
return daveWriteBits(dc, area, DB, 8*byteAdr+bitAdr, 1, &a);
|
|
}
|
|
|
|
|
|
int DECL2 initUpload(daveConnection * dc,char blockType, int blockNr, int * uploadID){
|
|
PDU p1,p2;
|
|
int res;
|
|
if (daveDebug & daveDebugUpload) {
|
|
LOG1("****initUpload\n");
|
|
}
|
|
p1.header=dc->msgOut+dc->PDUstartO;
|
|
_daveConstructUpload(&p1, blockType, blockNr);
|
|
res=_daveExchange(dc, &p1);
|
|
if (daveDebug & daveDebugUpload) {
|
|
LOG2("error:%d\n", res);
|
|
FLUSH;
|
|
}
|
|
if(res!=daveResOK) return res;
|
|
res=_daveSetupReceivedPDU(dc, &p2);
|
|
if(res!=daveResOK) return res;
|
|
* uploadID=p2.param[7];
|
|
return 0;
|
|
}
|
|
|
|
|
|
int DECL2 doUpload(daveConnection*dc, int * more, uc**buffer, int*len, int uploadID){
|
|
PDU p1,p2;
|
|
int res, netLen;
|
|
p1.header=dc->msgOut+dc->PDUstartO;
|
|
_daveConstructDoUpload(&p1, uploadID);
|
|
res=_daveExchange(dc, &p1);
|
|
if (daveDebug & daveDebugUpload) {
|
|
LOG2("error:%d\n", res);
|
|
FLUSH;
|
|
}
|
|
*more=0;
|
|
if(res!=daveResOK) return res;
|
|
res=_daveSetupReceivedPDU(dc, &p2);
|
|
*more=p2.param[1];
|
|
if(res!=daveResOK) return res;
|
|
// netLen=p2.data[1] /* +256*p2.data[0]; */ /* for long PDUs, I guess it is so */;
|
|
netLen=p2.data[1]+256*p2.data[0]; /* some user confirmed my guess... */;
|
|
if (*buffer) {
|
|
memcpy(*buffer,p2.data+4,netLen);
|
|
*buffer+=netLen;
|
|
if (daveDebug & daveDebugUpload) {
|
|
LOG2("buffer:%p\n",*buffer);
|
|
FLUSH;
|
|
}
|
|
}
|
|
*len+=netLen;
|
|
return res;
|
|
}
|
|
|
|
int DECL2 endUpload(daveConnection*dc, int uploadID){
|
|
PDU p1,p2;
|
|
int res;
|
|
|
|
p1.header=dc->msgOut+dc->PDUstartO;
|
|
_daveConstructEndUpload(&p1,uploadID);
|
|
|
|
res=_daveExchange(dc, &p1);
|
|
if (daveDebug & daveDebugUpload) {
|
|
LOG2("error:%d\n", res);
|
|
FLUSH;
|
|
}
|
|
if(res!=daveResOK) return res;
|
|
res=_daveSetupReceivedPDU(dc, &p2);
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
error code to message string conversion:
|
|
*/
|
|
char * DECL2 daveStrerror(int code) {
|
|
switch (code) {
|
|
case daveResOK: return "ok";
|
|
case daveResMultipleBitsNotSupported:return "the CPU does not support reading a bit block of length<>1";
|
|
case daveResItemNotAvailable: return "the desired item is not available in the PLC";
|
|
case daveResItemNotAvailable200: return "the desired item is not available in the PLC (200 family)";
|
|
case daveAddressOutOfRange: return "the desired address is beyond limit for this PLC";
|
|
case daveResCPUNoData : return "the PLC returned a packet with no result data";
|
|
case daveUnknownError : return "the PLC returned an error code not understood by this library";
|
|
case daveEmptyResultError : return "this result contains no data";
|
|
case daveEmptyResultSetError: return "cannot work with an undefined result set";
|
|
case daveResCannotEvaluatePDU: return "cannot evaluate the received PDU";
|
|
case daveWriteDataSizeMismatch: return "Write data size error";
|
|
case daveResNoPeripheralAtAddress: return "No data from I/O module";
|
|
case daveResUnexpectedFunc: return "Unexpected function code in answer";
|
|
case daveResUnknownDataUnitSize: return "PLC responds with an unknown data type";
|
|
|
|
case daveResShortPacket: return "Short packet from PLC";
|
|
case daveResTimeout: return "Timeout when waiting for PLC response";
|
|
case daveResNoBuffer: return "No buffer provided";
|
|
case daveNotAvailableInS5: return "Function not supported for S5";
|
|
|
|
case 0x8000: return "function already occupied.";
|
|
case 0x8001: return "not allowed in current operating status.";
|
|
case 0x8101: return "hardware fault.";
|
|
case 0x8103: return "object access not allowed.";
|
|
case 0x8104: return "context is not supported. Step7 says:Function not implemented or error in telgram.";
|
|
case 0x8105: return "invalid address.";
|
|
case 0x8106: return "data type not supported.";
|
|
case 0x8107: return "data type not consistent.";
|
|
case 0x810A: return "object does not exist.";
|
|
case 0x8301: return "insufficient CPU memory ?";
|
|
case 0x8402: return "CPU already in RUN or already in STOP ?";
|
|
case 0x8404: return "severe error ?";
|
|
case 0x8500: return "incorrect PDU size.";
|
|
case 0x8702: return "address invalid."; ;
|
|
case 0xd002: return "Step7:variant of command is illegal.";
|
|
case 0xd004: return "Step7:status for this command is illegal.";
|
|
case 0xd0A1: return "Step7:function is not allowed in the current protection level.";
|
|
case 0xd201: return "block name syntax error.";
|
|
case 0xd202: return "syntax error function parameter.";
|
|
case 0xd203: return "syntax error block type.";
|
|
case 0xd204: return "no linked block in storage medium.";
|
|
case 0xd205: return "object already exists.";
|
|
case 0xd206: return "object already exists.";
|
|
case 0xd207: return "block exists in EPROM.";
|
|
case 0xd209: return "block does not exist/could not be found.";
|
|
case 0xd20e: return "no block present.";
|
|
case 0xd210: return "block number too big.";
|
|
// case 0xd240: return "unfinished block transfer in progress?"; // my guess
|
|
case 0xd240: return "Coordination rules were violated.";
|
|
/* Multiple functions tried to manipulate the same object.
|
|
Example: a block could not be copied,because it is already present in the target system
|
|
and
|
|
*/
|
|
case 0xd241: return "Operation not permitted in current protection level.";
|
|
/**/ case 0xd242: return "protection violation while processing F-blocks. F-blocks can only be processed after password input.";
|
|
case 0xd401: return "invalid SZL ID.";
|
|
case 0xd402: return "invalid SZL index.";
|
|
case 0xd406: return "diagnosis: info not available.";
|
|
case 0xd409: return "diagnosis: DP error.";
|
|
case 0xdc01: return "invalid BCD code or Invalid time format?";
|
|
default: return "no message defined!";
|
|
}
|
|
}
|
|
|
|
/*
|
|
Copy an internal String into an external string buffer. This is needed to interface
|
|
with Visual Basic. Maybe it is helpful elsewhere, too.
|
|
*/
|
|
void DECL2 daveStringCopy(char * intString, char * extString) {
|
|
strncpy(extString, intString, 255); // arbritray limit. I hope each external string has at least this
|
|
// capacity
|
|
}
|
|
|
|
/*
|
|
I'm not quite sure whether this is all correct, but it seems to work for all numbers I tested
|
|
*/
|
|
float DECL2 daveGetKGAt(daveConnection * dc,int pos) {
|
|
char kgExponent;
|
|
int sign;
|
|
union {
|
|
uc b[4];
|
|
int mantissa;
|
|
} f;
|
|
union {
|
|
int a;
|
|
float f;
|
|
} v;
|
|
uc* p=dc->_resultPointer+pos;
|
|
kgExponent=*p;
|
|
p++;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
f.b[3]=0;
|
|
f.b[2]=*p;
|
|
p++;
|
|
f.b[1]=*p;
|
|
p++;
|
|
f.b[0]=*p;
|
|
sign=(f.b[2]& 0x80);
|
|
f.b[2]&=0x7f;
|
|
#else
|
|
f.b[0]=0;
|
|
f.b[1]=*p;
|
|
p++;
|
|
f.b[2]=*p;
|
|
p++;
|
|
f.b[3]=*p;
|
|
sign=(f.b[1]& 0x80);
|
|
f.b[1]&=0x7f;
|
|
#endif
|
|
p++;
|
|
LOG3("daveGetKG(dc:%p, mantissa:0x%08X)\n",dc, f.mantissa);
|
|
if(sign) {
|
|
f.mantissa=f.mantissa ^0xffffffff;
|
|
f.mantissa=f.mantissa +0x00800000;
|
|
}
|
|
v.f=f.mantissa;
|
|
if(sign) {
|
|
v.f=-v.f;
|
|
}
|
|
LOG5("daveGetKG(dc:%p, mantissa:0x%08X exponent:0x%02X %0.8f)\n",dc, f.mantissa, kgExponent,v.f);
|
|
while (kgExponent>23) {
|
|
v.f=v.f*2.0;
|
|
kgExponent--;
|
|
}
|
|
while (kgExponent<23) {
|
|
v.f=v.f/2.0;
|
|
kgExponent++;
|
|
}
|
|
LOG2("daveGetKG(%08X)\n",v.a);
|
|
v.f=-v.f;
|
|
LOG2("daveGetKG(%08X)\n",v.a);
|
|
v.f=-v.f;
|
|
#ifdef DEBUG_CALLS
|
|
LOG3("daveGetKG(dc:%p, result:%0.6f)\n", dc, v.f);
|
|
FLUSH;
|
|
#endif
|
|
return (v.f);
|
|
}
|
|
|
|
float DECL2 daveGetKG(daveConnection * dc) {
|
|
float f;
|
|
f=daveGetKGAt(dc, ((int)dc->resultPointer-(int)dc->_resultPointer));
|
|
dc->resultPointer+=4;
|
|
return f;
|
|
}
|
|
|
|
float DECL2 daveGetFloat(daveConnection * dc) {
|
|
union {
|
|
float a;
|
|
uc b[4];
|
|
} f;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
f.b[3]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
f.b[2]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
f.b[1]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
f.b[0]=*(dc->resultPointer);
|
|
#else
|
|
f.b[0]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
f.b[1]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
f.b[2]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
f.b[3]=*(dc->resultPointer);
|
|
#endif
|
|
dc->resultPointer++;
|
|
#ifdef DEBUG_CALLS
|
|
LOG3("daveGetFloat(dc:%p, result:%0.6f)\n", dc, f.a);
|
|
FLUSH;
|
|
#endif
|
|
return (f.a);
|
|
}
|
|
|
|
float DECL2 daveGetFloatAt(daveConnection * dc, int pos) {
|
|
union {
|
|
float a;
|
|
uc b[4];
|
|
} f;
|
|
uc* p=(uc*)dc->_resultPointer;
|
|
p+=pos;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
f.b[3]=*p;p++;
|
|
f.b[2]=*p;p++;
|
|
f.b[1]=*p;p++;
|
|
f.b[0]=*p;
|
|
#else
|
|
f.b[0]=*p;p++;
|
|
f.b[1]=*p;p++;
|
|
f.b[2]=*p;p++;
|
|
f.b[3]=*p;
|
|
#endif
|
|
return (f.a);
|
|
}
|
|
|
|
float DECL2 toPLCfloat(float ff) {
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
union {
|
|
float a;
|
|
uc b[4];
|
|
} f;
|
|
uc c;
|
|
|
|
f.a=ff;
|
|
c=f.b[0];
|
|
f.b[0]=f.b[3];
|
|
f.b[3]=c;
|
|
c=f.b[1];
|
|
f.b[1]=f.b[2];
|
|
f.b[2]=c;
|
|
|
|
// f.a=ff; //fixed bug found by luca at ventisei
|
|
#ifdef DEBUG_CALLS
|
|
LOG3("toPLCfloat(%0.6f) = %0.6f\n",ff,f.a);
|
|
FLUSH;
|
|
#endif
|
|
return (f.a);
|
|
#else
|
|
#ifdef DEBUG_CALLS
|
|
LOG3("toPLCfloat(%0.6f) = %0.6f\n",ff,ff);
|
|
FLUSH;
|
|
#endif
|
|
return ff;
|
|
#endif
|
|
}
|
|
|
|
int DECL2 daveToPLCfloat(float ff) {
|
|
union {
|
|
float a;
|
|
uc b[4];
|
|
int c;
|
|
} f;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
uc c;
|
|
f.a=ff;
|
|
c=f.b[0];
|
|
f.b[0]=f.b[3];
|
|
f.b[3]=c;
|
|
c=f.b[1];
|
|
f.b[1]=f.b[2];
|
|
f.b[2]=c;
|
|
#else
|
|
f.a=ff;
|
|
#endif
|
|
#ifdef DEBUG_CALLS
|
|
LOG3("toPLCfloat(%0.6f) = %08x\n",ff,f.c);
|
|
FLUSH;
|
|
#endif
|
|
return (f.c);
|
|
}
|
|
|
|
int DECL2 daveToKG(float ff) {
|
|
union {
|
|
uc b[4];
|
|
int c;
|
|
} f,f2;
|
|
char kgExponent=23;
|
|
LOG2("daveToKG(%0.8f)\n",ff);
|
|
if (ff==0.0) {
|
|
f.c=0;
|
|
return 0;
|
|
}
|
|
f2.c=(int)ff; // attention! what does this cast? I do want to take the integer part of that float, NOT reinterpret the bit pattern as int!
|
|
LOG4("daveToKG(mantissa:0x%08X exponent:0x%02X %0.8f)\n", f2.c, kgExponent,ff);
|
|
while (f2.c > 0x00400000){
|
|
ff/=2;
|
|
f2.c=(int)ff; // attention! what does this cast? I do want to take the integer part of that float, NOT reinterpret the bit pattern as int!
|
|
kgExponent++;
|
|
}
|
|
while (f2.c < 0x00400000){
|
|
ff*=2;
|
|
f2.c=(int)ff; // attention! what does this cast? I do want to take the integer part of that float, NOT reinterpret the bit pattern as int!
|
|
kgExponent--;
|
|
}
|
|
LOG4("daveToKG(mantissa:0x%08X exponent:0x%02X %0.8f)\n", f2.c, kgExponent,ff);
|
|
f.b[0]=kgExponent;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
f.b[1]=f2.b[2];
|
|
f.b[2]=f2.b[1];
|
|
f.b[3]=f2.b[0];
|
|
#else
|
|
f.b[3]=f2.b[3];
|
|
f.b[2]=f2.b[2];
|
|
f.b[1]=f2.b[1];
|
|
#endif
|
|
#ifdef DEBUG_CALLS
|
|
LOG3("daveToKG(%0.6f) = %08x\n",ff,f.c);
|
|
FLUSH;
|
|
#endif
|
|
return (f.c);
|
|
}
|
|
|
|
|
|
short DECL2 daveSwapIed_16(short ff) {
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
union {
|
|
short a;
|
|
uc b[2];
|
|
} f;
|
|
uc c;
|
|
f.a=ff;
|
|
c=f.b[0];
|
|
f.b[0]=f.b[1];
|
|
f.b[1]=c;
|
|
return (f.a);
|
|
#else
|
|
// printf("Here we are in BIG ENDIAN!!!\n");
|
|
return (ff);
|
|
#endif
|
|
}
|
|
|
|
int DECL2 daveSwapIed_32(int ff) {
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
union {
|
|
int a;
|
|
uc b[4];
|
|
} f;
|
|
uc c;
|
|
f.a=ff;
|
|
c=f.b[0];
|
|
f.b[0]=f.b[3];
|
|
f.b[3]=c;
|
|
c=f.b[1];
|
|
f.b[1]=f.b[2];
|
|
f.b[2]=c;
|
|
return f.a;
|
|
#else
|
|
// printf("Here we are in BIG ENDIAN!!!\n");
|
|
return ff;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
Timer and Counter conversion functions:
|
|
**/
|
|
/*
|
|
get time in seconds from current read position:
|
|
*/
|
|
float DECL2 daveGetSeconds(daveConnection * dc) {
|
|
uc b[2],a;
|
|
float f;
|
|
b[1]=*(dc->resultPointer)++;
|
|
b[0]=*(dc->resultPointer)++;
|
|
f=b[0] & 0xf;
|
|
f+=10*((b[0] & 0xf0)>>4);
|
|
f+=100*(b[1] & 0xf);
|
|
a=((b[1] & 0xf0)>>4);
|
|
switch (a) {
|
|
case 0: f*=0.01;break;
|
|
case 1: f*=0.1;break;
|
|
case 3: f*=10.0;break;
|
|
}
|
|
return (f);
|
|
}
|
|
/*
|
|
get time in seconds from random position:
|
|
*/
|
|
float DECL2 daveGetSecondsAt(daveConnection * dc, int pos) {
|
|
float f;
|
|
uc b[2],a;
|
|
uc* p=(uc*)dc->_resultPointer;
|
|
p+=pos;
|
|
b[1]=*p;
|
|
p++;
|
|
b[0]=*p;
|
|
f=b[0] & 0xf;
|
|
f+=10*((b[0] & 0xf0)>>4);
|
|
f+=100*(b[1] & 0xf);
|
|
a=((b[1] & 0xf0)>>4);
|
|
switch (a) {
|
|
case 0: f*=0.01;break;
|
|
case 1: f*=0.1;break;
|
|
case 3: f*=10.0;break;
|
|
}
|
|
return (f);
|
|
}
|
|
/*
|
|
get counter value from current read position:
|
|
*/
|
|
int DECL2 daveGetCounterValue(daveConnection * dc) {
|
|
uc b[2];
|
|
int f;
|
|
b[1]=*(dc->resultPointer)++;
|
|
b[0]=*(dc->resultPointer)++;
|
|
f=b[0] & 0xf;
|
|
f+=10*((b[0] & 0xf0)>>4);
|
|
f+=100*(b[1] & 0xf);
|
|
return (f);
|
|
}
|
|
/*
|
|
get counter value from random read position:
|
|
*/
|
|
int DECL2 daveGetCounterValueAt(daveConnection * dc,int pos){
|
|
int f;
|
|
uc b[2];
|
|
uc* p=(uc*)dc->_resultPointer;
|
|
p+=pos;
|
|
b[1]=*p;
|
|
p++;
|
|
b[0]=*p;
|
|
f=b[0] & 0xf;
|
|
f+=10*((b[0] & 0xf0)>>4);
|
|
f+=100*(b[1] & 0xf);
|
|
return (f);
|
|
}
|
|
|
|
/*
|
|
dummy functions for protocols not providing a specific function:
|
|
*/
|
|
|
|
int DECL2 _daveReturnOkDummy(daveInterface * di){
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveReturnOkDummy2(daveConnection * dc){
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveListReachablePartnersDummy (daveInterface * di, char * buf) {
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
MPI specific functions:
|
|
*/
|
|
|
|
/*
|
|
This writes a single chracter to the serial interface:
|
|
*/
|
|
|
|
void DECL2 _daveSendSingle(daveInterface * di, /* serial interface */
|
|
uc c /* chracter to be send */
|
|
)
|
|
{
|
|
di->ifwrite(di, (char*)&c, 1);
|
|
}
|
|
|
|
int DECL2 _daveReadSingle(daveInterface * di) {
|
|
char res;
|
|
int i;
|
|
i=di->ifread(di, &res,1);
|
|
if ((daveDebug & daveDebugSpecialChars)!=0)
|
|
LOG3("readSingle %d chars. 1st %02X\n",i,res);
|
|
if (i==1) return res;
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveReadMPI(daveInterface * di, uc *b) {
|
|
int res=0,state=0,nr_read;
|
|
uc bcc=0;
|
|
rep:
|
|
{
|
|
nr_read= di->ifread(di, (char*)(b+res), 1);
|
|
if (nr_read==0) return 0;
|
|
res+=nr_read;
|
|
if ((res==1) && (*(b+res-1)==DLE)) {
|
|
if ((daveDebug & daveDebugSpecialChars)!=0)
|
|
LOG1("readMPI single DLE.\n");
|
|
return 1;
|
|
}
|
|
if ((res==1) && (*(b+res-1)==STX)) {
|
|
if ((daveDebug & daveDebugSpecialChars)!=0)
|
|
LOG1("readMPI single STX.\n");
|
|
return 1;
|
|
}
|
|
if (*(b+res-1)==DLE) {
|
|
if (state==0) {
|
|
state=1;
|
|
/* if ((daveDebug & daveDebugSpecialChars)!=0)
|
|
LOG1("readMPI 1st DLE in data.\n")
|
|
;
|
|
*/
|
|
} else if (state==1) {
|
|
state=0;
|
|
res--; /* forget this DLE */
|
|
/* if ((daveDebug & daveDebugSpecialChars)!=0)
|
|
LOG1("readMPI 2nd DLE in data.\n")
|
|
;
|
|
*/
|
|
}
|
|
}
|
|
if (state==3) {
|
|
if ((daveDebug & daveDebugSpecialChars)!=0)
|
|
LOG3("readMPI: packet end, got BCC: %x. I calc: %x\n",*(b+res-1),bcc);
|
|
if ((daveDebug & daveDebugRawRead)!=0)
|
|
_daveDump("answer",b,res);
|
|
return res;
|
|
} else {
|
|
bcc=bcc^(*(b+res-1));
|
|
}
|
|
|
|
if (*(b+res-1)==ETX) if (state==1) {
|
|
state=3;
|
|
if ((daveDebug & daveDebugSpecialChars)!=0)
|
|
LOG1("readMPI: DLE ETX,packet end.\n");
|
|
}
|
|
goto rep;
|
|
}
|
|
}
|
|
|
|
int DECL2 _daveReadMPI2(daveInterface * di, uc *b) {
|
|
int res=_daveReadMPI(di, b);
|
|
if (res>1) {
|
|
_daveSendSingle(di, DLE);
|
|
_daveSendSingle(di, STX);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int DECL2 _daveGetAck(daveConnection * dc) {
|
|
int res;
|
|
daveInterface * di=dc->iface;
|
|
int nr=dc->needAckNumber;
|
|
uc b1[daveMaxRawLen];
|
|
if (daveDebug & daveDebugPacket)
|
|
LOG2("%s enter getAck ack\n", di->name);
|
|
res = _daveReadMPI(di, b1);
|
|
if (res<0) return res-10;
|
|
if (res!=di->ackPos+6) {
|
|
if (daveDebug & daveDebugPrintErrors) {
|
|
LOG4("%s *** getAck wrong length %d for ack. Waiting for %d\n dump:", di->name, res, nr);
|
|
_daveDump("wrong ack:",b1,res);
|
|
}
|
|
return -1;
|
|
}
|
|
if (b1[di->ackPos]!=0xB0) {
|
|
if (daveDebug & daveDebugPrintErrors) {
|
|
LOG3("%s *** getAck char[6] %x no ack\n", di->name, b1[di->ackPos+2]);
|
|
}
|
|
return -2;
|
|
}
|
|
if (b1[di->ackPos+2]!=nr) {
|
|
if (daveDebug & daveDebugPrintErrors) {
|
|
LOG4("%s *** getAck got: %d need: %d\n", di->name, b1[di->ackPos+2],nr);
|
|
}
|
|
return -3;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define tmo_normal 95000
|
|
|
|
/*
|
|
This reads up to max chracters when it can get them and returns the number:
|
|
*/
|
|
int DECL2 _daveReadChars2(daveInterface * di, /* serial interface */
|
|
uc *b, /* a buffer */
|
|
int max /* limit */
|
|
)
|
|
{
|
|
return di->ifread(di,(char*)b,max);
|
|
}
|
|
|
|
/*
|
|
This sends a string after doubling DLEs in the String
|
|
and adding DLE,ETX and bcc.
|
|
*/
|
|
int DECL2 _daveSendWithCRC(daveInterface * di, /* serial interface */
|
|
uc *b, /* a buffer containing the message */
|
|
int size /* the size of the string */
|
|
)
|
|
{
|
|
uc target[daveMaxRawLen];
|
|
int i,targetSize=0;
|
|
int bcc=DLE^ETX; /* preload */
|
|
for (i=0; i<size; i++) {
|
|
target[targetSize]=b[i];targetSize++;
|
|
if (DLE==b[i]) {
|
|
target[targetSize]=DLE;
|
|
targetSize++;
|
|
} else
|
|
bcc=bcc^b[i]; /* The doubled DLE effectively contributes nothing */
|
|
};
|
|
target[targetSize]=DLE;
|
|
target[targetSize+1]=ETX;
|
|
target[targetSize+2]=bcc;
|
|
targetSize+=3;
|
|
// daveWriteFile(di->fd.wfd, target, targetSize, wr);
|
|
di->ifwrite(di, (char*)target, targetSize);
|
|
if (daveDebug & daveDebugPacket)
|
|
_daveDump("_daveSendWithCRC",target, targetSize);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
This adds a prefix to a string and theen sends it
|
|
after doubling DLEs in the String
|
|
and adding DLE,ETX and bcc.
|
|
*/
|
|
int DECL2 _daveSendWithPrefix(daveConnection * dc, uc *b, int size)
|
|
{
|
|
uc target[daveMaxRawLen];
|
|
uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14};
|
|
uc fix2[]= {0x00,0x0c,0x03,0x03};
|
|
if (dc->iface->protocol==daveProtoMPI2) {
|
|
fix2[2]=dc->connectionNumber2; // 1/10/05 trying Andrew's patch
|
|
fix2[3]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
|
|
memcpy(target,fix2,sizeof(fix2));
|
|
memcpy(target+sizeof(fix2),b,size);
|
|
return _daveSendWithCRC(dc->iface,target,size+sizeof(fix2));
|
|
} else {
|
|
fix[4]=dc->connectionNumber2; // 1/10/05 trying Andrew's patch
|
|
fix[5]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
|
|
memcpy(target,fix,sizeof(fix));
|
|
memcpy(target+sizeof(fix),b,size);
|
|
target[1]|=dc->MPIAdr;
|
|
// target[2]|=dc->iface->localMPI;
|
|
memcpy(target+sizeof(fix),b,size);
|
|
return _daveSendWithCRC(dc->iface,target,size+sizeof(fix));
|
|
}
|
|
}
|
|
|
|
int DECL2 _daveSendWithPrefix2(daveConnection * dc, int size)
|
|
{
|
|
uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14};
|
|
uc fix2[]= {0x00, 0x0C, 0x03, 0x03};
|
|
|
|
if (dc->iface->protocol==daveProtoMPI2) {
|
|
fix2[2]=dc->connectionNumber2; // 1/10/05 trying Andrew's patch
|
|
fix2[3]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
|
|
memcpy(dc->msgOut, fix2, sizeof(fix2));
|
|
dc->msgOut[sizeof(fix2)]=0xF1;
|
|
return _daveSendWithCRC(dc->iface, dc->msgOut, size+sizeof(fix2));
|
|
}
|
|
else if (dc->iface->protocol==daveProtoMPI) {
|
|
fix[4]=dc->connectionNumber2; // 1/10/05 trying Andrew's patch
|
|
fix[5]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
|
|
memcpy(dc->msgOut, fix, sizeof(fix));
|
|
dc->msgOut[1]|=dc->MPIAdr;
|
|
// dc->msgOut[2]|=dc->iface->localMPI; //???
|
|
dc->msgOut[sizeof(fix)]=0xF1;
|
|
/* if (daveDebug & daveDebugPacket)
|
|
_daveDump("_daveSendWithPrefix2",dc->msgOut,size+sizeof(fix)); */
|
|
return _daveSendWithCRC(dc->iface, dc->msgOut, size+sizeof(fix));
|
|
}
|
|
return -1; /* shouldn't happen. */
|
|
}
|
|
|
|
/*
|
|
Sends an ackknowledge message for the message number nr:
|
|
*/
|
|
int DECL2 _daveSendAck(daveConnection * dc, int nr)
|
|
{
|
|
uc m[3];
|
|
if (daveDebug & daveDebugPacket)
|
|
LOG3("%s sendAck for message %d \n", dc->iface->name,nr);
|
|
m[0]=0xB0;
|
|
m[1]=0x01;
|
|
m[2]=nr;
|
|
return _daveSendWithPrefix(dc, m, 3);
|
|
}
|
|
|
|
/*
|
|
Handle MPI message numbers in a central place:
|
|
*/
|
|
int DECL2 _daveIncMessageNumber(daveConnection * dc) {
|
|
int res=dc->messageNumber++;
|
|
// LOG2("_daveIncMessageNumber new number %d \n", dc->messageNumber);
|
|
if ((dc->messageNumber)==0) dc->messageNumber=1;
|
|
return res;
|
|
}
|
|
/*
|
|
Executes part of the dialog necessary to send a message:
|
|
*/
|
|
int DECL2 _daveSendDialog2(daveConnection * dc, int size)
|
|
{
|
|
int a;
|
|
_daveSendSingle(dc->iface, STX);
|
|
if (_daveReadSingle(dc->iface)!=DLE) {
|
|
LOG2("%s *** no DLE before send.\n", dc->iface->name);
|
|
return -1;
|
|
}
|
|
if (size>5){
|
|
dc->needAckNumber=dc->messageNumber;
|
|
dc->msgOut[dc->iface->ackPos+1]=_daveIncMessageNumber(dc);
|
|
}
|
|
_daveSendWithPrefix2(dc, size);
|
|
a=_daveReadSingle(dc->iface);
|
|
if (a!=DLE) {
|
|
LOG3("%s *** no DLE after send(1) %02x.\n", dc->iface->name,a);
|
|
a=_daveReadSingle(dc->iface);
|
|
if (a!=DLE) {
|
|
LOG3("%s *** no DLE after send(2) %02x.\n", dc->iface->name,a);
|
|
_daveSendWithPrefix2(dc, size);
|
|
a=_daveReadSingle(dc->iface);
|
|
if (a!=DLE) {
|
|
LOG3("%s *** no DLE after resend(3) %02x.\n", dc->iface->name,a);
|
|
_daveSendSingle(dc->iface, STX);
|
|
a=_daveReadSingle(dc->iface);
|
|
if (a!=DLE) {
|
|
LOG2("%s *** no DLE before resend.\n", dc->iface->name);
|
|
return -1;
|
|
} else {
|
|
_daveSendWithPrefix2(dc, size);
|
|
a=_daveReadSingle(dc->iface);
|
|
if (a!=DLE) {
|
|
LOG2("%s *** no DLE before resend.\n", dc->iface->name);
|
|
return -1;
|
|
} else {
|
|
LOG2("%s *** got DLE after repeating whole transmisson.\n", dc->iface->name);
|
|
return 0;
|
|
}
|
|
}
|
|
} else
|
|
LOG3("%s *** got DLE after resend(3) %02x.\n", dc->iface->name,a);
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveGetResponseMPI(daveConnection *dc) {
|
|
int res;
|
|
res= _daveReadSingle(dc->iface);
|
|
if (res!=STX) {
|
|
if (daveDebug & daveDebugPrintErrors) {
|
|
LOG2("%s *** _daveGetResponseMPI no STX before answer data.\n", dc->iface->name);
|
|
}
|
|
res= _daveReadSingle(dc->iface);
|
|
}
|
|
_daveSendSingle(dc->iface,DLE);
|
|
if (daveDebug & daveDebugExchange) {
|
|
LOG2("%s _daveGetResponseMPI receive message.\n", dc->iface->name);
|
|
}
|
|
res = _daveReadMPI2(dc->iface,dc->msgIn);
|
|
/* LOG3("%s *** _daveExchange read result %d.\n", dc->iface->name, res); */
|
|
if (res<=0) {
|
|
if (daveDebug & daveDebugPrintErrors) {
|
|
LOG2("%s *** _daveGetResponseMPI no answer data.\n", dc->iface->name);
|
|
}
|
|
return -3;
|
|
}
|
|
/* This is NONSENSE!
|
|
if (daveDebug & daveDebugExchange) {
|
|
LOG3("%s _daveGetResponseMPI got %d bytes\n", dc->iface->name, dc->AnswLen);
|
|
}
|
|
*/
|
|
if (_daveReadSingle(dc->iface)!=DLE) {
|
|
if (daveDebug & daveDebugPrintErrors) {
|
|
LOG2("%s *** _daveGetResponseMPI: no DLE.\n", dc->iface->name);
|
|
}
|
|
return -5;
|
|
}
|
|
_daveSendAck(dc, dc->msgIn[dc->iface->ackPos+1]);
|
|
if (_daveReadSingle(dc->iface)!=DLE) {
|
|
if (daveDebug & daveDebugPrintErrors) {
|
|
LOG2("%s *** _daveGetResponseMPI: no DLE after Ack.\n", dc->iface->name);
|
|
}
|
|
return -6;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Sends a message and gets ackknowledge:
|
|
*/
|
|
int DECL2 _daveSendMessageMPI(daveConnection * dc, PDU * p) {
|
|
if (daveDebug & daveDebugExchange) {
|
|
LOG2("%s enter _daveSendMessageMPI\n", dc->iface->name);
|
|
}
|
|
if (_daveSendDialog2(dc, 2+p->hlen+p->plen+p->dlen)) {
|
|
LOG2("%s *** _daveSendMessageMPI error in _daveSendDialog.\n",dc->iface->name);
|
|
// return -1;
|
|
}
|
|
if (daveDebug & daveDebugExchange) {
|
|
LOG3("%s _daveSendMessageMPI send done. needAck %x\n", dc->iface->name,dc->needAckNumber);
|
|
}
|
|
|
|
if (_daveReadSingle(dc->iface)!=STX) {
|
|
if (daveDebug & daveDebugPrintErrors) {
|
|
LOG2("%s *** _daveSendMessageMPI no STX after _daveSendDialog.\n",dc->iface->name);
|
|
}
|
|
if ( _daveReadSingle(dc->iface)!=STX) {
|
|
if (daveDebug & daveDebugPrintErrors) {
|
|
LOG2("%s *** _daveSendMessageMPI no STX after _daveSendDialog.\n",dc->iface->name);
|
|
}
|
|
return -2;
|
|
} else {
|
|
if (daveDebug & daveDebugPrintErrors) {
|
|
LOG2("%s *** _daveSendMessageMPI got STX after retry.\n",dc->iface->name);
|
|
}
|
|
}
|
|
}
|
|
_daveSendSingle(dc->iface,DLE);
|
|
_daveGetAck(dc);
|
|
_daveSendSingle(dc->iface,DLE);
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveExchangeMPI(daveConnection * dc, PDU * p) {
|
|
_daveSendMessageMPI(dc, p);
|
|
dc->AnswLen=0;
|
|
return _daveGetResponseMPI(dc);
|
|
}
|
|
|
|
/*
|
|
Send a string of init data to the MPI adapter.
|
|
*/
|
|
int DECL2 _daveInitStep(daveInterface * di, int nr, uc *fix, int len, char * caller) {
|
|
_daveSendSingle(di, STX);
|
|
if (_daveReadSingle(di)!=DLE){
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG3("%s %s no answer (DLE) from adapter.\n", di->name, caller);
|
|
if (_daveReadSingle(di)!=DLE){
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG3("%s %s no answer (DLE) from adapter.\n", di->name, caller);
|
|
return nr;
|
|
}
|
|
}
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG4("%s %s step %d.\n", di->name, caller, nr);
|
|
_daveSendWithCRC(di, fix, len);
|
|
if (_daveReadSingle(di)!=DLE) return nr+1;
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG4("%s %s step %d.\n", di->name, caller,nr+1);
|
|
if (_daveReadSingle(di)!=STX) return nr+2;
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG4("%s %s step %d.\n", di->name, caller,nr+2);
|
|
_daveSendSingle(di, DLE);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
This initializes the MPI adapter. Andrew's version.
|
|
*/
|
|
int DECL2 _daveInitAdapterMPI2(daveInterface * di) /* serial interface */
|
|
{
|
|
uc b3[]={
|
|
0x01,0x03,0x02,0x17, 0x00,0x9F,0x01,0x3C,
|
|
0x00,0x90,0x01,0x14, 0x00, /* ^^^ MaxTsdr */
|
|
0x00,0x5,
|
|
0x02,/* Bus speed */
|
|
|
|
0x00,0x0F,0x05,0x01,0x01,0x03,0x80,/* from topserverdemo */
|
|
/*^^ - Local mpi */
|
|
};
|
|
|
|
int res;
|
|
uc b1[daveMaxRawLen];
|
|
b3[16]=di->localMPI;
|
|
if (di->speed==daveSpeed500k)
|
|
b3[7]=0x64;
|
|
if (di->speed==daveSpeed1500k)
|
|
b3[7]=0x96;
|
|
b3[15]=di->speed;
|
|
|
|
res=_daveInitStep(di, 1, b3, sizeof(b3),"initAdapter()");
|
|
|
|
res= _daveReadMPI(di, b1);
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s initAdapter() success.\n", di->name);
|
|
_daveSendSingle(di,DLE);
|
|
di->users=0; /* there cannot be any connections now */
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Initializes the MPI adapter.
|
|
*/
|
|
int DECL2 _daveInitAdapterMPI1(daveInterface * di) {
|
|
uc b2[]={
|
|
0x01,0x0D,0x02,
|
|
};
|
|
// us answ1[]={0x01,0x0D,0x20,'V','0','0','.','8','3'};
|
|
// us adapter0330[]={0x01,0x03,0x20,'E','=','0','3','3','0'};
|
|
// us answ2[]={0x01,0x03,0x20,'V','0','0','.','8','3'};
|
|
us answ1[]={0x01,0x10D,0x20,'V','0','0','.',0x138,0x133};
|
|
us adapter0330[]={0x01,0x03,0x20,'E','=','0','3',0x133,0x130};
|
|
|
|
|
|
uc b3[]={
|
|
0x01,0x03,0x02,0x27, 0x00,0x9F,0x01,0x3C,
|
|
0x00,0x90,0x01,0x14, 0x00,
|
|
0x00,0x05,
|
|
0x02,
|
|
0x00,0x1F,0x02,0x01,0x01,0x03,0x80,
|
|
// ^localMPI
|
|
};
|
|
uc v1[]={
|
|
0x01,0x0C,0x02,
|
|
};
|
|
int res;
|
|
uc b1[daveMaxRawLen];
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s enter initAdapter(1).\n", di->name);
|
|
|
|
res=_daveInitStep(di, 1, b2, sizeof(b2),"initAdapter()");
|
|
if (res) {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s initAdapter() fails.\n", di->name);
|
|
return -44;
|
|
}
|
|
|
|
res= _daveReadMPI(di, b1);
|
|
_daveSendSingle(di,DLE);
|
|
|
|
if (_daveMemcmp(answ1, b1, sizeof(answ1)/2)) return 4;
|
|
|
|
b3[16]=di->localMPI;
|
|
|
|
if (di->speed==daveSpeed500k)
|
|
b3[7]=0x64;
|
|
if (di->speed==daveSpeed1500k)
|
|
b3[7]=0x96;
|
|
b3[15]=di->speed;
|
|
res=_daveInitStep(di, 4, b3, sizeof(b3),"initAdapter()");
|
|
if (res) {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s initAdapter() fails.\n", di->name);
|
|
return -54;
|
|
}
|
|
/*
|
|
The following extra lines seem to be necessary for
|
|
TS adapter 6ES7 972-0CA33-0XAC:
|
|
*/
|
|
res= _daveReadMPI(di, b1);
|
|
_daveSendSingle(di,DLE);
|
|
if (!_daveMemcmp(adapter0330, b1, sizeof(adapter0330)/2)) {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s initAdapter() found Adapter E=0330.\n", di->name);
|
|
_daveSendSingle(di,STX);
|
|
res= _daveReadMPI2(di, b1);
|
|
_daveSendWithCRC(di, v1, sizeof(v1));
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s initAdapter() Adapter E=0330 step 7.\n", di->name);
|
|
if (_daveReadSingle(di)!=DLE) return 8;
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s initAdapter() Adapter E=0330 step 8.\n", di->name);
|
|
res= _daveReadMPI(di, b1);
|
|
if (res!=1 || b1[0]!=STX) return 9;
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s initAdapter() Adapter E=0330 step 9.\n", di->name);
|
|
_daveSendSingle(di,DLE);
|
|
/* This needed the exact Adapter version: */
|
|
/* instead, just read and waste it */
|
|
res= _daveReadMPI(di, b1);
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s initAdapter() Adapter E=0330 step 10.\n", di->name);
|
|
_daveSendSingle(di,DLE);
|
|
return 0;
|
|
|
|
} else if (!_daveMemcmp(answ1, b1, sizeof(answ1)/2)) {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s initAdapter() success.\n", di->name);
|
|
di->users=0; /* there cannot be any connections now */
|
|
return 0;
|
|
} else {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s initAdapter() failed.\n", di->name);
|
|
return -56;
|
|
}
|
|
}
|
|
|
|
us ccrc(uc *b,int size) {
|
|
us sum;
|
|
int i,j,m,lll;
|
|
//initialize for crc
|
|
lll=0xcf87;
|
|
sum=0x7e;
|
|
for(j=2;j<=size;j++) {
|
|
for(m=0;m<=7;m++) {
|
|
if((lll&0x8000)!=0) {
|
|
lll=lll^0x8408;
|
|
lll=lll<<1;
|
|
lll=lll+1;
|
|
} else {
|
|
lll=lll<<1;
|
|
}
|
|
}
|
|
sum=sum^lll;
|
|
}
|
|
for(j=0;j<size;j++) {
|
|
sum=sum ^ b[j];
|
|
for(i=0;i<=7;i++) {
|
|
if(sum&0x01) {
|
|
sum=sum>>1;
|
|
sum=sum^0x8408;
|
|
} else {
|
|
sum=sum>>1;
|
|
}
|
|
}
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
/*
|
|
us ccrc(uc *b, int size, us start) {
|
|
us sum;
|
|
int i, j;
|
|
// LOG3("crc start:%04x size%d\n",start,size);
|
|
sum = start;
|
|
for (j = 0; j < size; j++) {
|
|
sum = sum ^ (b[j]);
|
|
for (i = 0; i <= 7; i++) {
|
|
if (sum & 0x1) {
|
|
sum = sum >> 1;
|
|
sum = sum ^ 0x8408;
|
|
} else
|
|
sum = sum >> 1;
|
|
}
|
|
}
|
|
return sum;
|
|
}
|
|
*/
|
|
/*
|
|
MPI3 has a quite complicated CRC. It seems that a different start value is needed depending
|
|
on length of data. Maybe it only seems so due to my lack of mathematical capabilities...
|
|
I could find values for most message lengths making a CPU produce them. Most of the missing
|
|
ones may never occur at all.
|
|
*/
|
|
/*
|
|
us startTab[]={0x0000 , // 0
|
|
0x0000 , // 1
|
|
0x0000 , // 2
|
|
0xbdb7 , // 3
|
|
0x0000 , // 4
|
|
0x0000 , // 5
|
|
0x0000 , // 6
|
|
0xab86 , // 7
|
|
0x4169 , // 8
|
|
0xc854 , // 9
|
|
0x0000 , // 10
|
|
0x0000 , // 11
|
|
0x0000 , // 12
|
|
0x0000 , // 13
|
|
0x0000 , // 14
|
|
0x0000 , // 15
|
|
0x0000 , // 16
|
|
0x0000 , // 17
|
|
0x2d56 , // 18
|
|
0x0000 , // 19
|
|
0x167a , // 20
|
|
0x0000 , // 21
|
|
0x0000 , // 22
|
|
0xb376 , // 23
|
|
0x0000 , // 24
|
|
0x0000 , // 25
|
|
0x7ca2 , // 26
|
|
0xe0a8 , // 27
|
|
0x23b0 , // 28
|
|
0x1f25 , // 29
|
|
0x61c8 , // 30
|
|
0x6365 , // 31
|
|
0xde47 , // 32
|
|
0x377f , // 33
|
|
0x7171 , // 34
|
|
0x5b75 , // 35
|
|
0x05ee , // 36
|
|
0x7b72 , // 37
|
|
0x08df , // 38
|
|
0x22af , // 39
|
|
0x0834 , // 40
|
|
0xc9af , // 41
|
|
0x6618 , // 42
|
|
0x8b12 , // 43
|
|
0xdf58 , // 44
|
|
0x206e , // 45
|
|
0xd916 , // 46
|
|
0x5e08 , // 47
|
|
0x50bb , // 48
|
|
0x9355 , // 49
|
|
0x59c0 , // 50
|
|
0xa0cc , // 51
|
|
0x53d2 , // 52
|
|
0xe266 , // 53
|
|
0xfd92 , // 54
|
|
0xf07d , // 55
|
|
0x77a0 , // 56
|
|
0xba13 , // 57
|
|
0x5d68 , // 58
|
|
0x2888 , // 59
|
|
0x7f9e , // 60
|
|
0xc49b , // 61
|
|
0x3ac5 , // 62
|
|
0xa3ac , // 63
|
|
0x2be1 , // 64
|
|
0x0ead , // 65
|
|
0x60c9 , // 66
|
|
0x6a74 , // 67
|
|
0x87de , // 68
|
|
0x7394 , // 69
|
|
0xae57 , // 70
|
|
0xb83c , // 71
|
|
0x624a , // 72
|
|
0xf956 , // 73
|
|
0x1439 , // 74
|
|
0x2573 , // 75
|
|
0xec43 , // 76
|
|
0xa87c , // 77
|
|
0xa35a , // 78
|
|
0xdde1 , // 79
|
|
0x894c , // 80
|
|
0x917a , // 81
|
|
0x66e2 , // 82
|
|
0x7112 , // 83
|
|
0x3875 , // 84
|
|
0x038e , // 85
|
|
0x2b14 , // 86
|
|
0xfbad , // 87
|
|
0xff1b , // 88
|
|
0x695f , // 89
|
|
0xb4ed , // 90
|
|
0xd386 , // 91
|
|
0x9ea2 , // 92
|
|
0xc61d , // 93
|
|
0xace7 , // 94
|
|
0x181e , // 95
|
|
0x62bf , // 96
|
|
0x0c56 , // 97
|
|
0x8beb , // 98
|
|
0x2658 , // 99
|
|
0xdf70 , // 100
|
|
0x086e , // 101
|
|
0x93af , // 102
|
|
0xa3c0 , // 103
|
|
0x47e1 , // 104
|
|
0x7032 , // 105
|
|
0x1064 , // 106
|
|
0x5837 , // 107
|
|
0x5fdd , // 108
|
|
0x8daa , // 109
|
|
0x573e , // 110
|
|
0x2e22 , // 111
|
|
0xe5f8 , // 112
|
|
0x5be5 , // 113
|
|
0x95ee , // 114
|
|
0xd2a6 , // 115
|
|
0xb6b3 , // 116
|
|
0x9da4 , // 117
|
|
0xd82e , // 118
|
|
0x6e19 , // 119
|
|
0xca9a , // 120
|
|
0x4b2b , // 121
|
|
0xdafe , // 122
|
|
0xae3b , // 123
|
|
0xd43c , // 124
|
|
0x1cd5 , // 125
|
|
0x89fb , // 126
|
|
0x267a , // 127
|
|
0xfd70 , // 128
|
|
0x127d , // 129
|
|
0x5115 , // 130
|
|
0x3544 , // 131
|
|
0x5a53 , // 132
|
|
0x2bff , // 133
|
|
0x10ad , // 134
|
|
0x9137 , // 135
|
|
0x2be2 , // 136
|
|
0x0dad , // 137
|
|
0x78fa , // 138
|
|
0x98ec , // 139
|
|
0xb87b , // 140
|
|
0x254a , // 141
|
|
0xd543 , // 142
|
|
0x6bc4 , // 143
|
|
0x3fcf , // 144
|
|
0x81f9 , // 145
|
|
0x64f2 , // 146
|
|
0x7130 , // 147
|
|
0x1a75 , // 148
|
|
0x199d , // 149
|
|
0xe9ae , // 150
|
|
0x6d29 , // 151
|
|
0xe2a9 , // 152
|
|
0x3292 , // 153
|
|
0xb424 , // 154
|
|
0x1a86 , // 155
|
|
0xea9d , // 156
|
|
0x461a , // 157
|
|
0x8323 , // 158
|
|
0xaed0 , // 159
|
|
0x3f3c , // 160
|
|
0x72f9 , // 161
|
|
0xcb46 , // 162
|
|
0x9f3a , // 163
|
|
0x560c , // 164
|
|
0x1433 , // 165
|
|
0x2f73 , // 166
|
|
0xbce9 , // 167
|
|
0x970e , // 168
|
|
0x2284 , // 169
|
|
0x2334 , // 170
|
|
0x9b25 , // 171
|
|
0x6948 , // 172
|
|
0xa3ed , // 173
|
|
0x6ae1 , // 174
|
|
0x12de , // 175
|
|
0xf215 , // 176
|
|
0x0f82 , // 177
|
|
0x47d8 , // 178
|
|
0x4932 , // 179
|
|
0xd3dc , // 180
|
|
0xc4a2 , // 181
|
|
0x03c5 , // 182
|
|
0x6014 , // 183
|
|
0xb774 , // 184
|
|
0x52b5 , // 185
|
|
0x8d77 , // 186
|
|
0x8a3e , // 187
|
|
0xfb49 , // 188
|
|
0x1b1b , // 189
|
|
0x7f8c , // 190
|
|
0xd69b , // 191
|
|
0xabf7 , // 192
|
|
0x3069 , // 193
|
|
0x5f06 , // 194
|
|
0x56aa , // 195
|
|
0xb233 , // 196
|
|
0x3de0 , // 197
|
|
0xbedb , // 198
|
|
0xb52c , // 199
|
|
0x1a97 , // 200
|
|
0xfb9d , // 201
|
|
0xcf1b , // 202
|
|
0xe27e , // 203
|
|
0xe592 , // 204
|
|
0x31e5 , // 205
|
|
0xdb17 , // 206
|
|
0x4f2a , // 207
|
|
0xfbba , // 208
|
|
0xe81b , // 209
|
|
0xd038 , // 210
|
|
0x3891 , // 211
|
|
0xe78e , // 212
|
|
0x3dc7 , // 213
|
|
0x99db , // 214
|
|
0x876a , // 215
|
|
0xc794 , // 216
|
|
0x2df6 , // 217
|
|
0x29cb , // 218
|
|
0x348f , // 219
|
|
0x9942 , // 220
|
|
0x1e6a , // 221
|
|
0x26d9 , // 222
|
|
0x5e70 , // 223
|
|
0x28bb , // 224
|
|
0x4c9e , // 225
|
|
0x5789 , // 226
|
|
0x9922 , // 227
|
|
0x7e6a , // 228
|
|
0x388a , // 229
|
|
0xfc8e , // 230
|
|
0xe46c , // 231
|
|
0xc7f4 , // 232
|
|
0x4df6 , // 233
|
|
0x3798 , // 234
|
|
0x9671 , // 235
|
|
0x5595 , // 236
|
|
0x9500 , // 237
|
|
0x3ca6 , // 238
|
|
0xf0ca , // 239
|
|
0xc0a0 , // 240
|
|
0x2181 , // 241
|
|
0x3e07 , // 242
|
|
0x41e8 , // 243
|
|
0x4954 , // 244
|
|
0xb5dc , // 245
|
|
0xea97 , // 246
|
|
0x4c1a , // 247
|
|
0xd389 , // 248
|
|
0x0000 , // 249
|
|
0x0000 , // 250
|
|
0x0000 , // 251
|
|
0x0000 , // 252
|
|
0x0000 , // 253
|
|
0x0000 , // 254
|
|
};
|
|
*/
|
|
int daveSendWithCRC3(daveInterface * di, uc* buffer,int length) {
|
|
uc target[daveMaxRawLen];
|
|
us crc;
|
|
memcpy(target+4,buffer,length);
|
|
target[0]=0x7e;
|
|
if (target[10]==0xB0) {
|
|
target[1]=di->seqNumber+1;
|
|
} else {
|
|
di->seqNumber+=0x11;
|
|
if (di->seqNumber>=0x88) di->seqNumber=0;
|
|
target[1]=di->seqNumber;
|
|
}
|
|
target[2]=(length);
|
|
target[3]=0xff-(length);
|
|
// crc=ccrc(target,length+4,startTab[length]);
|
|
crc=ccrc(target,length+4);
|
|
target[4+length]=crc % 256;
|
|
target[5+length]=crc / 256;
|
|
target[6+length]=0x7e;
|
|
di->ifwrite(di, (char*)target, length+7);
|
|
return 0;
|
|
}
|
|
|
|
int read1(daveInterface * di, uc* b) {
|
|
int len,res;
|
|
if (daveDebug & daveDebugByte)
|
|
LOG1("enter read1\n");
|
|
len=0;
|
|
again:
|
|
res=di->ifread(di, (char*)b, 5);
|
|
if (res==5) {
|
|
if(b[4]==0x7e) goto again;
|
|
if(b[2]==255-b[3]) {
|
|
len=b[2]+7;
|
|
// LOG2("need length %d\n",len);
|
|
while (res<len) {
|
|
res+=di->ifread(di, (char*)(b+res), len-res);
|
|
}
|
|
}
|
|
}
|
|
// LOG3("need length %d got %d\n",len,res);
|
|
if (daveDebug & daveDebugByte)
|
|
_daveDump("got",b,res);
|
|
return res;
|
|
}
|
|
/*
|
|
This initializes the MPI adapter. Step 7 version.
|
|
*/
|
|
int DECL2 _daveInitAdapterMPI3(daveInterface * di)
|
|
{
|
|
uc b2[]={0x7E,0xFC,0x9B,0xCD,0x7E};
|
|
us adapter0330[]={0x01,0x03,0x20,'E','=','0','3','3','0'};
|
|
uc v1[]={0x01,0x0C,0x02};
|
|
|
|
uc b3[]={
|
|
0x01,0x03,0x02,0x17, 0x00,0x9F,0x01,0x3C,
|
|
0x00,0x90,0x01,0x14, 0x00, /* ^^^ MaxTsdr */
|
|
0x00,0x5,
|
|
0x02,/* Bus speed */
|
|
|
|
0x00,0x1F,0x05,0x01,0x01,0x03,0x80,/* from topserverdemo */
|
|
/*^^ - Local mpi */
|
|
};
|
|
uc m4[]={0x7e,0xca,0x2e,0x99,0x7e};
|
|
uc b55[]={0x01,0x08,0x02};
|
|
uc b1[daveMaxRawLen];
|
|
|
|
int res,count;
|
|
|
|
b3[16]=di->localMPI;
|
|
if (di->speed==daveSpeed500k)
|
|
b3[7]=0x64;
|
|
if (di->speed==daveSpeed1500k)
|
|
b3[7]=0x96;
|
|
b3[15]=di->speed;
|
|
count=0;
|
|
again:
|
|
count++;
|
|
if (count>20) return -2;
|
|
di->seqNumber=0x77;
|
|
di->ifwrite(di, (char*)b2, sizeof(b2));
|
|
res=di->ifread(di, (char*)b1, 5);
|
|
if (res==0) {
|
|
di->ifwrite(di, (char*)b2, sizeof(b2));
|
|
res=di->ifread(di, (char*)b1, 5);
|
|
}
|
|
if (res==0) {
|
|
di->ifwrite(di, (char*)b2,sizeof(b2));
|
|
res=di->ifread(di, (char*)b1, 5);
|
|
}
|
|
if (daveDebug & daveDebugByte)
|
|
_daveDump("got", b1, res);
|
|
if (res==5) {
|
|
if (b1[1]==0xCE) {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG1("ok, I begin sequence\n");
|
|
di->seqNumber=0x77;
|
|
} else if (b1[1]==0xCA) {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG1("refused.\n");
|
|
goto again;
|
|
// res=di->ifread(di, b1, 100); //certainly nonsense after a jump
|
|
} else if (b1[1]==0xF8) {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG1("refused.\n");
|
|
di->ifwrite(di, (char*)m4, sizeof(m4));
|
|
res=di->ifread(di, (char*)b1, 100);
|
|
goto again;
|
|
} else if (b1[1]==0x8a) {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG1("in sequence. set to 0x11\n");
|
|
di->seqNumber=0x0;
|
|
} else if (b1[1]==0x8b) {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG1("in sequence. set to 0x22\n");
|
|
di->seqNumber=0x22;
|
|
} else if (b1[1]==0x8c) {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG1("in sequence. set to 0x33\n");
|
|
di->seqNumber=0x33;
|
|
} else if (b1[1]==0x8d) {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG1("in sequence. set to 0x44\n");
|
|
di->seqNumber=0x44;
|
|
}
|
|
} else return -1;
|
|
daveSendWithCRC3(di,b3,sizeof(b3));
|
|
read1(di, b1);
|
|
if (!_daveMemcmp(adapter0330, b1+4, sizeof(adapter0330)/2)) {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s initAdapter() found Adapter E=0330.\n", di->name);
|
|
daveSendWithCRC3(di,v1,sizeof(v1));
|
|
read1(di, b1);
|
|
return 0;
|
|
}
|
|
daveSendWithCRC3(di,b55,sizeof(b55));
|
|
read1(di, b1);
|
|
// daveSendWithCRC3(di,b66,sizeof(b66));
|
|
// read1(di, b1);
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveSendWithPrefix32(daveConnection * dc, int size) {
|
|
uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14};
|
|
fix[4]=dc->connectionNumber2; // 1/10/05 trying Andrew's patch
|
|
fix[5]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
|
|
memcpy(dc->msgOut, fix, sizeof(fix));
|
|
dc->msgOut[1]|=dc->MPIAdr;
|
|
dc->msgOut[sizeof(fix)]=0xF1;
|
|
return daveSendWithCRC3(dc->iface, dc->msgOut, size+sizeof(fix));
|
|
}
|
|
|
|
int DECL2 _daveListReachablePartnersMPI3(daveInterface * di,char * buf) {
|
|
uc b1[daveMaxRawLen];
|
|
uc m1[]={1,7,2};
|
|
int res;
|
|
daveSendWithCRC3(di,m1,sizeof(m1));
|
|
res=read1(di, b1);
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("res:%d\n",res);
|
|
if(140==res){
|
|
memcpy(buf,b1+10,126);
|
|
return 126;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Open connection to a PLC. This assumes that dc is initialized by
|
|
daveNewConnection and is not yet used.
|
|
(or reused for the same PLC ?)
|
|
*/
|
|
int DECL2 _daveConnectPLCMPI3(daveConnection * dc) {
|
|
int res, mpi;
|
|
|
|
PDU p1;
|
|
uc b1[daveMaxRawLen];
|
|
|
|
uc e18[]={0x04,0x82,0x00,
|
|
0x0d,0x00,0x14,0xe0,0x04,0x00,0x80,
|
|
0x00,0x02,0x00,0x02,
|
|
0x01,
|
|
0x00,
|
|
0x01,0x00,
|
|
// 0x02,0x03,0x01,0x00
|
|
};
|
|
uc b4[]={
|
|
0x00,0x0d,0x00,0x03,0xe0,0x04,0x00,0x80,
|
|
0x00,0x02,0x01,0x06,
|
|
0x01,
|
|
0x00,
|
|
0x00,0x01,
|
|
0x02,0x03,0x01,0x00
|
|
/*^^ MPI ADDR */
|
|
};
|
|
|
|
us t4[]={
|
|
0x00,0x0c,0x103,0x103,0xd0,0x04,0x00,0x80,
|
|
0x01,0x06,
|
|
0x00,0x02,0x00,0x01,0x02,
|
|
0x03,0x01,0x00,
|
|
0x01,0x00,0x10,0x03,0x4d
|
|
};
|
|
uc b5[]={
|
|
0x05,0x01,
|
|
};
|
|
|
|
b4[3]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
|
|
b4[sizeof(b4)-3]=dc->MPIAdr;
|
|
t4[15]=dc->MPIAdr;
|
|
t4[sizeof(t4)/2-1]^=dc->MPIAdr; /* 'patch' the checksum */
|
|
mpi=dc->MPIAdr;
|
|
// dc->MPIAdr=2;
|
|
// e18[sizeof(e18)-3]=dc->MPIAdr;
|
|
e18[1]|=dc->MPIAdr;
|
|
daveSendWithCRC3(dc->iface,e18,sizeof(e18));
|
|
read1(dc->iface, b1);
|
|
|
|
// dc->connectionNumber2=b1[3]; // 1/10/05 trying Andrew's patch
|
|
dc->connectionNumber2=b1[9];
|
|
dc->connectionNumber=0x14;
|
|
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC() step 3.\n", dc->iface->name);
|
|
// res=_daveReadMPI(dc->iface,b1);
|
|
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC() step 4.\n", dc->iface->name);
|
|
|
|
_daveSendWithPrefix31(dc, b5, sizeof(b5));
|
|
read1(dc->iface, b1);
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC() step 6.\n", dc->iface->name);
|
|
res= _daveNegPDUlengthRequest(dc, &p1);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
This adds a prefix to a string and theen sends it
|
|
after doubling DLEs in the String
|
|
and adding DLE,ETX and bcc.
|
|
*/
|
|
int DECL2 _daveSendWithPrefix31(daveConnection * dc, uc *b, int size)
|
|
{
|
|
uc target[daveMaxRawLen];
|
|
uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14};
|
|
fix[4]=dc->connectionNumber2; // 1/10/05 trying Andrew's patch
|
|
fix[5]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
|
|
memcpy(target,fix,sizeof(fix));
|
|
memcpy(target+sizeof(fix),b,size);
|
|
target[1]|=dc->MPIAdr;
|
|
memcpy(target+sizeof(fix),b,size);
|
|
return daveSendWithCRC3(dc->iface,target,size+sizeof(fix));
|
|
}
|
|
|
|
/*
|
|
Executes part of the dialog necessary to send a message:
|
|
*/
|
|
int DECL2 _daveSendDialog3(daveConnection * dc, int size)
|
|
{
|
|
if (size>5){
|
|
dc->needAckNumber=dc->messageNumber;
|
|
dc->msgOut[dc->iface->ackPos-dc->PDUstartI+dc->PDUstartO+1]=_daveIncMessageNumber(dc);
|
|
}
|
|
_daveSendWithPrefix32(dc, size);
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveSendMessageMPI3(daveConnection * dc, PDU * p) {
|
|
if (daveDebug & daveDebugExchange) {
|
|
LOG2("%s enter _daveSendMessageMPI3\n", dc->iface->name);
|
|
}
|
|
if (_daveSendDialog3(dc, 2+p->hlen+p->plen+p->dlen)) {
|
|
LOG2("%s *** _daveSendMessageMPI3 error in _daveSendDialog.\n",dc->iface->name);
|
|
// return -1;
|
|
}
|
|
if (daveDebug & daveDebugExchange) {
|
|
LOG3("%s _daveSendMessageMPI send done. needAck %x\n", dc->iface->name,dc->needAckNumber);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Sends an ackknowledge message for the message number nr:
|
|
*/
|
|
int DECL2 _daveSendAckMPI3(daveConnection * dc, int nr)
|
|
{
|
|
uc m[3];
|
|
if (daveDebug & daveDebugPacket)
|
|
LOG3("%s sendAck for message %d \n", dc->iface->name,nr);
|
|
m[0]=0xB0;
|
|
m[1]=0x01;
|
|
m[2]=nr;
|
|
return _daveSendWithPrefix31(dc, m, 3);
|
|
}
|
|
|
|
//#define CRC
|
|
#ifdef CRC
|
|
int testcrc(unsigned char *b, int size, int start)
|
|
{
|
|
unsigned short sum, s2;
|
|
int i, j;
|
|
unsigned char *b1 = b;
|
|
sum = start;
|
|
|
|
for (j = 0; j < size-2; j++) {
|
|
// LOG2("I calc: %x.\n", sum);
|
|
sum = sum ^ (b1[j]);
|
|
// LOG2("after xor data: %x.\n", sum);
|
|
s2 = sum;
|
|
for (i = 0; i <= 7; i++) {
|
|
if (sum & 0x1) {
|
|
sum = sum >> 1;
|
|
sum = sum ^ 0x8408;
|
|
} else
|
|
sum = sum >> 1;
|
|
// LOG2("loop: %x.\n", sum);
|
|
}
|
|
}
|
|
/*
|
|
if (
|
|
((sum /256)==b[size-1]) &&
|
|
((sum %256)==b[size-2])
|
|
) {
|
|
printf ("found 1 %04x \n",start);
|
|
return 1;
|
|
}
|
|
*/
|
|
if (
|
|
((sum %256)==b[size-2]) &&
|
|
((sum /256)==b[size-1])
|
|
){
|
|
printf ("found 2 %04x %d\n",start, size-6);
|
|
startTab[size-6]=start;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int DECL2 _daveGetResponseMPI3(daveConnection *dc) {
|
|
int res,count;
|
|
if (daveDebug & daveDebugExchange)
|
|
LOG1("enter _daveGetResponseMPI3\n");
|
|
count=0;
|
|
dc->msgIn[10]=0;
|
|
do {
|
|
// res=dc->iface->ifread(dc->iface, dc->msgIn, 400);
|
|
res=read1(dc->iface, dc->msgIn);
|
|
count++;
|
|
}while((count<5) && (dc->msgIn[10]!=0xF1));
|
|
if (dc->msgIn[10]==0xF1) {
|
|
dc->iface->seqNumber=dc->msgIn[1];
|
|
_daveSendAckMPI3(dc, dc->msgIn[dc->iface->ackPos+1]);
|
|
#ifdef CRC
|
|
if (startTab[res-7]==0) {
|
|
for(count=0;count<0xffff;count++)
|
|
testcrc(dc->msgIn,res-1, count);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
return -10;
|
|
}
|
|
|
|
|
|
int DECL2 _daveExchangeMPI3(daveConnection * dc, PDU * p) {
|
|
_daveSendMessageMPI3(dc, p);
|
|
dc->AnswLen=0;
|
|
return _daveGetResponseMPI3(dc);
|
|
}
|
|
|
|
int DECL2 _daveDisconnectPLCMPI3(daveConnection * dc)
|
|
{
|
|
// uc m[]={
|
|
// 0x80
|
|
// };
|
|
uc fix[]= {04,0x82,0x0,0x0C,0x03,0x14,0x80};
|
|
fix[4]=dc->connectionNumber2; // 1/10/05 trying Andrew's patch
|
|
fix[5]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
|
|
// _daveSendWithPrefix31(dc, m, 1);
|
|
fix[1]|=dc->MPIAdr;
|
|
daveSendWithCRC3(dc->iface,fix,sizeof(fix));
|
|
read1(dc->iface,dc->msgIn);
|
|
return 0;
|
|
}
|
|
/*
|
|
It seems to be better to complete this subroutine even if answers
|
|
from adapter are not as expected.
|
|
*/
|
|
int DECL2 _daveDisconnectAdapterMPI3(daveInterface * di) {
|
|
// uc m3[]={
|
|
// 0x80
|
|
// };
|
|
uc m2[]={
|
|
1,4,2
|
|
};
|
|
uc b[daveMaxRawLen];
|
|
// uc m4[]={0x7e,0xca,0x2e,0x99,0x7e};
|
|
// _daveSendWithPrefix31(di, m3, sizeof(m3));
|
|
// read1(di,b);
|
|
daveSendWithCRC3(di, m2, sizeof(m2));
|
|
read1(di,b);
|
|
#ifdef CRC
|
|
printf ("\n\n\n\nus startTab[]={");
|
|
for (res=0;res<255;res++) {
|
|
printf ("0x%04x , // %d\n",startTab[res],res);
|
|
}
|
|
printf ("}\n\n\n\n");
|
|
#endif
|
|
// di->ifwrite(di, m4, 5);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
It seems to be better to complete this subroutine even if answers
|
|
from adapter are not as expected.
|
|
*/
|
|
int DECL2 _daveDisconnectAdapterMPI(daveInterface * di) {
|
|
int res;
|
|
uc m2[]={
|
|
1,4,2
|
|
};
|
|
|
|
uc b1[daveMaxRawLen];
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s enter DisconnectAdapter()\n", di->name);
|
|
_daveSendSingle(di, STX);
|
|
res=_daveReadMPI(di,b1);
|
|
/* if ((res!=1)||(b1[0]!=DLE)) return -1; */
|
|
_daveSendWithCRC(di, m2, sizeof(m2));
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s daveDisconnectAdapter() step 1.\n", di->name);
|
|
res=_daveReadMPI(di, b1);
|
|
/* if ((res!=1)||(b1[0]!=DLE)) return -2; */
|
|
res=_daveReadMPI(di, b1);
|
|
/* if ((res!=1)||(b1[0]!=STX)) return -3; */
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s daveDisconnectAdapter() step 2.\n", di->name);
|
|
_daveSendSingle(di, DLE);
|
|
_daveReadChars2(di, b1, daveMaxRawLen);
|
|
// _daveReadChars(di, b1, tmo_normal, daveMaxRawLen);
|
|
_daveSendSingle(di, DLE);
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
_daveDump("got",b1,10);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
This doesn't work yet. I'm not sure whether it is possible to get that
|
|
list after having connected to a PLC.
|
|
*/
|
|
int DECL2 _daveListReachablePartnersMPI(daveInterface * di,char * buf) {
|
|
uc b1[daveMaxRawLen];
|
|
uc m1[]={1,7,2};
|
|
int res;
|
|
res=_daveInitStep(di, 1, m1, sizeof(m1),"listReachablePartners()");
|
|
if (res) return 0;
|
|
res=_daveReadMPI(di,b1);
|
|
// LOG2("res %d\n", res);
|
|
if(136==res){
|
|
_daveSendSingle(di,DLE);
|
|
memcpy(buf,b1+6,126);
|
|
return 126;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveDisconnectPLCMPI(daveConnection * dc)
|
|
{
|
|
int res;
|
|
uc m[]={
|
|
0x80
|
|
};
|
|
uc b1[daveMaxRawLen];
|
|
|
|
_daveSendSingle(dc->iface, STX);
|
|
|
|
res=_daveReadMPI(dc->iface,b1);
|
|
if ((res!=1)||(b1[0]!=DLE)) {
|
|
if (daveDebug & daveDebugPrintErrors)
|
|
LOG2("%s *** no DLE before send.\n", dc->iface->name);
|
|
return -1;
|
|
}
|
|
_daveSendWithPrefix(dc, m, 1);
|
|
|
|
res=_daveReadMPI(dc->iface,b1);
|
|
if ((res!=1)||(b1[0]!=DLE)) {
|
|
if (daveDebug & daveDebugPrintErrors)
|
|
LOG2("%s *** no DLE after send.\n", dc->iface->name);
|
|
return -2;
|
|
}
|
|
|
|
_daveSendSingle(dc->iface, DLE);
|
|
|
|
res=_daveReadMPI(dc->iface,b1);
|
|
if ((res!=1)||(b1[0]!=STX)) return 6;
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveDisConnectPLC() step 6.\n", dc->iface->name);
|
|
res=_daveReadMPI(dc->iface,b1);
|
|
if (daveDebug & daveDebugConnect)
|
|
_daveDump("got",b1,10);
|
|
_daveSendSingle(dc->iface, DLE);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
build the PDU for a PDU length negotiation
|
|
*/
|
|
int DECL2 _daveNegPDUlengthRequest(daveConnection * dc, PDU *p) {
|
|
uc pa[]= {0xF0, 0 ,0, 1, 0, 1, 3, 0xC0,};
|
|
int res;
|
|
int CpuPduLimit;
|
|
PDU p2;
|
|
p->header=dc->msgOut+dc->PDUstartO;
|
|
_daveInitPDUheader(p,1);
|
|
_daveAddParam(p, pa, sizeof(pa));
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(p);
|
|
}
|
|
res=_daveExchange(dc, p);
|
|
if(res!=daveResOK) return res;
|
|
res=_daveSetupReceivedPDU(dc, &p2);
|
|
if(res!=daveResOK) return res;
|
|
CpuPduLimit=daveGetU16from(p2.param+6);
|
|
if (dc->maxPDUlength > CpuPduLimit) dc->maxPDUlength = CpuPduLimit; // use lower number as limit
|
|
if (daveDebug & daveDebugConnect) {
|
|
LOG3("\n*** Partner offered PDU length: %d used limit %d\n\n",CpuPduLimit,dc->maxPDUlength);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
Open connection to a PLC. This assumes that dc is initialized by
|
|
daveNewConnection and is not yet used.
|
|
(or reused for the same PLC ?)
|
|
*/
|
|
int DECL2 _daveConnectPLCMPI2(daveConnection * dc) {
|
|
int res;
|
|
PDU p1;
|
|
uc b1[daveMaxRawLen];
|
|
|
|
uc b4[]={
|
|
0x00,0x0d,0x00,0x03,0xe0,0x04,0x00,0x80,
|
|
0x00,0x02,0x01,0x06,
|
|
0x01,
|
|
0x00,
|
|
0x00,0x01,
|
|
0x02,0x03,0x01,0x00
|
|
/*^^ MPI ADDR */
|
|
};
|
|
|
|
us t4[]={
|
|
0x00,0x0c,0x103,0x103,0xd0,0x04,0x00,0x80,
|
|
0x01,0x06,
|
|
0x00,0x02,0x00,0x01,0x02,
|
|
0x03,0x01,0x00,
|
|
0x01,0x00,0x10,0x03,0x4d
|
|
};
|
|
uc b5[]={
|
|
0x05,0x01,
|
|
};
|
|
|
|
us t5[]={
|
|
0x00,
|
|
0x0c,
|
|
0x103,0x103,0x05,0x01,0x10,0x03,0x1b
|
|
};
|
|
|
|
b4[3]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
|
|
b4[sizeof(b4)-3]=dc->MPIAdr;
|
|
t4[15]=dc->MPIAdr;
|
|
t4[sizeof(t4)/2-1]^=dc->MPIAdr; /* 'patch' the checksum */
|
|
|
|
_daveInitStep(dc->iface, 1, b4, sizeof(b4),"connectPLC(2)");
|
|
res=_daveReadMPI2(dc->iface,b1);
|
|
if (_daveMemcmp(t4, b1, res)) {
|
|
LOG2("%s daveConnectPLC() step 3 ends with 3.\n", dc->iface->name);
|
|
return 3;
|
|
}
|
|
dc->connectionNumber2=b1[3]; // 1/10/05 trying Andrew's patch
|
|
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC() step 4.\n", dc->iface->name);
|
|
res=_daveReadMPI(dc->iface,b1);
|
|
if ((res!=1)||(b1[0]!=DLE)) {
|
|
LOG2("%s daveConnectPLC() step 4 ends with 4.\n", dc->iface->name);
|
|
return 4;
|
|
}
|
|
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC() step 5.\n", dc->iface->name);
|
|
_daveSendWithPrefix(dc, b5, sizeof(b5));
|
|
res=_daveReadMPI(dc->iface,b1);
|
|
if ((res!=1)||(b1[0]!=DLE)) return 5;
|
|
res=_daveReadMPI(dc->iface,b1);
|
|
if ((res!=1)||(b1[0]!=STX)) return 5;
|
|
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC() step 6.\n", dc->iface->name);
|
|
_daveSendSingle(dc->iface, DLE);
|
|
|
|
res=_daveReadMPI(dc->iface,b1);
|
|
_daveSendSingle(dc->iface, DLE);
|
|
if (dc->iface->protocol==daveProtoMPI4) _daveSendSingle(dc->iface, STX);
|
|
if (_daveMemcmp(t5, b1, res)) return 6;
|
|
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC() step 6.\n", dc->iface->name);
|
|
res= _daveNegPDUlengthRequest(dc, &p1);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Open connection to a PLC. This assumes that dc is initialized by
|
|
daveNewConnection and is not yet used.
|
|
(or reused for the same PLC ?)
|
|
*/
|
|
int DECL2 _daveConnectPLCMPI1(daveConnection * dc) {
|
|
int res;
|
|
PDU p1;
|
|
uc b4[]={
|
|
0x04,0x80,0x80,0x0D,0x00,0x14,0xE0,0x04,
|
|
0x00,0x80,0x00,0x02,
|
|
0x00,
|
|
0x02,
|
|
0x01,0x00,
|
|
0x01,0x00,
|
|
};
|
|
|
|
us t4[]={
|
|
0x04,0x80,0x180,0x0C,0x114,0x103,0xD0,0x04, // 1/10/05 trying Andrew's patch
|
|
0x00,0x80,
|
|
0x00,0x02,0x00,0x02,0x01,
|
|
0x00,0x01,0x00,
|
|
};
|
|
uc b5[]={
|
|
0x05,0x01,
|
|
};
|
|
us t5[]={
|
|
0x04,
|
|
0x80,
|
|
0x180,0x0C,0x114,0x103,0x05,0x01,
|
|
};
|
|
b4[1]|=dc->MPIAdr;
|
|
b4[5]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
|
|
|
|
t4[1]|=dc->MPIAdr;
|
|
t5[1]|=dc->MPIAdr;
|
|
|
|
_daveInitStep(dc->iface, 1, b4, sizeof(b4),"connectPLC(1)");
|
|
|
|
res= _daveReadMPI2(dc->iface,dc->msgIn);
|
|
if (_daveMemcmp(t4, dc->msgIn, sizeof(t4)/2)) return 3;
|
|
dc->connectionNumber2=dc->msgIn[5]; // 1/10/05 trying Andrew's patch
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC(1) step 4.\n", dc->iface->name);
|
|
|
|
if (_daveReadSingle(dc->iface)!=DLE) return 4;
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC() step 5.\n", dc->iface->name);
|
|
_daveSendWithPrefix(dc, b5, sizeof(b5));
|
|
if (_daveReadSingle(dc->iface)!=DLE) return 5;
|
|
if (_daveReadSingle(dc->iface)!=STX) return 5;
|
|
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC() step 6.\n", dc->iface->name);
|
|
_daveSendSingle(dc->iface, DLE);
|
|
res= _daveReadMPI2(dc->iface,dc->msgIn);
|
|
if (_daveMemcmp(t5, dc->msgIn, sizeof(t5)/2)) return 6;
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC() step 6.\n", dc->iface->name);
|
|
res= _daveNegPDUlengthRequest(dc, &p1);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Protocol specific functions for ISO over TCP:
|
|
*/
|
|
#ifdef HAVE_SELECT
|
|
int DECL2 _daveReadOne(daveInterface * di, uc *b) {
|
|
fd_set FDS;
|
|
struct timeval t;
|
|
FD_ZERO(&FDS);
|
|
FD_SET(di->fd.rfd, &FDS);
|
|
|
|
t.tv_sec = di->timeout / 1000000;
|
|
t.tv_usec = di->timeout % 1000000;
|
|
/* if (daveDebug & daveDebugByte)
|
|
LOG2("timeout %d\n",di->timeout); */
|
|
if (select(di->fd.rfd + 1, &FDS, NULL, NULL, &t) <= 0)
|
|
{
|
|
if (daveDebug & daveDebugByte) LOG1("timeout in readOne.\n");
|
|
return (0);
|
|
} else {
|
|
return read(di->fd.rfd, b, 1);
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#ifdef BCCWIN
|
|
int DECL2 _daveReadOne(daveInterface * di, uc *b) {
|
|
unsigned long i;
|
|
char res;
|
|
ReadFile(di->fd.rfd, b, 1, &i,NULL);
|
|
return i;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_SELECT
|
|
/*
|
|
Read one complete packet. The bytes 3 and 4 contain length information.
|
|
This version needs a socket filedescriptor that is set to O_NONBLOCK or
|
|
it will hang, if there are not enough bytes to read.
|
|
The advantage may be that the timeout is not used repeatedly.
|
|
*/
|
|
int DECL2 _daveReadISOPacket(daveInterface * di,uc *b) {
|
|
int res,length;
|
|
fd_set FDS;
|
|
struct timeval t;
|
|
FD_ZERO(&FDS);
|
|
FD_SET(di->fd.rfd, &FDS);
|
|
|
|
t.tv_sec = di->timeout / 1000000;
|
|
t.tv_usec = di->timeout % 1000000;
|
|
if (select(di->fd.rfd + 1, &FDS, NULL, NULL, &t) <= 0) {
|
|
if (daveDebug & daveDebugByte) LOG1("timeout in ReadISOPacket.\n");
|
|
return 0;
|
|
} else {
|
|
res=read(di->fd.rfd, b, 4);
|
|
if (res<4) {
|
|
if (daveDebug & daveDebugByte) {
|
|
LOG2("res %d ",res);
|
|
_daveDump("readISOpacket: short packet", b, res);
|
|
}
|
|
return (0); /* short packet */
|
|
}
|
|
length=b[3]+0x100*b[2];
|
|
res+=read(di->fd.rfd, b+4, length-4);
|
|
if (daveDebug & daveDebugByte) {
|
|
LOG3("readISOpacket: %d bytes read, %d needed\n",res, length);
|
|
_daveDump("readISOpacket: packet", b, res);
|
|
}
|
|
return (res);
|
|
}
|
|
}
|
|
/*
|
|
struct timeval t;
|
|
fd_set FDS,EFDS;
|
|
*/
|
|
int DECL2 _daveReadIBHPacket(daveInterface * di,uc *b) {
|
|
struct timeval t;
|
|
fd_set FDS,EFDS;
|
|
int res,length;
|
|
t.tv_sec = di->timeout / 1000000;
|
|
t.tv_usec = di->timeout % 1000000;
|
|
|
|
FD_ZERO(&FDS);
|
|
FD_ZERO(&EFDS);
|
|
FD_SET(di->fd.rfd, &FDS);
|
|
FD_SET(di->fd.rfd, &EFDS);
|
|
|
|
// LOG2("time %d\n",di->timeout);
|
|
// if (select(di->fd.rfd + 1, &FDS, NULL, NULL, &t) <= 0) {
|
|
res= select(di->fd.rfd + 1, &FDS, NULL, &EFDS, &t);
|
|
// LOG2("select returned:%d\n",res);
|
|
// LOG3("rest time %d %d\n",t.tv_sec,t.tv_usec);
|
|
// if FD_ISSET(di->fd.rfd, &FDS)
|
|
// LOG1("true\n");
|
|
// if FD_ISSET(di->fd.rfd, &EFDS)
|
|
// LOG1("e true\n");
|
|
// if (select(di->fd.rfd + 1, &FDS, NULL, NULL, &t) <= 0) {
|
|
if(res<=0) {
|
|
if (daveDebug & daveDebugByte) LOG1("timeout in ReadIBHPacket.\n");
|
|
return 0;
|
|
} else {
|
|
res=read(di->fd.rfd, b, 3);
|
|
if (res==0) {
|
|
t.tv_sec = 0;
|
|
t.tv_usec = 20000;
|
|
res= select(0, NULL, NULL, NULL, &t);
|
|
// LOG3("rest time 2 %d %d\n",t.tv_sec,t.tv_usec);
|
|
}
|
|
// res=recv(di->fd.rfd, b, 3,0);
|
|
if (res<3) {
|
|
if (daveDebug & daveDebugByte) {
|
|
// LOG2("res %d ",res);
|
|
// _daveDump("readIBHpacket: short packet", b, res);
|
|
}
|
|
return (0); /* short packet */
|
|
}
|
|
length=b[2]+8; //b[3]+0x100*b[2];
|
|
res+=read(di->fd.rfd, b+3, length-3);
|
|
if (daveDebug & daveDebugByte) {
|
|
LOG3("readIBHpacket: %d bytes read, %d needed\n",res, length);
|
|
_daveDump("readIBHpacket: packet", b, res);
|
|
}
|
|
return (res);
|
|
}
|
|
}
|
|
#endif /* HAVE_SELECT */
|
|
|
|
#ifdef BCCWIN
|
|
|
|
int DECL2 _daveReadISOPacket(daveInterface * di,uc *b) {
|
|
int res,i,length;
|
|
fd_set FDS;
|
|
struct timeval t;
|
|
FD_ZERO(&FDS);
|
|
FD_SET((SOCKET)(di->fd.rfd), &FDS);
|
|
|
|
t.tv_sec = di->timeout / 1000000;
|
|
t.tv_usec = di->timeout % 1000000;
|
|
if (select(/*di->fd.rfd +*/ 1, &FDS, NULL, NULL, &t) <= 0) {
|
|
if (daveDebug & daveDebugByte) LOG1("timeout in ReadIBHPacket.\n");
|
|
return 0;
|
|
} else {
|
|
i=recv((SOCKET)(di->fd.rfd), b, 4, 0);
|
|
res=i;
|
|
if (res <= 0) {
|
|
if (daveDebug & daveDebugByte) LOG1("timeout in ReadISOPacket.\n");
|
|
return 0;
|
|
} else {
|
|
if (res<4) {
|
|
if (daveDebug & daveDebugByte) {
|
|
LOG2("res %d ",res);
|
|
_daveDump("readISOpacket: short packet", b, res);
|
|
}
|
|
return (0); /* short packet */
|
|
}
|
|
length=b[3]+0x100*b[2];
|
|
i=recv((SOCKET)(di->fd.rfd), b+4, length-4, 0);
|
|
res+=i;
|
|
if (daveDebug & daveDebugByte) {
|
|
LOG3("readISOpacket: %d bytes read, %d needed\n",res, length);
|
|
_daveDump("readISOpacket: packet", b, res);
|
|
}
|
|
return (res);
|
|
}
|
|
}
|
|
}
|
|
|
|
int DECL2 _daveReadIBHPacket(daveInterface * di,uc *b) {
|
|
int res,length;
|
|
fd_set FDS;
|
|
struct timeval t;
|
|
FD_ZERO(&FDS);
|
|
FD_SET((SOCKET)(di->fd.rfd), &FDS);
|
|
|
|
t.tv_sec = di->timeout / 1000000;
|
|
t.tv_usec = di->timeout % 1000000;
|
|
if (select(/*di->fd.rfd +*/ 1, &FDS, NULL, NULL, &t) <= 0) {
|
|
if (daveDebug & daveDebugByte) LOG1("timeout in ReadIBHPacket.\n");
|
|
return 0;
|
|
} else {
|
|
// res=read(di->fd.rfd, b, 3);
|
|
res=recv((SOCKET)(di->fd.rfd), b, 3, 0);
|
|
if (res<3) {
|
|
if (daveDebug & daveDebugByte) {
|
|
LOG2("res %d ",res);
|
|
_daveDump("readIBHpacket: short packet", b, res);
|
|
}
|
|
return (0); /* short packet */
|
|
}
|
|
length=b[2]+8; //b[3]+0x100*b[2];
|
|
// res+=read(di->fd.rfd, b+3, length-3);
|
|
res+=recv((SOCKET)(di->fd.rfd), b+3, length-3, 0);
|
|
if (daveDebug & daveDebugByte) {
|
|
LOG3("readIBHpacket: %d bytes read, %d needed\n",res, length);
|
|
_daveDump("readIBHpacket: packet", b, res);
|
|
}
|
|
return (res);
|
|
}
|
|
}
|
|
|
|
/*
|
|
int DECL2 _daveReadIBHPacket(daveInterface * di,uc *b) {
|
|
int res,i,length;
|
|
i=recv((SOCKET)(di->fd.rfd), b, 3, 0);
|
|
res=i;
|
|
if (res <= 0) {
|
|
if (daveDebug & daveDebugByte) LOG1("timeout in ReadIBHPacket.\n");
|
|
return 0;
|
|
} else {
|
|
if (res<3) {
|
|
if (daveDebug & daveDebugByte) {
|
|
LOG2("res %d ",res);
|
|
_daveDump("readIBHpacket: short packet", b, res);
|
|
}
|
|
return (0); // short packet
|
|
}
|
|
length=b[2]+8; //b[3]+0x100*b[2];
|
|
i=recv((SOCKET)(di->fd.rfd), b+3, length-3, 0);
|
|
res+=i;
|
|
if (daveDebug & daveDebugByte) {
|
|
LOG3("readIBHpacket: %d bytes read, %d needed\n",res, length);
|
|
_daveDump("readIBHpacket: packet", b, res);
|
|
}
|
|
return (res);
|
|
}
|
|
}
|
|
*/
|
|
|
|
#endif /* */
|
|
|
|
int DECL2 _daveSendISOPacket(daveConnection * dc, int size) {
|
|
unsigned long i;
|
|
size+=4;
|
|
*(dc->msgOut+3)=size % 0x100; //was %0xFF, certainly a bug
|
|
*(dc->msgOut+2)=size / 0x100;
|
|
*(dc->msgOut+1)=0;
|
|
*(dc->msgOut+0)=3;
|
|
if (daveDebug & daveDebugByte)
|
|
_daveDump("send packet: ",dc->msgOut,size);
|
|
#ifdef HAVE_SELECT
|
|
daveWriteFile(dc->iface->fd.wfd, dc->msgOut, size, i);
|
|
#endif
|
|
#ifdef BCCWIN
|
|
send((unsigned int)(dc->iface->fd.wfd), dc->msgOut, size, 0);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#define ISOTCPminPacketLength 16
|
|
int DECL2 _daveGetResponseISO_TCP(daveConnection * dc) {
|
|
int res;
|
|
res=_daveReadISOPacket(dc->iface,dc->msgIn);
|
|
if(res==7) {
|
|
if (daveDebug & daveDebugByte)
|
|
LOG1("CPU sends funny 7 byte packets.\n");
|
|
res=_daveReadISOPacket(dc->iface,dc->msgIn);
|
|
}
|
|
if (res==0) return daveResTimeout;
|
|
if (res<ISOTCPminPacketLength) return daveResShortPacket;
|
|
return 0;
|
|
}
|
|
/*
|
|
Executes the dialog around one message:
|
|
*/
|
|
int DECL2 _daveExchangeTCP(daveConnection * dc, PDU * p) {
|
|
int res;
|
|
if (daveDebug & daveDebugExchange) {
|
|
LOG2("%s enter _daveExchangeTCP\n", dc->iface->name);
|
|
}
|
|
*(dc->msgOut+6)=0x80;
|
|
*(dc->msgOut+5)=0xf0;
|
|
*(dc->msgOut+4)=0x02;
|
|
_daveSendISOPacket(dc,3+p->hlen+p->plen+p->dlen);
|
|
res=_daveReadISOPacket(dc->iface,dc->msgIn);
|
|
if(res==7) {
|
|
if (daveDebug & daveDebugByte)
|
|
LOG1("CPU sends funny 7 byte packets.\n");
|
|
res=_daveReadISOPacket(dc->iface,dc->msgIn);
|
|
}
|
|
if (daveDebug & daveDebugExchange) {
|
|
LOG3("%s _daveExchangeTCP res from read %d\n", dc->iface->name,res);
|
|
}
|
|
if (res==0) return daveResTimeout;
|
|
if (res<=ISOTCPminPacketLength) return daveResShortPacket;
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveConnectPLCTCP(daveConnection * dc) {
|
|
int res, success, retries;
|
|
uc b4[]={
|
|
0x11,0xE0,0x00,
|
|
0x00,0x00,0x01,0x00,
|
|
0xC1,2,1,0,
|
|
0xC2,2,0,1,
|
|
0xC0,1,9,
|
|
};
|
|
|
|
uc b4R[]={ // for routing
|
|
6 + 30 + 30 + 3, // Length over all without this byte (fixed
|
|
// Data 6 Bytes + size of Parameters (3 for C0h,30 for C1h+C2h)
|
|
|
|
0xE0, // TDPU Type CR = Connection Request (see RFC1006/ISO8073)
|
|
0x00,0x00, // TPDU Destination Reference (unknown)
|
|
0x00,0x01, // TPDU Source-Reference (my own reference, should not be zero)
|
|
0x00, // TPDU Class 0 and no Option
|
|
|
|
0xC1, // Parameter Source-TSAP
|
|
28, // Length of this parameter
|
|
1, // one block of data (???)
|
|
0, // Length for S7-Subnet-ID
|
|
0, // Length of PLC-Number
|
|
2, // Length of Function/Rack/Slot
|
|
0,0,0,0,0,0,0,0, // empty Data
|
|
0,0,0,0,0,0,0,0,
|
|
0,0,0,0,0,0,
|
|
1, // Function (1=PG,2=OP,3=Step7Basic)
|
|
0, // Rack (Bit 7-5) and Slot (Bit 4-0)
|
|
|
|
0xC2, // Parameter Destination-TSAP
|
|
28, // Length of this parameter
|
|
1, // one block of data (???)
|
|
6, // Length for S7-Subnet-ID
|
|
1, // Length of PLC-Number
|
|
2, // Length of Function/Rack/Slot
|
|
|
|
0xFF,0xFF, // first part of S7-Subnet-ID
|
|
// (look into the S7Project/Network configuration)
|
|
0x00,0x00, // fix always 0000 (reserved for later use ?)
|
|
0xFF,0xFF, // second part of S7-Subnet-ID
|
|
// (see S7Project/Network configuration)
|
|
|
|
18, // PLC-Number (0-126)
|
|
|
|
0,0,0,0,0,0,0,0, // empty
|
|
0,0,0,0,0,0,0,
|
|
|
|
1, // Function (1=PG,2=OP,3=Step7Basic)
|
|
2, // Rack (Bit 7-5) and Slot (Bit 4-0)
|
|
// 0 for slot = let select the plc itself the correct slotnumber
|
|
|
|
0xC0, // Parameter requested TPDU-Size
|
|
1, // Length of this parameter
|
|
9, // requested TPDU-Size 8=256 Bytes, 9=512 Bytes
|
|
};
|
|
|
|
uc b243[]={
|
|
0x11,0xE0,0x00,
|
|
0x00,0x00,0x01,0x00,
|
|
0xC1,2,'M','W',
|
|
0xC2,2,'M','W',
|
|
0xC0,1,9,
|
|
};
|
|
|
|
PDU p1;
|
|
success=0;
|
|
retries=0;
|
|
|
|
if (dc->iface->protocol==daveProtoISOTCP243) {
|
|
memcpy(dc->msgOut+4, b243, sizeof(b243));
|
|
} else if (dc->iface->protocol==daveProtoISOTCP) {
|
|
memcpy(dc->msgOut+4, b4, sizeof(b4));
|
|
// printf("******** do inc %d\n",a);
|
|
dc->msgOut[17]=dc->rack+1;
|
|
dc->msgOut[18]=dc->slot;
|
|
} else {
|
|
memcpy(dc->msgOut+4, b4R, sizeof(b4R)); // with routing over MPI
|
|
dc->msgOut[17]=dc->rack+1; // this is probably wrong
|
|
dc->msgOut[18]=dc->slot;
|
|
}
|
|
|
|
_daveSendISOPacket(dc, sizeof(b4)); /* sizes are identical */
|
|
do {
|
|
res=_daveReadISOPacket(dc->iface,dc->msgIn);
|
|
if (daveDebug & daveDebugConnect) {
|
|
LOG2("%s daveConnectPLC() step 1. ", dc->iface->name);
|
|
_daveDump("got packet: ", dc->msgIn, res);
|
|
}
|
|
if (res==22) {
|
|
success=1;
|
|
} else {
|
|
if (daveDebug & daveDebugPrintErrors){
|
|
LOG2("%s error in daveConnectPLC() step 1. retrying...", dc->iface->name);
|
|
}
|
|
}
|
|
retries++;
|
|
} while ((success==0)&&(retries<3));
|
|
if (success==0) return -1;
|
|
|
|
retries=0;
|
|
do {
|
|
res= _daveNegPDUlengthRequest(dc, &p1);
|
|
if (res==0) {
|
|
return res;
|
|
} else {
|
|
if (daveDebug & daveDebugPrintErrors){
|
|
LOG2("%s error in daveConnectPLC() step 1. retrying...\n", dc->iface->name);
|
|
}
|
|
}
|
|
retries++;
|
|
} while (retries<3);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
Changes:
|
|
07/19/04 removed unused vars.
|
|
*/
|
|
|
|
/*
|
|
Changes:
|
|
07/19/04 added return values in daveInitStep and daveSendWithPrefix2.
|
|
09/09/04 applied patch for variable Profibus speed from Andrew Rostovtsew.
|
|
*/
|
|
|
|
/* PPI specific functions: */
|
|
#define tmo_normalPPI 140000
|
|
|
|
void DECL2 _daveSendLength(daveInterface * di, int len) {
|
|
uc c[]={104,0,0,104};
|
|
c[1]=len;
|
|
c[2]=len;
|
|
di->ifwrite(di, (char *)c, 4);
|
|
if ((daveDebug & daveDebugByte)!=0) {
|
|
_daveDump("I send", c, 4);
|
|
}
|
|
}
|
|
|
|
void DECL2 _daveSendIt(daveInterface * di, uc * b, int size) {
|
|
int i;
|
|
us sum = 0;
|
|
for (i=0;i<size;i++) {
|
|
sum+=b[i];
|
|
}
|
|
sum=sum & 0xff;
|
|
b[size]=sum;
|
|
size++;
|
|
b[size]=SYN;
|
|
size++;
|
|
di->ifwrite(di, (char*)b, size);
|
|
|
|
if ((daveDebug & daveDebugByte)!=0) {
|
|
LOG2("send %d\n",i);
|
|
_daveDump("I send", b, size);
|
|
}
|
|
}
|
|
|
|
void DECL2 _daveSendRequestData(daveConnection * dc,int alt) {
|
|
uc b[]={DLE,0,0,0x5C,0,0};
|
|
b[1]=dc->MPIAdr;
|
|
b[2]=dc->iface->localMPI;
|
|
if(alt) b[3]=0x7c; else b[3]=0x5c;
|
|
dc->iface->ifwrite(dc->iface, (char*)b, 1); //cs:
|
|
_daveSendIt(dc->iface, b+1, sizeof(b)-3);
|
|
}
|
|
|
|
|
|
int seconds, thirds;
|
|
|
|
int DECL2 _daveGetResponsePPI(daveConnection *dc) {
|
|
int res, expectedLen, expectingLength, i, sum, alt;
|
|
uc * b;
|
|
res = 0;
|
|
expectedLen=6;
|
|
expectingLength=1;
|
|
b=dc->msgIn;
|
|
alt=1;
|
|
while ((expectingLength)||(res<expectedLen)) {
|
|
i = _daveReadChars2(dc->iface, dc->msgIn+res, 1);
|
|
res += i;
|
|
if ((daveDebug & daveDebugByte)!=0) {
|
|
LOG3("i:%d res:%d\n",i,res);
|
|
FLUSH;
|
|
}
|
|
if (i == 0) {
|
|
return daveResTimeout;
|
|
} else {
|
|
if ( (expectingLength) && (res==1) && (b[0] == 0xE5)) {
|
|
if(alt) {
|
|
_daveSendRequestData(dc,alt);
|
|
res=0;
|
|
alt=0;
|
|
} else {
|
|
_daveSendRequestData(dc,alt);
|
|
res=0;
|
|
alt=1;
|
|
}
|
|
}
|
|
if ( (expectingLength) && (res>=4) && (b[0] == b[3]) && (b[1] == b[2]) ) {
|
|
expectedLen=b[1]+6;
|
|
expectingLength=0;
|
|
}
|
|
}
|
|
}
|
|
if ((daveDebug & daveDebugByte)!=0) {
|
|
LOG2("res %d testing lastChar\n",res);
|
|
}
|
|
if (b[res-1]!=SYN) {
|
|
LOG1("block format error\n");
|
|
return 1024;
|
|
}
|
|
if ((daveDebug & daveDebugByte)!=0) {
|
|
LOG1("testing check sum\n");
|
|
}
|
|
sum=0;
|
|
for (i=4; i<res-2; i++){
|
|
sum+=b[i];
|
|
}
|
|
sum=sum&0xff;
|
|
if ((daveDebug & daveDebugByte)!=0) {
|
|
LOG3("I calc: %x sent: %x\n", sum, b[res-2]);
|
|
}
|
|
if (b[res-2]!=sum) {
|
|
if ((daveDebug & daveDebugByte)!=0) {
|
|
LOG1("checksum error\n");
|
|
}
|
|
return 2048;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveExchangePPI(daveConnection * dc,PDU * p1) {
|
|
int i,res=0,len;
|
|
dc->msgOut[0]=dc->MPIAdr; /* address ? */
|
|
dc->msgOut[1]=dc->iface->localMPI;
|
|
dc->msgOut[2]=108;
|
|
len=3+p1->hlen+p1->plen+p1->dlen; /* The 3 fix bytes + all parts of PDU */
|
|
_daveSendLength(dc->iface, len);
|
|
_daveSendIt(dc->iface, dc->msgOut, len);
|
|
i = _daveReadChars2(dc->iface, dc->msgIn+res, 1);
|
|
if ((daveDebug & daveDebugByte)!=0) {
|
|
LOG3("i:%d res:%d\n",i,res);
|
|
_daveDump("got",dc->msgIn,i); // 5.1.2004
|
|
}
|
|
if (i == 0) {
|
|
seconds++;
|
|
_daveSendLength(dc->iface, len);
|
|
_daveSendIt(dc->iface, dc->msgOut, len);
|
|
i = _daveReadChars2(dc->iface, dc->msgIn+res, 1);
|
|
if (i == 0) {
|
|
thirds++;
|
|
_daveSendLength(dc->iface, len);
|
|
_daveSendIt(dc->iface, dc->msgOut, len);
|
|
i = _daveReadChars2(dc->iface, dc->msgIn+res, 1);
|
|
if (i == 0) {
|
|
LOG1("timeout in _daveExchangePPI!\n");
|
|
FLUSH;
|
|
return daveResTimeout;
|
|
}
|
|
}
|
|
}
|
|
_daveSendRequestData(dc,0);
|
|
return _daveGetResponsePPI(dc);
|
|
}
|
|
|
|
int DECL2 _daveConnectPLCPPI(daveConnection * dc) {
|
|
PDU p;
|
|
return _daveNegPDUlengthRequest(dc,&p);
|
|
}
|
|
|
|
/*
|
|
"generic" functions calling the protocol specific ones (or the dummies)
|
|
*/
|
|
int DECL2 daveInitAdapter(daveInterface * di) {
|
|
return di->initAdapter(di);
|
|
}
|
|
|
|
int DECL2 daveConnectPLC(daveConnection * dc) {
|
|
return dc->iface->connectPLC(dc);
|
|
}
|
|
|
|
int DECL2 daveDisconnectPLC(daveConnection * dc) {
|
|
return dc->iface->disconnectPLC(dc);
|
|
}
|
|
|
|
int DECL2 daveDisconnectAdapter(daveInterface * di) {
|
|
return di->disconnectAdapter(di);
|
|
}
|
|
|
|
int DECL2 _daveExchange(daveConnection * dc, PDU *p) {
|
|
int res;
|
|
if ((p->header[4]==0)&&(p->header[5]==0)) { /* do not number already numbered PDUs 12/10/04 */
|
|
dc->PDUnumber++;
|
|
if (daveDebug & daveDebugExchange) {
|
|
LOG2("_daveExchange PDU number: %d\n", dc->PDUnumber);
|
|
}
|
|
p->header[5]=dc->PDUnumber % 256; // test!
|
|
p->header[4]=dc->PDUnumber / 256; // test!
|
|
}
|
|
res=dc->iface->exchange(dc, p);
|
|
if (((daveDebug & daveDebugExchange)!=0) ||((daveDebug & daveDebugErrorReporting)!=0)) {
|
|
LOG2("result of exchange: %d\n",res);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int DECL2 daveSendMessage(daveConnection * dc, PDU *p) {
|
|
return dc->iface->sendMessage(dc, p);
|
|
}
|
|
|
|
int DECL2 daveListReachablePartners(daveInterface * di, char * buf) {
|
|
return di->listReachablePartners(di, buf);
|
|
}
|
|
|
|
int DECL2 daveGetResponse(daveConnection * dc) {
|
|
return dc->iface->getResponse(dc);
|
|
}
|
|
|
|
/**
|
|
Newer conversion routines. As the terms WORD, INT, INTEGER etc have different meanings
|
|
for users of different programming languages and compilers, I choose to provide a new
|
|
set of conversion routines named according to the bit length of the value used. The 'U'
|
|
or 'S' stands for unsigned or signed.
|
|
**/
|
|
/*
|
|
Get a value from the position b points to. B is typically a pointer to a buffer that has
|
|
been filled with daveReadBytes:
|
|
*/
|
|
int DECL2 daveGetS8from(uc *b) {
|
|
char* p=(char*)b;
|
|
return *p;
|
|
}
|
|
|
|
int DECL2 daveGetU8from(uc *b) {
|
|
return *b;
|
|
}
|
|
|
|
int DECL2 daveGetS16from(uc *b) {
|
|
union {
|
|
short a;
|
|
uc b[2];
|
|
} u;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
u.b[1]=*b;
|
|
b++;
|
|
u.b[0]=*b;
|
|
#else
|
|
u.b[0]=*b;
|
|
b++;
|
|
u.b[1]=*b;
|
|
#endif
|
|
return u.a;
|
|
}
|
|
|
|
int DECL2 daveGetU16from(uc *b) {
|
|
union {
|
|
unsigned short a;
|
|
uc b[2];
|
|
} u;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
u.b[1]=*b;
|
|
b++;
|
|
u.b[0]=*b;
|
|
#else
|
|
u.b[0]=*b;
|
|
b++;
|
|
u.b[1]=*b;
|
|
#endif
|
|
return u.a;
|
|
}
|
|
|
|
int DECL2 daveGetS32from(uc *b) {
|
|
union {
|
|
int a;
|
|
uc b[4];
|
|
} u;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
u.b[3]=*b;
|
|
b++;
|
|
u.b[2]=*b;
|
|
b++;
|
|
u.b[1]=*b;
|
|
b++;
|
|
u.b[0]=*b;
|
|
#else
|
|
u.b[0]=*b;
|
|
b++;
|
|
u.b[1]=*b;
|
|
b++;
|
|
u.b[2]=*b;
|
|
b++;
|
|
u.b[3]=*b;
|
|
#endif
|
|
return u.a;
|
|
}
|
|
|
|
unsigned int DECL2 daveGetU32from(uc *b) {
|
|
union {
|
|
unsigned int a;
|
|
uc b[4];
|
|
} u;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
u.b[3]=*b;
|
|
b++;
|
|
u.b[2]=*b;
|
|
b++;
|
|
u.b[1]=*b;
|
|
b++;
|
|
u.b[0]=*b;
|
|
#else
|
|
u.b[0]=*b;
|
|
b++;
|
|
u.b[1]=*b;
|
|
b++;
|
|
u.b[2]=*b;
|
|
b++;
|
|
u.b[3]=*b;
|
|
#endif
|
|
return u.a;
|
|
}
|
|
|
|
float DECL2 daveGetFloatfrom(uc *b) {
|
|
union {
|
|
float a;
|
|
uc b[4];
|
|
} u;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
u.b[3]=*b;
|
|
b++;
|
|
u.b[2]=*b;
|
|
b++;
|
|
u.b[1]=*b;
|
|
b++;
|
|
u.b[0]=*b;
|
|
#else
|
|
u.b[0]=*b;
|
|
b++;
|
|
u.b[1]=*b;
|
|
b++;
|
|
u.b[2]=*b;
|
|
b++;
|
|
u.b[3]=*b;
|
|
#endif
|
|
return u.a;
|
|
}
|
|
|
|
/*
|
|
Get a value from the current position in the last result read on the connection dc.
|
|
This will increment an internal pointer, so the next value is read from the position
|
|
following this value.
|
|
*/
|
|
int DECL2 daveGetS8(daveConnection * dc) {
|
|
char * p;
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveGetS8(dc:%p)\n",dc);
|
|
FLUSH;
|
|
#endif
|
|
p=(char *) dc->resultPointer;
|
|
dc->resultPointer++;
|
|
return *p;
|
|
}
|
|
|
|
int DECL2 daveGetU8(daveConnection * dc) {
|
|
uc * p;
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveGetU8(dc:%p)\n",dc);
|
|
FLUSH;
|
|
#endif
|
|
p=dc->resultPointer;
|
|
dc->resultPointer++;
|
|
return *p;
|
|
}
|
|
|
|
int DECL2 daveGetS16(daveConnection * dc) {
|
|
union {
|
|
short a;
|
|
uc b[2];
|
|
} u;
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveGetS16(dc:%p)\n",dc);
|
|
FLUSH;
|
|
#endif
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
u.b[1]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[0]=*(dc->resultPointer);
|
|
#else
|
|
u.b[0]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[1]=*(dc->resultPointer);
|
|
#endif
|
|
dc->resultPointer++;
|
|
return u.a;
|
|
}
|
|
|
|
int DECL2 daveGetU16(daveConnection * dc) {
|
|
union {
|
|
unsigned short a;
|
|
uc b[2];
|
|
} u;
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveGetU16(dc:%p)\n",dc);
|
|
FLUSH;
|
|
#endif
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
u.b[1]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[0]=*(dc->resultPointer);
|
|
#else
|
|
u.b[0]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[1]=*(dc->resultPointer);
|
|
#endif
|
|
dc->resultPointer++;
|
|
return u.a;
|
|
}
|
|
|
|
int DECL2 daveGetS32(daveConnection * dc) {
|
|
union {
|
|
int a;
|
|
uc b[4];
|
|
} u;
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveGetS32(dc:%p)\n",dc);
|
|
FLUSH;
|
|
#endif
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
u.b[3]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[2]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[1]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[0]=*(dc->resultPointer);
|
|
#else
|
|
u.b[0]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[1]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[2]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[3]=*(dc->resultPointer);
|
|
#endif
|
|
dc->resultPointer++;
|
|
return u.a;
|
|
}
|
|
|
|
unsigned int DECL2 daveGetU32(daveConnection * dc) {
|
|
union {
|
|
unsigned int a;
|
|
uc b[4];
|
|
} u;
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveGetU32(dc:%p)\n",dc);
|
|
FLUSH;
|
|
#endif
|
|
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
u.b[3]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[2]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[1]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[0]=*(dc->resultPointer);
|
|
#else
|
|
u.b[0]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[1]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[2]=*(dc->resultPointer);
|
|
dc->resultPointer++;
|
|
u.b[3]=*(dc->resultPointer);
|
|
#endif
|
|
dc->resultPointer++;
|
|
return u.a;
|
|
}
|
|
/*
|
|
Get a value from a given position in the last result read on the connection dc.
|
|
*/
|
|
int DECL2 daveGetS8At(daveConnection * dc, int pos) {
|
|
char * p=(char *)(dc->_resultPointer);
|
|
p+=pos;
|
|
return *p;
|
|
}
|
|
|
|
int DECL2 daveGetU8At(daveConnection * dc, int pos) {
|
|
uc * p=(uc *)(dc->_resultPointer);
|
|
p+=pos;
|
|
return *p;
|
|
}
|
|
|
|
int DECL2 daveGetS16At(daveConnection * dc, int pos) {
|
|
union {
|
|
short a;
|
|
uc b[2];
|
|
} u;
|
|
uc * p=(uc *)(dc->_resultPointer);
|
|
p+=pos;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
u.b[1]=*p;
|
|
p++;
|
|
u.b[0]=*p;
|
|
#else
|
|
u.b[0]=*p;
|
|
p++;
|
|
u.b[1]=*p;
|
|
#endif
|
|
return u.a;
|
|
}
|
|
|
|
int DECL2 daveGetU16At(daveConnection * dc, int pos) {
|
|
union {
|
|
unsigned short a;
|
|
uc b[2];
|
|
} u;
|
|
uc * p=(uc *)(dc->_resultPointer);
|
|
p+=pos;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
u.b[1]=*p;
|
|
p++;
|
|
u.b[0]=*p;
|
|
#else
|
|
u.b[0]=*p;
|
|
p++;
|
|
u.b[1]=*p;
|
|
#endif
|
|
return u.a;
|
|
}
|
|
|
|
int DECL2 daveGetS32At(daveConnection * dc, int pos) {
|
|
union {
|
|
int a;
|
|
uc b[4];
|
|
} u;
|
|
uc * p=dc->_resultPointer;
|
|
p+=pos;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
u.b[3]=*p;
|
|
p++;
|
|
u.b[2]=*p;
|
|
p++;
|
|
u.b[1]=*p;
|
|
p++;
|
|
u.b[0]=*p;
|
|
#else
|
|
u.b[0]=*p;
|
|
p++;
|
|
u.b[1]=*p;
|
|
p++;
|
|
u.b[2]=*p;
|
|
p++;
|
|
u.b[3]=*p;
|
|
#endif
|
|
return u.a;
|
|
}
|
|
|
|
unsigned int DECL2 daveGetU32At(daveConnection * dc, int pos) {
|
|
union {
|
|
unsigned int a;
|
|
uc b[4];
|
|
} u;
|
|
uc * p=(uc *)(dc->_resultPointer);
|
|
p+=pos;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
u.b[3]=*p;
|
|
p++;
|
|
u.b[2]=*p;
|
|
p++;
|
|
u.b[1]=*p;
|
|
p++;
|
|
u.b[0]=*p;
|
|
#else
|
|
u.b[0]=*p;
|
|
p++;
|
|
u.b[1]=*p;
|
|
p++;
|
|
u.b[2]=*p;
|
|
p++;
|
|
u.b[3]=*p;
|
|
#endif
|
|
return u.a;
|
|
}
|
|
/*
|
|
put one byte into buffer b:
|
|
*/
|
|
uc * DECL2 davePut8(uc *b,int v) {
|
|
*b = v & 0xff;
|
|
b++;
|
|
return b;
|
|
}
|
|
|
|
uc * DECL2 davePut16(uc *b,int v) {
|
|
union {
|
|
short a;
|
|
uc b[2];
|
|
} u;
|
|
u.a=v;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
*b=u.b[1];
|
|
b++;
|
|
*b=u.b[0];
|
|
#else
|
|
*b=u.b[0];
|
|
b++;
|
|
*b=u.b[1];
|
|
#endif
|
|
b++;
|
|
return b;
|
|
}
|
|
|
|
uc * DECL2 davePut32(uc *b, int v) {
|
|
union {
|
|
int a;
|
|
uc b[2];
|
|
} u;
|
|
u.a=v;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
*b=u.b[3];
|
|
b++;
|
|
*b=u.b[2];
|
|
b++;
|
|
*b=u.b[1];
|
|
b++;
|
|
*b=u.b[0];
|
|
#else
|
|
*b=u.b[0];
|
|
b++;
|
|
*b=u.b[1];
|
|
b++;
|
|
*b=u.b[2];
|
|
b++;
|
|
*b=u.b[3];
|
|
#endif
|
|
b++;
|
|
return b;
|
|
}
|
|
|
|
uc * DECL2 davePutFloat(uc *b,float v) {
|
|
union {
|
|
float a;
|
|
uc b[2];
|
|
} u;
|
|
u.a=v;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
*b=u.b[3];
|
|
b++;
|
|
*b=u.b[2];
|
|
b++;
|
|
*b=u.b[1];
|
|
b++;
|
|
*b=u.b[0];
|
|
#else
|
|
*b=u.b[0];
|
|
b++;
|
|
*b=u.b[1];
|
|
b++;
|
|
*b=u.b[2];
|
|
b++;
|
|
*b=u.b[3];
|
|
#endif
|
|
b++;
|
|
return b;
|
|
}
|
|
|
|
void DECL2 davePut8At(uc *b, int pos, int v) {
|
|
union {
|
|
short a;
|
|
uc b[2];
|
|
} u;
|
|
u.a=v;
|
|
b+=pos;
|
|
*b=v & 0xff;
|
|
}
|
|
|
|
void DECL2 davePut16At(uc *b, int pos, int v) {
|
|
union {
|
|
short a;
|
|
uc b[2];
|
|
} u;
|
|
u.a=v;
|
|
b+=pos;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
*b=u.b[1];
|
|
b++;
|
|
*b=u.b[0];
|
|
#else
|
|
*b=u.b[0];
|
|
b++;
|
|
*b=u.b[1];
|
|
#endif
|
|
}
|
|
|
|
void DECL2 davePut32At(uc *b, int pos, int v) {
|
|
union {
|
|
int a;
|
|
uc b[2];
|
|
} u;
|
|
u.a=v;
|
|
b+=pos;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
*b=u.b[3];
|
|
b++;
|
|
*b=u.b[2];
|
|
b++;
|
|
*b=u.b[1];
|
|
b++;
|
|
*b=u.b[0];
|
|
#else
|
|
*b=u.b[0];
|
|
b++;
|
|
*b=u.b[1];
|
|
b++;
|
|
*b=u.b[2];
|
|
b++;
|
|
*b=u.b[3];
|
|
#endif
|
|
}
|
|
|
|
void DECL2 davePutFloatAt(uc *b, int pos,float v) {
|
|
union {
|
|
float a;
|
|
uc b[2];
|
|
} u;
|
|
u.a=v;
|
|
b+=pos;
|
|
#ifdef DAVE_LITTLE_ENDIAN
|
|
*b=u.b[3];
|
|
b++;
|
|
*b=u.b[2];
|
|
b++;
|
|
*b=u.b[1];
|
|
b++;
|
|
*b=u.b[0];
|
|
#else
|
|
*b=u.b[0];
|
|
b++;
|
|
*b=u.b[1];
|
|
b++;
|
|
*b=u.b[2];
|
|
b++;
|
|
*b=u.b[3];
|
|
#endif
|
|
}
|
|
/*
|
|
"passive mode" functions. Needed to "simulate" an S7 PLC.
|
|
*/
|
|
userReadFunc readCallBack=NULL;
|
|
userWriteFunc writeCallBack=NULL;
|
|
|
|
void DECL2 _daveConstructReadResponse(PDU * p) {
|
|
uc pa[]={4,1};
|
|
uc da[]={0xFF,4,0,0};
|
|
_daveInitPDUheader(p,3);
|
|
_daveAddParam(p, pa, sizeof(pa));
|
|
_daveAddData(p, da, sizeof(da));
|
|
}
|
|
|
|
void DECL2 _daveConstructBadReadResponse(PDU * p) {
|
|
uc pa[]={4,1};
|
|
uc da[]={0x0A,0,0,0};
|
|
_daveInitPDUheader(p,3);
|
|
_daveAddParam(p, pa, sizeof(pa));
|
|
_daveAddData(p, da, sizeof(da));
|
|
}
|
|
|
|
void DECL2 _daveConstructWriteResponse(PDU * p) {
|
|
uc pa[]={5,1};
|
|
uc da[]={0xFF};
|
|
_daveInitPDUheader(p,3);
|
|
_daveAddParam(p, pa, sizeof(pa));
|
|
_daveAddData(p, da, sizeof(da));
|
|
}
|
|
|
|
void DECL2 _daveHandleRead(PDU * p1,PDU * p2) {
|
|
int result;
|
|
uc * userBytes=NULL; //is this really better than reading from a dangling pointer?
|
|
int bytes=0x100*p1->param[6]+p1->param[7];
|
|
int DBnumber=0x100*p1->param[8]+p1->param[9];
|
|
int area=p1->param[10];
|
|
int start=0x10000*p1->param[11]+0x100*p1->param[12]+p1->param[13];
|
|
LOG5("read %d bytes from %s %d beginning at %d.\n",
|
|
bytes, daveAreaName(area),DBnumber,start);
|
|
if (readCallBack)
|
|
userBytes=readCallBack(area, DBnumber,start, bytes, &result);
|
|
_daveConstructReadResponse(p2);
|
|
_daveAddValue(p2, userBytes, bytes);
|
|
_daveDumpPDU(p2);
|
|
};
|
|
|
|
void DECL2 _daveHandleWrite(PDU * p1,PDU * p2) {
|
|
int result,bytes=0x100*p1->param[6]+p1->param[7];
|
|
int DBnumber=0x100*p1->param[8]+p1->param[9];
|
|
int area=p1->param[10];
|
|
int start=0x10000*p1->param[11]+0x100*p1->param[12]+p1->param[13];
|
|
LOG5("write %d bytes to %s %d beginning at %d.\n",
|
|
bytes, daveAreaName(area),DBnumber,start);
|
|
if (writeCallBack)
|
|
writeCallBack(area, DBnumber,start, bytes, &result, p1->data+4);
|
|
LOG1("after callback\n");
|
|
FLUSH;
|
|
_daveConstructWriteResponse(p2);
|
|
LOG1("after ConstructWriteResponse()\n");
|
|
FLUSH;
|
|
_daveDumpPDU(p2);
|
|
LOG1("after DumpPDU()\n");
|
|
FLUSH;
|
|
};
|
|
|
|
|
|
/*
|
|
10/04/2003 PPI has an address. Implemented now.
|
|
06/03/2004 Fixed a bug in _davePPIexchange, which caused timeouts
|
|
when the first call to readChars returned less then 4 characters.
|
|
*/
|
|
|
|
int DECL2 _daveWriteIBH(daveInterface * di, uc * buffer, int len) { //cs: let it be uc
|
|
int res;
|
|
if (daveDebug & daveDebugByte) {
|
|
_daveDump("writeIBH: ", buffer, len);
|
|
}
|
|
#ifdef HAVE_SELECT
|
|
res=write(di->fd.wfd, buffer, len);
|
|
#endif
|
|
#ifdef BCCWIN
|
|
// res=send((SOCKET)(di->fd.wfd), buffer, ((len+1)/2)*2, 0);
|
|
res=send((SOCKET)(di->fd.wfd), buffer, len, 0);
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
int DECL2 _davePackPDU(daveConnection * dc,PDU *p) {
|
|
IBHpacket * ibhp;
|
|
MPIheader * hm= (MPIheader*) (dc->msgOut+sizeof(IBHpacket)); // MPI headerPDU begins packet header
|
|
hm->MPI=dc->MPIAdr;
|
|
hm->localMPI=dc->iface->localMPI;
|
|
hm->src_conn=dc->ibhSrcConn;
|
|
hm->dst_conn=dc->ibhDstConn;
|
|
hm->len=2+p->hlen+p->plen+p->dlen; // set MPI length
|
|
hm->func=0xf1; // set MPI "function code"
|
|
hm->packetNumber=_daveIncMessageNumber(dc);
|
|
ibhp = (IBHpacket*) dc->msgOut;
|
|
ibhp->ch1=7;
|
|
ibhp->ch2=0xff;
|
|
ibhp->len=hm->len+5;
|
|
ibhp->packetNumber=dc->packetNumber;
|
|
dc->packetNumber++;
|
|
ibhp->rFlags=0x82;
|
|
ibhp->sFlags=0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
uc _MPIack[]={
|
|
0x07,0xff,0x08,0x05,0x00,0x00,0x82,0x00, 0x15,0x14,0x02,0x00,0x03,0xb0,0x01,0x00,
|
|
};
|
|
|
|
|
|
void DECL2 _daveSendMPIAck_IBH(daveConnection*dc) {
|
|
_MPIack[15]=dc->msgIn[16];
|
|
_MPIack[8]=dc->ibhSrcConn;
|
|
_MPIack[9]=dc->ibhDstConn;
|
|
_MPIack[10]=dc->MPIAdr;
|
|
_daveWriteIBH(dc->iface,_MPIack,sizeof(_MPIack));
|
|
}
|
|
|
|
/*
|
|
send a network level ackknowledge
|
|
*/
|
|
void DECL2 _daveSendIBHNetAck(daveConnection * dc) {
|
|
IBHpacket * p;
|
|
uc ack[13];
|
|
memcpy(ack, dc->msgIn, sizeof(ack));
|
|
p= (IBHpacket*) ack;
|
|
p->len=sizeof(ack)-sizeof(IBHpacket);
|
|
ack[11]=1;
|
|
ack[12]=9;
|
|
// LOG2("Sending net level ack for number: %d\n",p->packetNumber);
|
|
_daveWriteIBH(dc->iface, ack,sizeof(ack));
|
|
}
|
|
|
|
uc _MPIconnectResponse[]={
|
|
0xff,0x07,0x13,0x00,0x00,0x00,0xc2,0x02, 0x14,0x14,0x03,0x00,0x00,0x22,0x0c,0xd0,
|
|
0x04,0x00,0x80,0x00,0x02,0x00,0x02,0x01, 0x00,0x01,0x00,
|
|
};
|
|
|
|
/*
|
|
packet analysis. mixes all levels.
|
|
*/
|
|
int DECL2 __daveAnalyze(daveConnection * dc) {
|
|
int haveResp;
|
|
IBHpacket * p, *p2;
|
|
MPIheader * pm;
|
|
MPIheader2 * m2;
|
|
PDU p1;
|
|
#ifdef passiveMode
|
|
PDU pr;
|
|
#endif
|
|
uc resp[2000];
|
|
|
|
// if (dc->AnswLen==0) return _davePtEmpty;
|
|
haveResp=0;
|
|
|
|
p= (IBHpacket*) dc->msgIn;
|
|
dc->needAckNumber=-1; // Assume no ack
|
|
if (daveDebug & daveDebugPacket){
|
|
LOG2("Channel: %d\n",p->ch1);
|
|
LOG2("Channel: %d\n",p->ch2);
|
|
LOG2("Length: %d\n",p->len);
|
|
LOG2("Number: %d\n",p->packetNumber);
|
|
LOG3("sFlags: %04x rFlags:%04x\n",p->sFlags,p->rFlags);
|
|
}
|
|
if (p->rFlags==0x82) {
|
|
pm= (MPIheader*) (dc->msgIn+sizeof(IBHpacket));
|
|
if (daveDebug & daveDebugMPI){
|
|
LOG2("srcconn: %d\n",pm->src_conn);
|
|
LOG2("dstconn: %d\n",pm->dst_conn);
|
|
LOG2("MPI: %d\n",pm->MPI);
|
|
LOG2("MPI len: %d\n",pm->len);
|
|
LOG2("MPI func:%d\n",pm->func);
|
|
}
|
|
if (pm->func==0xf1) {
|
|
if (daveDebug & daveDebugMPI)
|
|
LOG2("MPI packet number: %d needs ackknowledge\n",pm->packetNumber);
|
|
dc->needAckNumber=pm->packetNumber;
|
|
_daveSetupReceivedPDU(dc, &p1);
|
|
#ifdef passiveMode
|
|
// construct response:
|
|
pr.header=resp+sizeof(IBHpacket)+sizeof(MPIheader2);
|
|
#endif
|
|
p2= (IBHpacket*) resp;
|
|
p2->ch1=p->ch2;
|
|
p2->ch2=p->ch1;
|
|
p2->packetNumber=0;
|
|
p2->sFlags=0;
|
|
p2->rFlags=0x2c2;
|
|
|
|
m2= (MPIheader2*) (resp+sizeof(IBHpacket));
|
|
m2->src_conn=pm->src_conn;
|
|
m2->dst_conn=pm->dst_conn;
|
|
m2->MPI=pm->MPI;
|
|
m2->xxx1=0;
|
|
m2->xxx2=0;
|
|
m2->xx22=0x22;
|
|
if (p1.param[0]==daveFuncRead) {
|
|
#ifdef passiveMode
|
|
_daveHandleRead(&p1,&pr);
|
|
haveResp=1;
|
|
m2->len=pr.hlen+pr.plen+pr.dlen+2;
|
|
#endif
|
|
p2->len=m2->len+7;
|
|
} else if (p1.param[0]==daveFuncWrite) {
|
|
#ifdef passiveMode
|
|
_daveHandleWrite(&p1,&pr);
|
|
haveResp=1;
|
|
m2->len=pr.hlen+pr.plen+pr.dlen+2;
|
|
#endif
|
|
p2->len=m2->len+7;
|
|
} else {
|
|
if (daveDebug & daveDebugPDU)
|
|
LOG2("Unsupported PDU function code: %d\n",p1.param[0]);
|
|
return _davePtUnknownPDUFunc;
|
|
}
|
|
|
|
}
|
|
if (pm->func==0xb0) {
|
|
LOG2("Ackknowledge for packet number: %d\n",*(dc->msgIn+15));
|
|
return _davePtMPIAck;
|
|
}
|
|
if (pm->func==0xe0) {
|
|
LOG2("Connect to MPI: %d\n",pm->MPI);
|
|
memcpy(resp, _MPIconnectResponse, sizeof(_MPIconnectResponse));
|
|
resp[8]=pm->src_conn;
|
|
resp[9]=pm->src_conn;
|
|
resp[10]=pm->MPI;
|
|
haveResp=1;
|
|
}
|
|
}
|
|
|
|
if (p->rFlags==0x2c2) {
|
|
MPIheader2 * pm= (MPIheader2*) (dc->msgIn+sizeof(IBHpacket));
|
|
if (daveDebug & daveDebugMPI) {
|
|
LOG2("srcconn: %d\n",pm->src_conn);
|
|
LOG2("dstconn: %d\n",pm->dst_conn);
|
|
LOG2("MPI: %d\n",pm->MPI);
|
|
LOG2("MPI len: %d\n",pm->len);
|
|
LOG2("MPI func:%d\n",pm->func);
|
|
}
|
|
if (pm->func==0xf1) {
|
|
if (daveDebug & daveDebugMPI)
|
|
LOG1("analyze 1\n");
|
|
dc->needAckNumber=pm->packetNumber;
|
|
dc->PDUstartI= sizeof(IBHpacket)+sizeof(MPIheader2);
|
|
_daveSendMPIAck_IBH(dc);
|
|
|
|
return 55;
|
|
|
|
/*
|
|
if (daveDebug & daveDebugMPI)
|
|
LOG2("MPI packet number: %d\n",pm->packetNumber);
|
|
dc->needAckNumber=pm->packetNumber;
|
|
// p1.header=((uc*)pm)+sizeof(MPIheader2);
|
|
dc->PDUstartI= sizeof(IBHpacket)+sizeof(MPIheader2);
|
|
_daveSetupReceivedPDU(dc, &p1);
|
|
|
|
if (p1.param[0]==daveFuncRead) {
|
|
LOG1("read Response\n");
|
|
_daveSendMPIAck_IBH(dc);
|
|
dc->resultPointer=p1.data+4;
|
|
dc->_resultPointer=p1.data+4;
|
|
dc->AnswLen=p1.dlen-4;
|
|
return _davePtReadResponse;
|
|
} else if (p1.param[0]==daveFuncWrite) {
|
|
_daveSendMPIAck_IBH(dc);
|
|
LOG1("write Response\n");
|
|
return _davePtWriteResponse;
|
|
} else {
|
|
LOG2("Unsupported PDU function code: %d\n",p1.param[0]);
|
|
}
|
|
*/
|
|
}
|
|
|
|
if (pm->func==0xb0) {
|
|
if (daveDebug & daveDebugMPI)
|
|
LOG2("Ackknowledge for packet number: %d\n",pm->packetNumber);
|
|
} else {
|
|
LOG2("Unsupported MPI function code !!: %d\n",pm->func);
|
|
_daveSendMPIAck_IBH(dc);
|
|
}
|
|
}
|
|
/*
|
|
Sending IBHNetAck also for packets with sFlags=082 nearly doubles the speed for LINUX and
|
|
speeds up windows version to the level of LINUX.
|
|
Thanks to Axel Kinting for this proposal and for his patience finding it out!
|
|
*/
|
|
if (((p->sFlags==0x82)||(p->sFlags==0x82))&&(p->packetNumber)&&(p->len)) _daveSendIBHNetAck(dc);
|
|
if (haveResp) {
|
|
_daveWriteIBH(dc->iface, resp, resp[2]+8);
|
|
_daveDump("I send response:", resp, resp[2]+8);
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
int DECL2 _daveExchangeIBH(daveConnection * dc, PDU * p) {
|
|
|
|
_daveSendMessageMPI_IBH(dc, p);
|
|
dc->AnswLen=0;
|
|
return _daveGetResponseMPI_IBH(dc);
|
|
}
|
|
|
|
int DECL2 _daveSendMessageMPI_IBH(daveConnection * dc, PDU * p) {
|
|
int res;
|
|
_davePackPDU(dc, p);
|
|
res=_daveWriteIBH(dc->iface, dc->msgOut, dc->msgOut[2]+8);
|
|
if (daveDebug & daveDebugPDU)
|
|
_daveDump("I send request: ",dc->msgOut, dc->msgOut[2]+8);
|
|
return res;
|
|
}
|
|
|
|
int DECL2 _daveGetResponseMPI_IBH(daveConnection * dc) {
|
|
int res,count,pt;
|
|
count=0;
|
|
pt=0;
|
|
do {
|
|
res=_daveReadIBHPacket(dc->iface, dc->msgIn);
|
|
count++;
|
|
if(res>4)
|
|
pt=__daveAnalyze(dc);
|
|
if (daveDebug & daveDebugExchange)
|
|
LOG2("ExchangeIBH packet type:%d\n",pt);
|
|
} while ((pt!=55)&&(count<5));
|
|
if(pt!=55) return daveResTimeout;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
This performs initialization steps with sampled byte sequences. If chal is <>NULL
|
|
it will send this byte sequence.
|
|
It will then wait for a packet and compare it to the sample.
|
|
*/
|
|
int DECL2 _daveInitStepIBH(daveInterface * iface, uc * chal, int cl, us* resp,int rl, uc*b) {
|
|
int res, res2, a=0;
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG1("_daveInitStepIBH before write.\n");
|
|
if (chal) res=_daveWriteIBH(iface, chal, cl);else res=daveResInvalidParam;
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("_daveInitStepIBH write returned %d.\n",res);
|
|
if (res<0) return 100;
|
|
res=_daveReadIBHPacket(iface, b);
|
|
/*
|
|
We may get a network layer ackknowledge and an MPI layer ackknowledge, which we discard.
|
|
So, normally at least the 3rd packet should have the desired response.
|
|
Waiting for more does:
|
|
-discard extra packets resulting from last step.
|
|
-extend effective timeout.
|
|
*/
|
|
while (a<5) {
|
|
if (a) {
|
|
res=_daveReadIBHPacket(iface, b);
|
|
// _daveDump("got:",b,res);
|
|
}
|
|
if (res>0) {
|
|
res2=_daveMemcmp(resp,b,rl/2);
|
|
if (0==res2) {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG3("*** Got response %d %d\n",res,rl);
|
|
return a;
|
|
} else {
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("wrong! %d\n",res2);
|
|
}
|
|
}
|
|
a++;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
/*
|
|
uc chal0[]={
|
|
0x00,0xff,0x03,0x00,0x00,0x00,0x04,0x00, 0x03,0x07,0x02,
|
|
};
|
|
*/
|
|
/*
|
|
us resp0[]={
|
|
0xff,0x00,0xfe,0x00, 0x04,0x00,0x00,0x00,
|
|
0x00,0x07,0x02,0x03,0x1f,
|
|
0x100, // MPI address of NetLink
|
|
0x02,0x00,
|
|
0x103, // 187,5, MPI = 3
|
|
// 19,5, PB = 1
|
|
0x00,0x00,0x00,0x102,
|
|
0x00,0x02,0x3e,
|
|
0x19f, // 187,5, MPI = 9F
|
|
// 19,5, PB = 64
|
|
0x101, // 187,5, MPI = 1
|
|
// 19,5, PB = 0
|
|
|
|
0x101, // 187,5, MPI = 1
|
|
// 19,5, PB = 0
|
|
0x00,
|
|
0x102, // 187,5, MPI = 1
|
|
// 19,5, PB = 2
|
|
0x00,
|
|
0x116, // 187,5, MPI = 3c
|
|
// 19,5, PB = 16
|
|
0x00,
|
|
0x13c, // 187,5, MPI = 90
|
|
// 19,5, PB = 3c
|
|
|
|
0x101,
|
|
0x110,
|
|
0x127,0x100,0x100,0x114,0x101, 0x100,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x0e,
|
|
'H', 'i','l','s','c','h','e','r',' ','G','m','b','H',' ',
|
|
|
|
0x200, / * do not compare the rest * /
|
|
};
|
|
*/
|
|
|
|
uc chal1[]={
|
|
0x07,0xff,0x08,0x01,0x00,0x00,0x96,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x01,
|
|
};
|
|
|
|
us resp1[]={
|
|
0xff,0x07,0x87,0x01,0x96,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x7f,0x0a,0x02,
|
|
};
|
|
/*
|
|
uc chal2[]={
|
|
0x00,0x10,0x01,0x00,0x00,0x00,0x01,0x00, 0x0f,
|
|
};
|
|
*/
|
|
/*
|
|
us resp2[]={
|
|
0x10,0x00,
|
|
0x20,
|
|
0x00,
|
|
0x01,0x00,0x00,0x00,
|
|
0x01,0x106,0x120,0x103,0x17,0x00,0x43,0x00,
|
|
0x00,0x00,
|
|
0x122,0x121,
|
|
0x00,0x00,0x00,0x00, 0x49,0x42,0x48,0x53,0x00,0x00,0x00,0x00,
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
};
|
|
*/
|
|
uc chal3[]={
|
|
0x07,0xff,0x06,0x01,0x00,0x00,0x97,0x00, 0x15,0xff,0xf0,0xf0,0xf0,0xf0,
|
|
};
|
|
|
|
us resp3[]={
|
|
0xff,0x107,0x02,0x01,0x97,0x00,0x00,0x00, 0x114,0x100,
|
|
};
|
|
|
|
uc chal8[]={
|
|
0x07,0xff,0x11,0x02,0x00,0x00,0x82,0x00, 0x14,0x00,0x02,0x01,0x0c,0xe0,0x04,0x00,
|
|
0x80,0x00,0x02,0x00,0x02,0x01,0x00,0x01, 0x00,
|
|
};
|
|
|
|
/* This is the correct response. I just inserted a "don't care" to make it work with latest
|
|
IBH simulator. Better fix the simulator!
|
|
us resp7[]={
|
|
0xff,0x07,0x13,0x00,0x00,0x00,0xc2,0x02, 0x115,0x114,0x102,0x100,0x00,0x22,0x0c,0xd0,
|
|
0x04,0x00,0x80,0x00,0x02,0x00,0x02,0x01, 0x00,0x01,0x00,
|
|
};
|
|
*/
|
|
us resp7[]={
|
|
0xff,0x07,0x13,0x00,0x00,0x00,0xc2,0x02, 0x115,0x114,0x102,0x100,0x00,0x22,0x0c,0xd0,
|
|
0x04,0x00,0x80,0x00,0x02,0x00,0x102,0x01, 0x00,0x01,0x00,
|
|
};
|
|
|
|
uc chal011[]={
|
|
0x07,0xff,0x07,0x03,0x00,0x00,0x82,0x00, 0x15,0x14,0x02,0x00,0x02,0x05,0x01,
|
|
};
|
|
|
|
us resp09[]={
|
|
0xff,0x07,0x09,0x00,0x00,0x00,0xc2,0x02, 0x115,0x114,0x102,0x100,0x00,0x22,0x02,0x05,
|
|
0x101, // A.K.
|
|
};
|
|
|
|
|
|
/*
|
|
Connect to a PLC via IBH-NetLink. Returns 0 for success and a negative number on errors.
|
|
*/
|
|
int DECL2 _daveConnectPLC_IBH(daveConnection*dc) {
|
|
int a, retries;
|
|
PDU p1;
|
|
uc b[daveMaxRawLen];
|
|
dc->iface->timeout=500000;
|
|
dc->iface->localMPI=0;
|
|
dc->ibhSrcConn=20-1;
|
|
dc->ibhDstConn=20-1;
|
|
retries=0;
|
|
do {
|
|
if (daveDebug & daveDebugConnect) // show only if in debug mode
|
|
LOG1("trying next ID:\n");
|
|
dc->ibhSrcConn++;
|
|
chal3[8]=dc->ibhSrcConn;
|
|
a=_daveInitStepIBH(dc->iface, chal3,sizeof(chal3),resp3,sizeof(resp3),b);
|
|
retries++;
|
|
} while ((b[9]!=0) && (retries<10));
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("_daveInitStepIBH 4:%d\n",a); if (a>3) /* !!! */ return -4;;
|
|
chal8[10]=dc->MPIAdr;
|
|
// LOG2("setting MPI %d\n",dc->MPIAdr);
|
|
chal8[8]=dc->ibhSrcConn;
|
|
a=_daveInitStepIBH(dc->iface, chal8,sizeof(chal8),resp7,sizeof(resp7),b);
|
|
dc->ibhDstConn=b[9];
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG3("_daveInitStepIBH 5:%d connID: %d\n",a, dc->ibhDstConn); if (a>3) return -5;
|
|
|
|
chal011[8]=dc->ibhSrcConn;
|
|
chal011[9]=dc->ibhDstConn;
|
|
chal011[10]=dc->MPIAdr; //??????
|
|
a=_daveInitStepIBH(dc->iface, chal011,sizeof(chal011),resp09,sizeof(resp09),b);
|
|
|
|
dc->ibhDstConn=b[9];
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG3("_daveInitStepIBH 5a:%d connID: %d\n",a, dc->ibhDstConn); if (a>3) return -5;
|
|
|
|
dc->packetNumber=4;
|
|
return _daveNegPDUlengthRequest(dc, &p1);
|
|
}
|
|
|
|
uc chal31[]={
|
|
0x07,0xff,0x06,0x08,0x00,0x00,0x82,0x00, 0x14,0x14,0x02,0x00,0x01,0x80,
|
|
};
|
|
|
|
/*
|
|
Disconnect from a PLC via IBH-NetLink.
|
|
Returns 0 for success and a negative number on errors.
|
|
*/
|
|
int DECL2 _daveDisconnectPLC_IBH(daveConnection*dc) {
|
|
uc b[daveMaxRawLen];
|
|
chal31[8]=dc->ibhSrcConn;
|
|
chal31[9]=dc->ibhDstConn;
|
|
chal31[10]=dc->MPIAdr;
|
|
_daveWriteIBH(dc->iface, chal31, sizeof(chal31));
|
|
_daveReadIBHPacket(dc->iface, b);
|
|
#ifdef BCCWIN
|
|
#else
|
|
_daveReadIBHPacket(dc->iface, b);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Disconnect from a PLC via IBH-NetLink. This can be used to free other than your own
|
|
connections. Be careful, this may disturb third party programs/devices.
|
|
*/
|
|
int DECL2 daveForceDisconnectIBH(daveInterface * di, int src, int dest, int mpi) {
|
|
uc b[daveMaxRawLen];
|
|
chal31[8]=src;
|
|
chal31[9]=dest;
|
|
chal31[10]=mpi;
|
|
_daveWriteIBH(di, chal31, sizeof(chal31));
|
|
_daveReadIBHPacket(di, b);
|
|
#ifdef BCCWIN
|
|
#else
|
|
_daveReadIBHPacket(di, b);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Resets the IBH-NetLink.
|
|
Returns 0 for success and a negative number on errors.
|
|
*/
|
|
int DECL2 daveResetIBH(daveInterface * di) {
|
|
uc chalReset[]={
|
|
0x00,0xff,0x01,0x00,0x00,0x00,0x01,0x00,0x01
|
|
};
|
|
uc b[daveMaxRawLen];
|
|
_daveWriteIBH(di, chalReset, sizeof(chalReset));
|
|
_daveReadIBHPacket(di, b);
|
|
#ifdef BCCWIN
|
|
#else
|
|
_daveReadIBHPacket(di, b);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void DECL2 _daveSendMPIAck2(daveConnection *dc) {
|
|
IBHpacket * p;
|
|
uc c;
|
|
uc ack[18];
|
|
memcpy(ack,dc->msgIn,sizeof(ack));
|
|
p= (IBHpacket*) ack;
|
|
p->rFlags|=0x240; //Why that?
|
|
c=p->ch1; p->ch1=p->ch2; p->ch2=c;
|
|
p->len=sizeof(ack)-sizeof(IBHpacket);
|
|
p->packetNumber=0; // this may mean: no net work level acknowledge
|
|
ack[13]=0x22;
|
|
ack[14]=3;
|
|
ack[15]=176;
|
|
ack[16]=1;
|
|
ack[17]=dc->needAckNumber;
|
|
_daveDump("send MPI-Ack2",ack,sizeof(ack));
|
|
_daveWriteIBH(dc->iface,ack,sizeof(ack));
|
|
};
|
|
|
|
int DECL2 _davePackPDU_PPI(daveConnection * dc,PDU *p) {
|
|
IBHpacket * ibhp;
|
|
uc IBHPPIHeader[]={0xff,0xff,2,0,8};
|
|
IBHPPIHeader[2]=dc->MPIAdr;
|
|
memcpy(dc->msgOut+sizeof(IBHpacket), &IBHPPIHeader, sizeof(IBHPPIHeader));
|
|
|
|
ibhp = (IBHpacket*) dc->msgOut;
|
|
ibhp->ch1=7;
|
|
ibhp->ch2=0xff;
|
|
ibhp->len=p->hlen+p->plen+p->dlen+5; // set MPI length
|
|
// dc->msgOut[3]=2;
|
|
*(dc->msgOut+sizeof(IBHpacket)+4)=p->hlen+p->plen+p->dlen+0;
|
|
ibhp->packetNumber=dc->packetNumber;
|
|
ibhp->rFlags=0x80;
|
|
ibhp->sFlags=0;
|
|
// dc->msgOut[3]=dc->packetNumber;
|
|
dc->packetNumber++;
|
|
dc->msgOut[6]=0x82;
|
|
dc->msgOut[3]=2;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
send a network level ackknowledge
|
|
*/
|
|
void DECL2 _daveSendIBHNetAckPPI(daveConnection * dc) {
|
|
uc ack[]={7,0xff,5,3,0,0,0x82,0,0xff,0xff,2,0,0};
|
|
ack[10]=dc->MPIAdr;
|
|
ack[3]=_daveIncMessageNumber(dc);
|
|
_daveWriteIBH(dc->iface, ack, sizeof(ack));
|
|
}
|
|
|
|
int DECL2 _daveListReachablePartnersMPI_IBH(daveInterface * di, char * buf) {
|
|
int a, i;
|
|
uc b[2*daveMaxRawLen];
|
|
a=_daveInitStepIBH(di, chal1,sizeof(chal1),resp1,16,b);
|
|
if (daveDebug & daveDebugListReachables)
|
|
LOG2("_daveListReachablePartnersMPI_IBH:%d\n",a);
|
|
for (i=0;i<126;i++) {
|
|
if (b[i+16]==0xFF) buf[i]=0x10; else buf[i]=0x30;
|
|
// LOG3(" %d %d\n",i, b[i+16]);
|
|
}
|
|
return 126;
|
|
}
|
|
|
|
/*
|
|
packet analysis. mixes all levels.
|
|
*/
|
|
int DECL2 __daveAnalyzePPI(daveConnection * dc, uc sa) {
|
|
IBHpacket* p;
|
|
|
|
p= (IBHpacket*) dc->msgIn;
|
|
if (daveDebug & daveDebugPacket){
|
|
LOG2("Channel: %d\n",p->ch1);
|
|
LOG2("Channel: %d\n",p->ch2);
|
|
LOG2("Length: %d\n",p->len);
|
|
LOG2("Number: %d\n",p->packetNumber);
|
|
LOG3("sFlags: %04x rFlags:%04x\n",p->sFlags,p->rFlags);
|
|
}
|
|
if (p->sFlags==0x82) {
|
|
if(p->len<=5) {
|
|
if(sa) _daveSendIBHNetAckPPI(dc);
|
|
} else
|
|
if ((p->len>=7) && (dc->msgIn[14]==0x32))
|
|
return 55;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
int DECL2 _daveExchangePPI_IBH(daveConnection * dc, PDU * p) {
|
|
int res, count, pt;
|
|
_davePackPDU_PPI(dc, p);
|
|
|
|
res=_daveWriteIBH(dc->iface, dc->msgOut, dc->msgOut[2]+8);
|
|
if (daveDebug & daveDebugExchange)
|
|
_daveDump("I send request: ",dc->msgOut, dc->msgOut[2]+8);
|
|
// _daveSendIBHNetAckPPI(dc);
|
|
count=0;
|
|
do {
|
|
res=_daveReadIBHPacket(dc->iface, dc->msgIn);
|
|
count++;
|
|
if (res>0)
|
|
pt=__daveAnalyzePPI(dc,1);
|
|
else
|
|
pt=0;
|
|
if (daveDebug & daveDebugExchange)
|
|
LOG2("ExchangeIBH packet type:%d\n",pt);
|
|
} while ((pt!=55)&&(count<7));
|
|
if(pt!=55) return daveResTimeout;
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveGetResponsePPI_IBH(daveConnection * dc) {
|
|
int res, count, pt;
|
|
count=0;
|
|
do {
|
|
_daveSendIBHNetAckPPI(dc);
|
|
res=_daveReadIBHPacket(dc->iface, dc->msgIn);
|
|
LOG2("_daveReadIBHPacket():%d\n",res);
|
|
count++;
|
|
if (res>0)
|
|
pt=__daveAnalyzePPI(dc,0);
|
|
else
|
|
pt=0;
|
|
if (daveDebug & daveDebugExchange)
|
|
LOG2("ExchangeIBH packet type:%d\n",pt);
|
|
} while ((pt!=55)&&(count<7));
|
|
if(pt!=55) return daveResTimeout;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Build a PDU with data from 2 data blocks.
|
|
*/
|
|
int DECL2 BuildAndSendPDU(daveConnection * dc, PDU*p2,uc *pa,int psize, uc *ud,int usize,
|
|
uc *ud2,int usize2) {
|
|
int res;
|
|
PDU p,*p3;
|
|
uc * dn;
|
|
p.header=dc->msgOut+dc->PDUstartO;
|
|
_daveInitPDUheader(&p, 7);
|
|
_daveAddParam(&p, pa, psize);
|
|
_daveAddUserData(&p, ud, usize);
|
|
// LOG2("*** here we are: %d\n",p.dlen);
|
|
p3=&p;
|
|
dn= p3->data+p3->dlen;
|
|
p3->dlen+=usize2;
|
|
memcpy(dn, ud2, usize2);
|
|
|
|
((PDUHeader*)p3->header)->dlen=daveSwapIed_16(p3->dlen);
|
|
|
|
LOG2("*** here we are: %d\n",p.dlen);
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(&p);
|
|
}
|
|
res=_daveExchange(dc, &p);
|
|
if (daveDebug & daveDebugErrorReporting)
|
|
LOG2("*** res of _daveExchange(): %d\n",res);
|
|
if (res!=daveResOK) return res;
|
|
res=_daveSetupReceivedPDU(dc,p2);
|
|
if (daveDebug & daveDebugPDU) {
|
|
_daveDumpPDU(p2);
|
|
}
|
|
if (daveDebug & daveDebugErrorReporting)
|
|
LOG2("*** res of _daveSetupReceivedPDU(): %d\n",res);
|
|
if (res!=daveResOK) return res;
|
|
res=_daveTestPGReadResult(p2);
|
|
if (daveDebug & daveDebugErrorReporting)
|
|
LOG2("*** res of _daveTestPGReadResult(): %d\n",res);
|
|
return res;
|
|
}
|
|
|
|
int DECL2 daveForce200(daveConnection * dc,int area, int start, int val) {
|
|
int res;
|
|
PDU p2;
|
|
// uc pa[]={0,1,18,4,17,67,2,0};
|
|
// uc da[]={'0','0'};
|
|
|
|
//32,7,0,0,0,0,0,c,0,16,
|
|
|
|
uc pa[]={0,1,18,8,18,72,14,0,0,0,0,0};
|
|
uc da[]={0,1,0x10,2,
|
|
0,1,
|
|
0,0,
|
|
0, // area
|
|
0,0,0, // start
|
|
};
|
|
uc da2[]={0,4,0,8,0,0,};
|
|
// uc da2[]={0,4,0,8,7,0,};
|
|
|
|
if ((area==daveAnaIn) || (area==daveAnaOut) /*|| (area==daveP)*/) {
|
|
da[3]=4;
|
|
start*=8; /* bits */
|
|
} else if ((area==daveTimer) || (area==daveCounter)||(area==daveTimer200) || (area==daveCounter200)) {
|
|
da[3]=area;
|
|
} else {
|
|
start*=8;
|
|
}
|
|
/* else {
|
|
if(isBit) {
|
|
pa[3]=1;
|
|
} else {
|
|
start*=8;
|
|
}
|
|
}
|
|
*/
|
|
da[8]=area;
|
|
da[9]=start / 0x10000;
|
|
da[10]=(start / 0x100) & 0xff;
|
|
da[11]=start & 0xff;
|
|
|
|
|
|
da2[4]=val % 0x100;
|
|
da2[5]=val / 0x100;
|
|
res=BuildAndSendPDU(dc, &p2, pa, sizeof(pa), da, sizeof(da), da2, sizeof(da2));
|
|
return res;
|
|
}
|
|
|
|
daveResultSet * DECL2 daveNewResultSet() {
|
|
daveResultSet * p=(daveResultSet*) calloc(1, sizeof(daveResultSet));
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveNewResultSet() = %p\n",p);
|
|
FLUSH;
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
void DECL2 daveFree(void * dc) {
|
|
// if (dc!=NULL) { // I'm not sure whether freeing a NULL pointer will do no harm on each and
|
|
// every system. So for safety, we check and set it to NULL afterwards.
|
|
free(dc);
|
|
// }
|
|
}
|
|
|
|
int DECL2 daveGetProgramBlock(daveConnection * dc, int blockType, int number, char* buffer, int * length) {
|
|
int res, uploadID, len, more, totlen;
|
|
uc *bb=(uc*)buffer; //cs: is this right?
|
|
len=0;
|
|
totlen=0;
|
|
if (dc->iface->protocol==daveProtoAS511) {
|
|
return daveGetS5ProgramBlock(dc, blockType, number, buffer, length);
|
|
}
|
|
|
|
res=initUpload(dc, blockType, number, &uploadID);
|
|
if (res!=0) return res;
|
|
do {
|
|
res=doUpload(dc,&more,&bb,&len,uploadID);
|
|
totlen+=len;
|
|
if (res!=0) return res;
|
|
} while (more);
|
|
res=endUpload(dc,uploadID);
|
|
*length=totlen;
|
|
return res;
|
|
}
|
|
|
|
int DECL2 daveReadPLCTime(daveConnection * dc) {
|
|
int res, len;
|
|
PDU p2;
|
|
uc pa[]={0,1,18,4,17,'G',1,0};
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveGetTime(dc:%p)\n", dc);
|
|
FLUSH;
|
|
#endif
|
|
len=0;res=daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), NULL, 1);
|
|
if (res==daveResOK) {
|
|
dc->resultPointer=p2.udata;
|
|
dc->_resultPointer=p2.udata;
|
|
len=p2.udlen;
|
|
} else {
|
|
if(daveDebug & daveDebugPrintErrors)
|
|
LOG3("daveGetTime: %04X=%s\n",res, daveStrerror(res));
|
|
}
|
|
dc->AnswLen=len;
|
|
return res;
|
|
}
|
|
|
|
int DECL2 daveSetPLCTime(daveConnection * dc,uc * ts) {
|
|
int res, len;
|
|
PDU p2;
|
|
uc pa[]={0,1,18,4,17,'G',2,0};
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("daveSetTime(dc:%p)\n", dc);
|
|
FLUSH;
|
|
#endif
|
|
len=0;
|
|
res=daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), ts, 10);
|
|
if (res==daveResOK) {
|
|
dc->resultPointer=p2.udata;
|
|
dc->_resultPointer=p2.udata;
|
|
len=p2.udlen;
|
|
} else {
|
|
if(daveDebug & daveDebugPrintErrors)
|
|
LOG3("daveGetTime: %04X=%s\n",res, daveStrerror(res));
|
|
}
|
|
dc->AnswLen=len;
|
|
return res;
|
|
}
|
|
|
|
uc DECL2 daveToBCD(uc i) {
|
|
return 16*(i /10)+(i%10);
|
|
}
|
|
|
|
uc DECL2 daveFromBCD(uc i) {
|
|
return 10*(i /16)+(i%16);
|
|
}
|
|
|
|
int DECL2 daveSetPLCTimeToSystime(daveConnection * dc) {
|
|
int res, len;
|
|
PDU p2;
|
|
uc pa[]={0,1,18,4,17,'G',2,0};
|
|
uc ts[]={
|
|
0x00,0x19,0x05,0x08,0x23,0x04,0x10,0x23,0x67,0x83,
|
|
};
|
|
#ifdef LINUX
|
|
struct tm systime;
|
|
struct timeval t1;
|
|
gettimeofday(&t1, NULL);
|
|
localtime_r(&(t1.tv_sec),&systime);
|
|
t1.tv_usec/=100; //tenth of miliseconds from microseconds
|
|
// ts[1]=daveToBCD(systime.tm_year/100+19);
|
|
ts[2]=daveToBCD(systime.tm_year % 100); // fix 2010 bug
|
|
ts[3]=daveToBCD(systime.tm_mon+1);
|
|
ts[4]=daveToBCD(systime.tm_mday);
|
|
ts[5]=daveToBCD(systime.tm_hour);
|
|
ts[6]=daveToBCD(systime.tm_min);
|
|
ts[7]=daveToBCD(systime.tm_sec);
|
|
ts[8]=daveToBCD(t1.tv_usec/100);
|
|
ts[9]=daveToBCD(t1.tv_usec%100);
|
|
// _daveDump("timestamp: ",ts,10);
|
|
// LOG2("tm.sec: %d\n", systime.tm_sec);
|
|
// LOG2("tm.min: %d\n", systime.tm_min);
|
|
// LOG2("tm.hour: %d\n", systime.tm_hour);
|
|
#endif
|
|
|
|
#ifdef BCCWIN
|
|
SYSTEMTIME t1;
|
|
// gettimeofday(&t1, NULL);
|
|
GetLocalTime(&t1);
|
|
// tm=localtime(&t1);
|
|
// t1.tv_usec/=100; //tenth of miliseconds from microseconds
|
|
// WORD wYear;
|
|
/*
|
|
WORD wMonth;
|
|
WORD wDayOfWeek;
|
|
WORD wDay;
|
|
WORD wHour;
|
|
WORD wMinute;
|
|
WORD wSecond;
|
|
WORD wMilliseconds;
|
|
*/
|
|
ts[2]=daveToBCD(t1.wYear % 100); // fix 2010 bug
|
|
ts[3]=daveToBCD(t1.wMonth);
|
|
ts[4]=daveToBCD(t1.wDay);
|
|
ts[5]=daveToBCD(t1.wHour);
|
|
ts[6]=daveToBCD(t1.wMinute);
|
|
ts[7]=daveToBCD(t1.wSecond);
|
|
ts[8]=daveToBCD(t1.wMilliseconds/10);
|
|
ts[9]=daveToBCD((t1.wMilliseconds%10)*10);
|
|
// _daveDump("timestamp: ",ts,10);
|
|
// LOG2("tm.sec: %d\n", t1.wSecond);
|
|
// LOG2("tm.min: %d\n", t1.wMinute);
|
|
// LOG2("tm.hour: %d\n", t1.wHour);
|
|
#endif
|
|
|
|
#ifdef DEBUG_CALLS
|
|
LOG2("SetPLCTimeToSystime(dc:%p)\n", dc);
|
|
FLUSH;
|
|
#endif
|
|
len=0;
|
|
res=daveBuildAndSendPDU(dc, &p2,pa, sizeof(pa), ts, sizeof(ts));
|
|
if (res==daveResOK) {
|
|
dc->resultPointer=p2.udata;
|
|
dc->_resultPointer=p2.udata;
|
|
len=p2.udlen;
|
|
} else {
|
|
if(daveDebug & daveDebugPrintErrors)
|
|
LOG3("daveGetTime: %04X=%s\n",res, daveStrerror(res));
|
|
}
|
|
dc->AnswLen=len;
|
|
return res;
|
|
}
|
|
|
|
|
|
/***************
|
|
Simatic S5:
|
|
****************/
|
|
|
|
uc __davet2[]={STX};
|
|
uc __davet10[]={DLE};
|
|
char __davet1006[]={DLE,ACK}; // cs: this is only sent by system functions, so let it be char
|
|
us __daveT1006[]={DLE,ACK};
|
|
uc __davet121003[]={0x12,DLE,ETX};
|
|
us __daveT121003[]={0x12,DLE,ETX};
|
|
uc __davet161003[]={0x16,DLE,ETX};
|
|
us __daveT161003[]={0x16,DLE,ETX};
|
|
/*
|
|
Reads <count> bytes from area <BlockN> with offset <offset>,
|
|
that can be readed with daveGetInteger etc. You can read bytes from
|
|
PBs & FBs too, but use daveReadBlock for this:
|
|
*/
|
|
int DECL2 daveReadS5Bytes(daveConnection * dc, uc area, uc BlockN, int offset, int count)
|
|
{
|
|
int res,datastart,dataend;
|
|
daveS5AreaInfo ai;
|
|
uc b1[daveMaxRawLen];
|
|
// if (_daveIsS5BlockArea(area)==0) {
|
|
if (area==daveDB) {
|
|
res=_daveReadS5BlockAddress(dc,area,BlockN,&ai);//TODO make address cache
|
|
if (res<0) {
|
|
LOG2("%s *** Error in ReadS5Bytes.BlockAddr request.\n", dc->iface->name);
|
|
return res-50;
|
|
}
|
|
datastart=ai.address;
|
|
} else {
|
|
switch (area) {
|
|
case daveRawMemoryS5: datastart=0; break;
|
|
case daveInputs: datastart=dc->cache->PAE; break;
|
|
case daveOutputs: datastart=dc->cache->PAA; break;
|
|
case daveFlags: datastart=dc->cache->flags; break;
|
|
case daveTimer: datastart=dc->cache->timers; break;
|
|
case daveCounter: datastart=dc->cache->counters; break;
|
|
case daveSysDataS5: datastart=dc->cache->systemData; break;
|
|
default:
|
|
LOG2("%s *** Unknown area in ReadS5Bytes request.\n", dc->iface->name);
|
|
return -1;
|
|
}
|
|
}
|
|
//It's difficult to convert Intel-Motorola so I will use arithmetic:
|
|
if ((count>daveMaxRawLen)
|
|
// ||(offset+count>ai.len)
|
|
) {
|
|
LOG2("%s *** readS5Bytes: Requested data is out-of-range.\n", dc->iface->name);
|
|
return -1;
|
|
}
|
|
datastart+=offset;
|
|
dataend=datastart+count-1;
|
|
b1[0]=datastart/256;
|
|
b1[1]=datastart%256;
|
|
b1[2]=dataend/256;
|
|
b1[3]=dataend%256;
|
|
res=_daveExchangeAS511(dc,b1,4,2*count+7,0x04);
|
|
if (res<0) {
|
|
LOG2("%s *** Error in ReadS5Bytes.Exchange sequence.\n", dc->iface->name);
|
|
return res-10;
|
|
}
|
|
if (dc->AnswLen<count+7) {
|
|
LOG3("%s *** Too few chars (%d) in ReadS5Bytes data.\n", dc->iface->name,dc->AnswLen);
|
|
return -5;
|
|
}
|
|
if ((dc->msgIn[0]!=0)||(dc->msgIn[1]!=0)||(dc->msgIn[2]!=0)||(dc->msgIn[3]!=0)||(dc->msgIn[4]!=0)) {
|
|
LOG2("%s *** Wrong ReadS5Bytes data signature.\n", dc->iface->name);
|
|
return -6;
|
|
}
|
|
dc->resultPointer=dc->msgIn+5;
|
|
dc->_resultPointer=dc->resultPointer;
|
|
|
|
// dc->dlen=dc->AnswLen-7;
|
|
dc->AnswLen-=7;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Write DLE,ACK to the serial interface:
|
|
*/
|
|
void DECL2 _daveSendDLEACK(daveInterface * di) // serial interface
|
|
{
|
|
di->ifwrite(di, __davet1006, 2);
|
|
}
|
|
|
|
/*
|
|
Sends a sequence of characters after doubling DLEs and adding DLE,EOT.
|
|
*/
|
|
int DECL2 _daveSendWithDLEDup(daveInterface * di, // serial interface
|
|
uc *b, // a buffer containing the message
|
|
int size // the size of the string
|
|
)
|
|
{
|
|
uc target[daveMaxRawLen];
|
|
int res;
|
|
int targetSize=0,i; //preload
|
|
if(daveDebug & daveDebugExchange)
|
|
LOG1("SendWithDLEDup: \n");
|
|
if(daveDebug & daveDebugExchange)
|
|
_daveDump("I send",b,size);
|
|
for (i=0; i<size; i++) {
|
|
target[targetSize]=b[i];targetSize++;
|
|
if (b[i]==DLE) {
|
|
target[targetSize]=DLE;
|
|
targetSize++;
|
|
}
|
|
};
|
|
target[targetSize]=DLE;
|
|
target[targetSize+1]=EOT;
|
|
targetSize+=2;
|
|
if(daveDebug & daveDebugExchange)
|
|
_daveDump("I send", target, targetSize);
|
|
res=di->ifwrite(di, (char*)target, targetSize);
|
|
if(daveDebug & daveDebugExchange)
|
|
LOG2("send: res:%d\n",res);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Executes part of the dialog that requests transaction with PLC:
|
|
*/
|
|
int DECL2 _daveReqTrans(daveConnection * dc, uc trN)
|
|
{
|
|
uc b1[3];
|
|
int res;
|
|
if (daveDebug & daveDebugExchange)
|
|
LOG3("%s daveReqTrans %d\n", dc->iface->name, trN);
|
|
_daveSendSingle(dc->iface, STX);
|
|
res=_daveReadChars2(dc->iface, b1, /*dc->iface->timeout,*/ sizeof(__daveT1006)/2);
|
|
if (daveDebug & daveDebugByte)
|
|
_daveDump("2got",b1, res);
|
|
if (_daveMemcmp(__daveT1006, b1, sizeof(__daveT1006)/2)) {
|
|
if (daveDebug & daveDebugPrintErrors)
|
|
LOG3("%s daveReqTrans %d *** no DLE,ACK before send.\n", dc->iface->name, trN);
|
|
return -1;
|
|
}
|
|
_daveSendSingle(dc->iface, trN);
|
|
if (_daveReadSingle(dc->iface)!=STX) {
|
|
if (daveDebug & daveDebugPrintErrors)
|
|
LOG3("%s daveReqTrans %d *** no STX before send.\n", dc->iface->name, trN);
|
|
return -2;
|
|
}
|
|
_daveSendDLEACK(dc->iface);
|
|
_daveReadChars2(dc->iface, b1, /*dc->iface->timeout,*/ sizeof(__daveT161003)/2);
|
|
if (daveDebug & daveDebugByte)
|
|
_daveDump("1got",b1, res);
|
|
if (_daveMemcmp(__daveT161003, b1, sizeof(__daveT161003)/2)) {
|
|
if (daveDebug & daveDebugPrintErrors)
|
|
LOG3("%s daveReqTrans %d *** no accept0 from plc.\n", dc->iface->name, trN);
|
|
return -3;
|
|
}
|
|
_daveSendDLEACK(dc->iface);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Executes part of the dialog required to terminate transaction:
|
|
*/
|
|
int DECL2 _daveEndTrans(daveConnection * dc)
|
|
{
|
|
int res;
|
|
uc b1[3];
|
|
if (daveDebug & daveDebugExchange)
|
|
LOG2("%s daveEndTrans\n", dc->iface->name);
|
|
if (_daveReadSingle(dc->iface)!=STX) {
|
|
LOG2("%s daveEndTrans *** no STX at eot sequense.\n", dc->iface->name);
|
|
// return -1;
|
|
}
|
|
_daveSendDLEACK(dc->iface);
|
|
res=_daveReadChars2(dc->iface, b1, /*dc->iface->timeout,*/ sizeof(__daveT121003)/2);
|
|
if (daveDebug & daveDebugByte)
|
|
_daveDump("3got",b1, res);
|
|
if (_daveMemcmp(__daveT121003, b1, sizeof(__daveT121003)/2)) {
|
|
LOG2("%s daveEndTrans *** no accept of eot/ETX from plc.\n", dc->iface->name);
|
|
return -2;
|
|
}
|
|
_daveSendDLEACK(dc->iface);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Remove the DLE doubling:
|
|
*/
|
|
int DECL2 _daveDLEDeDup(daveConnection * dc, uc* rawBuf, int rawLen) {
|
|
int j=0,k;
|
|
for (k=0;k<rawLen-2;k++){
|
|
dc->msgIn[j]=rawBuf[k]; j++;
|
|
if (DLE==rawBuf[k]){
|
|
if (DLE!=rawBuf[k+1]) return -1;//Bad doubling found
|
|
k++;
|
|
}
|
|
}
|
|
dc->msgIn[j]=rawBuf[k];//Copy 2 last chars (DLE,ETX)
|
|
j++;k++;
|
|
dc->msgIn[j]=rawBuf[k];
|
|
dc->AnswLen=j+1;
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveExchangeAS511(daveConnection * dc, uc * b, int len, int maxlen, int trN) {
|
|
int res, i;
|
|
uc b1[3];
|
|
res=_daveReqTrans(dc, trN);
|
|
if (res<0) {
|
|
LOG2("%s *** Error in Exchange.ReqTrans request.\n", dc->iface->name);
|
|
return res-10;
|
|
}
|
|
if (trN==8) { //Block write functions have advanced syntax
|
|
LOG1("trN 8\n");
|
|
_daveSendWithDLEDup(dc->iface,b,4);
|
|
LOG1("trN 8 done\n");
|
|
} else {
|
|
if (daveDebug & daveDebugExchange)
|
|
LOG3("trN %d len %d\n",trN,len);
|
|
_daveSendWithDLEDup(dc->iface,b,len);
|
|
if (daveDebug & daveDebugExchange)
|
|
LOG2("trN %d done\n",trN);
|
|
}
|
|
// _daveSendDLEACK(dc->iface);
|
|
// res=_daveReadChars2(dc->iface, b1, /*dc->iface->timeout,*/ 2000 /*sizeof(__daveT1006)/2*/);
|
|
res=_daveReadChars2(dc->iface, b1, /*dc->iface->timeout,*/ sizeof(__daveT1006)/2);
|
|
if (daveDebug & daveDebugByte)
|
|
_daveDump("4 got:",b1, res);
|
|
if (_daveMemcmp(__daveT1006, b1, sizeof(__daveT1006)/2)) {
|
|
LOG2("%s *** no DLE,ACK in Exchange request.\n", dc->iface->name);
|
|
return -1;
|
|
}
|
|
if ((trN!=3)&&(trN!=7)&&(trN!=9)) {//write bytes, compress & delblk
|
|
if (!_daveReadSingle(dc->iface)==STX) {
|
|
LOG2("%s *** no STX in Exchange request.\n", dc->iface->name);
|
|
return -2;
|
|
}
|
|
// usleep(500000);
|
|
_daveSendDLEACK(dc->iface);
|
|
res=0;
|
|
do {
|
|
i=_daveReadChars2(dc->iface, dc->msgIn+res, /*100*dc->iface->timeout,*/ daveMaxRawLen-res);
|
|
res+=i;
|
|
if (daveDebug & daveDebugByte)
|
|
_daveDump("5 got:",dc->msgIn, res);
|
|
} while((i>0)&& ( (dc->msgIn[res-2]!=DLE) || (dc->msgIn[res-1]!=ETX)));
|
|
|
|
if (daveDebug & daveDebugByte)
|
|
LOG3("%s *** got %d bytes.\n", dc->iface->name,res);
|
|
if (res<0) {
|
|
LOG2("%s *** Error in Exchange.ReadChars request.\n", dc->iface->name);
|
|
return res-20;
|
|
}
|
|
if ((dc->msgIn[res-2]!=DLE)||(dc->msgIn[res-1]!=ETX)) {
|
|
LOG2("%s *** No DLE,ETX in Exchange data.\n", dc->iface->name);
|
|
return -4;
|
|
}
|
|
if (_daveDLEDeDup(dc,dc->msgIn,res)<0) {
|
|
LOG2("%s *** Error in Exchange rawdata.\n", dc->iface->name);
|
|
return -3;
|
|
}
|
|
|
|
// usleep(500000);
|
|
_daveSendDLEACK(dc->iface);
|
|
}
|
|
if (trN==8) { //Write requests have more differences from others ;(
|
|
if (dc->msgIn[0]!=9) {
|
|
LOG2("%s 8 *** No 0x09 in special Exchange request.\n", dc->iface->name);
|
|
return -5;
|
|
}
|
|
_daveSendSingle(dc->iface,STX);
|
|
res=_daveReadChars2(dc->iface, b1, /*dc->iface->timeout,*/ sizeof(__daveT1006)/2);
|
|
_daveDump("got:",b1, res);
|
|
if (_daveMemcmp(__daveT1006, b1, sizeof(__daveT1006)/2)) {
|
|
LOG2("%s 8 *** no DLE,ACK in special Exchange request.\n", dc->iface->name);
|
|
return -6;
|
|
}
|
|
_daveSendWithDLEDup(dc->iface,b+4,len);
|
|
|
|
res=_daveReadChars2(dc->iface, b1, /*dc->iface->timeout,*/ sizeof(__daveT1006)/2);
|
|
_daveDump("got:",b1, res);
|
|
if (_daveMemcmp(__daveT1006, b1, sizeof(__daveT1006)/2)) {
|
|
// if (!_daveTestChars(dc->iface, __davet1006, 2)) {
|
|
LOG2("%s 8 *** no DLE,ACK after transfer in Exchange.\n", dc->iface->name);
|
|
return -7;
|
|
}
|
|
}
|
|
if (trN==7) {
|
|
// usleep(450000);
|
|
}//TODO: check compression time
|
|
res=_daveEndTrans(dc);
|
|
if (res<0) {
|
|
LOG2("%s *** Error in Exchange.EndTrans request.\n", dc->iface->name);
|
|
return res-30;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
In S7, we need to tell the PLC what memory area we want to read from or write to. The PLC
|
|
behaves as if each area were a different physical memory starting with an offset of 0.
|
|
In S5, everything is in common 64k of memory. For different areas, we have to add start
|
|
offsets of areas or objects.
|
|
The following is needed to make memory access as S7-compatible as possible.
|
|
*/
|
|
int areaFromBlockType(int area){
|
|
switch(area) {
|
|
case daveS5BlockType_DB: // S5 block type
|
|
case daveBlockType_DB: // S7 block type
|
|
case daveDB: // S7 area type
|
|
return daveS5BlockType_DB;
|
|
case daveS5BlockType_OB:
|
|
case daveBlockType_OB:
|
|
return daveS5BlockType_OB;
|
|
case daveS5BlockType_FB:
|
|
case daveBlockType_FB:
|
|
return daveS5BlockType_FB;
|
|
// s5 only:
|
|
case daveS5BlockType_PB:
|
|
return daveS5BlockType_PB;
|
|
case daveS5BlockType_SB:
|
|
return daveS5BlockType_SB;
|
|
default: return area;
|
|
}
|
|
}
|
|
/*
|
|
Requests physical addresses and lengths of blocks in PLC memory and writes
|
|
them to ai structure:
|
|
*/
|
|
int DECL2 _daveReadS5BlockAddress(daveConnection * dc, uc area, uc BlockN, daveS5AreaInfo * ai)
|
|
{
|
|
int res,dbaddr,dblen, s5Area;
|
|
uc b1[24]; //15 + some Dups
|
|
// if (_daveIsS5BlockArea(area)<0) {
|
|
// printf("%s *** Not block area .\n", dc->iface->name);
|
|
// return -1;
|
|
// }
|
|
|
|
// b1[0]=area;
|
|
|
|
s5Area=areaFromBlockType(area);
|
|
b1[0]=s5Area;
|
|
b1[1]=BlockN;
|
|
res=_daveExchangeAS511(dc,b1,2,24,0x1A);
|
|
if (res<0) {
|
|
printf("%s *** Error in BlockAddr.Exchange sequense.\n", dc->iface->name);
|
|
return res-10;
|
|
}
|
|
if (dc->AnswLen<15) {
|
|
printf("%s *** Too few chars (%d) in BlockAddr data.\n", dc->iface->name,dc->AnswLen);
|
|
return -2;
|
|
}
|
|
if ((dc->msgIn[0]!=0)
|
|
||(dc->msgIn[3]!=0x70)
|
|
||(dc->msgIn[4]!=0x70)
|
|
||(dc->msgIn[5]!=0x40+s5Area)||(dc->msgIn[6]!=BlockN)) {
|
|
printf("%s *** Wrong BlockAddr data signature.\n", dc->iface->name);
|
|
return -3;
|
|
}
|
|
dbaddr=dc->msgIn[1];
|
|
dbaddr=dbaddr*256+dc->msgIn[2];//Let make shift operations to compiler's optimizer
|
|
dblen=dc->msgIn[11];
|
|
dblen=(dblen*256+dc->msgIn[12]-5)*2; //PLC returns dblen in words including
|
|
//5 word header (but returnes the
|
|
//start address after the header) so
|
|
//dblen is length of block body
|
|
ai->address=dbaddr;
|
|
ai->len=dblen;
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveIsS5BlockArea(uc area)
|
|
{
|
|
if (
|
|
// (area!=daveBlockType_S5DB)&&
|
|
(area!=daveS5BlockType_SB)&&
|
|
(area!=daveS5BlockType_PB)&&
|
|
(area!=daveS5BlockType_FX)&&
|
|
(area!=daveS5BlockType_FB)&&
|
|
(area!=daveS5BlockType_DX)&&
|
|
(area!=daveS5BlockType_OB)) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveIsS5DBlockArea(uc area)
|
|
{
|
|
if (area!=daveDB) {
|
|
// (area!=daveBlockType_S5DX))
|
|
// (area!=daveBlockType_S5DX)) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define maxSysinfoLen 87
|
|
/*
|
|
This is a trick which will intercept all functions not available for S5 AS511. It works
|
|
this way:
|
|
|
|
A function for S7 forms a packet for S7 communication and then calls daveExchange which
|
|
will send the packet and return the answer.
|
|
If a function is also vailable for S5, it must check whether the protocol is AS511. If so,
|
|
the function calls it's S5 counterpart and returns the result of it.
|
|
Hence, it will never reach daveExchange.
|
|
|
|
Now, functions for which there is no S5 counterpart simply continue, (superfluously) form
|
|
the S7 packet and call daveExchange, which will point hereto.
|
|
This fake function allways returns a specific error code so the user knows the function
|
|
is not available in the S5 protocol.
|
|
|
|
The advantage of this mechanism is that additional functions for S7 can be added at any
|
|
time without caring about S5: If no special handling is provided, they end up here.
|
|
*/
|
|
int DECL2 _daveFakeExchangeAS511(daveConnection * dc, PDU *p){
|
|
return daveNotAvailableInS5;
|
|
}
|
|
|
|
/*
|
|
This is a deviation from normal use of connect functions: There are no connections in AS511.
|
|
The reason why we provide a daveConnect() is this:
|
|
From an S5 CPU, you don't read inputs, outputs,flags or any other memory area but simply
|
|
bytes from global memory.
|
|
There are addresses of input image area, output image area, flags, timers etc. These depend
|
|
on CPU model. Next, there are start addresses of the data blocks. These addresses change
|
|
whenever a data block is created or changed in size or modified by programming device.
|
|
|
|
In both cases, we could read the adresses from the PLC before reading the data. To save
|
|
time and gain efficiency, we read them once in connectPLC. We rely on users following the
|
|
S7 scheme: connect to a PLC before reading from it !!
|
|
|
|
If we would read addresses each time, you could do something you cannot with S7: pull the
|
|
plug from one PLC, connect to another PLC and the program still works.
|
|
|
|
Here, you CANNOT do that. You have to call connectPLC again after changing to the new PLC.
|
|
|
|
Another thing are data block addresses. We could fetch all 256 possible addresses in connectPLC,
|
|
too. But that would use 256 entries that must exist while the program might not use data
|
|
blocks at all. So we don't. We add data block addresses to the PLC address cache when they
|
|
are used for the first time.
|
|
There are S5 programs that create data blocks dynamically. Hence cached addresses get invalid.
|
|
If you have a PLC with such a program use
|
|
daveSetNonCacheable(dc, DBnumber);
|
|
If you suspect somebody could pull the plug, connect a programming device, modify data blocks
|
|
and reconnect your application program, use
|
|
daveSetNonCacheable(dc, allDBs);
|
|
In this case, the actual address will be fetched before each read or write from/to the related
|
|
data blocks (which will slow down your application).
|
|
*/
|
|
|
|
int DECL2 _daveConnectPLCAS511(daveConnection * dc){
|
|
int res;
|
|
uc b1[maxSysinfoLen]; //20 words + some Dups
|
|
// dc->maxPDUlength=1000;
|
|
dc->maxPDUlength=240;
|
|
dc->cache=(daveS5cache*)calloc(1,sizeof(daveS5cache));
|
|
|
|
res=_daveExchangeAS511(dc,b1,0,maxSysinfoLen,0x18);
|
|
if (res<0) {
|
|
LOG2("%s *** Error in ImageAddr.Exchange sequence.\n", dc->iface->name);
|
|
return res-10;
|
|
}
|
|
if (dc->AnswLen<47) {
|
|
LOG3("%s *** Too few chars (%d) in ImageAddr data.\n", dc->iface->name,dc->AnswLen);
|
|
return -2;
|
|
}
|
|
_daveDump("connect:",dc->msgIn, 47);
|
|
dc->cache->PAE=daveGetU16from(dc->msgIn+5); // start of inputs;
|
|
dc->cache->PAA=daveGetU16from(dc->msgIn+7); // start of outputs;
|
|
dc->cache->flags=daveGetU16from(dc->msgIn+9); // start of flag (marker) memory;
|
|
dc->cache->timers=daveGetU16from(dc->msgIn+11); // start of timer memory;
|
|
dc->cache->counters=daveGetU16from(dc->msgIn+13); // start of counter memory
|
|
dc->cache->systemData=daveGetU16from(dc->msgIn+15); // start of system data
|
|
dc->cache->first=NULL;
|
|
LOG2("start of inputs in memory %04x\n",dc->cache->PAE);
|
|
LOG2("start of outputs in memory %04x\n",dc->cache->PAA);
|
|
LOG2("start of flags in memory %04x\n",dc->cache->flags);
|
|
LOG2("start of timers in memory %04x\n",dc->cache->timers);
|
|
LOG2("start of counters in memory %04x\n",dc->cache->counters);
|
|
LOG2("start of system data in memory %04x\n",dc->cache->systemData);
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveDisconnectPLCAS511(daveConnection * dc){
|
|
free(dc->cache);
|
|
dc->cache=0;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Writes <count> bytes from area <BlockN> with offset <offset> from buf.
|
|
You can't write data to the program blocks because you can't syncronize
|
|
with PLC cycle. For this purposes use daveWriteBlock:
|
|
*/
|
|
int DECL2 daveWriteS5Bytes(daveConnection * dc, uc area, uc BlockN, int offset, int count, void * buf)
|
|
{
|
|
int res,datastart;
|
|
daveS5AreaInfo ai;
|
|
uc b1[daveMaxRawLen];
|
|
// if (_daveIsS5DBlockArea(area)==0) {
|
|
if (area==daveDB) {
|
|
// LOG1("_daveIsS5DBlockArea\n");
|
|
res=_daveReadS5BlockAddress(dc,area,BlockN,&ai);
|
|
if (res<0) {
|
|
LOG2("%s *** Error in WriteS5Bytes.BlockAddr request.\n", dc->iface->name);
|
|
return res-50;
|
|
}
|
|
datastart=ai.address;
|
|
} else {
|
|
switch (area) {
|
|
case daveRawMemoryS5: datastart=0; break;
|
|
case daveInputs: datastart=dc->cache->PAE; break;
|
|
case daveOutputs: datastart=dc->cache->PAA; break;
|
|
case daveFlags: datastart=dc->cache->flags; break;
|
|
case daveTimer: datastart=dc->cache->timers; break;
|
|
case daveCounter: datastart=dc->cache->counters; break;
|
|
case daveSysDataS5: datastart=dc->cache->systemData; break;
|
|
default:
|
|
LOG2("%s *** Unknown area in WriteS5Bytes request.\n", dc->iface->name);
|
|
return -1;
|
|
}
|
|
}
|
|
if ((count>daveMaxRawLen)||(offset+count>ai.len)) {
|
|
LOG2("%s writeS5Bytes *** Requested data is out-of-range.\n", dc->iface->name);
|
|
return -1;
|
|
}
|
|
// datastart=ai.address+offset;
|
|
LOG2("area start is %04x, ",datastart);
|
|
datastart+=offset;
|
|
LOG2("data start is %04x\n",datastart);
|
|
b1[0]=datastart/256;
|
|
b1[1]=datastart%256;
|
|
memcpy(&b1[2],buf,count);
|
|
res=_daveExchangeAS511(dc,b1,2+count,0,0x03);
|
|
if (res<0) {
|
|
LOG2("%s *** Error in WriteS5Bytes.Exchange sequense.\n", dc->iface->name);
|
|
return res-10;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 daveStopS5(daveConnection * dc) {
|
|
uc b1[]={0x88,0x04}; // I don't know what this mean
|
|
return daveWriteBytes(dc,daveSysDataS5,0,0x0c,2,b1);
|
|
}
|
|
|
|
int DECL2 daveStartS5(daveConnection * dc) {
|
|
uc b1[]={0x68,0x00}; // I don't know what this mean
|
|
return daveWriteBytes(dc,daveSysDataS5,0,0x0c,2,b1);
|
|
}
|
|
|
|
int DECL2 daveGetS5ProgramBlock(daveConnection * dc, int blockType, int number, char* buffer, int * length) {
|
|
// int totlen,res;
|
|
// *length=totlen;
|
|
return daveResNotYetImplemented;
|
|
}
|
|
|
|
|
|
/********************************************
|
|
Use Siemens DLLs and drivers for transport:
|
|
*********************************************/
|
|
|
|
/*
|
|
While the following code is useless under operating systems others than win32,
|
|
I leave it here, independent of conditionals. This ensures it is and will continue
|
|
to be at least compileable now and over version changes. Who knows what it might
|
|
be good for in the future...
|
|
*/
|
|
/*
|
|
fill some standard fields and pass it to SCP-send:
|
|
*/
|
|
int DECL2 _daveSCP_send(int fd, uc * reqBlock) {
|
|
S7OexchangeBlock* fdr;
|
|
fdr=(S7OexchangeBlock*)reqBlock;
|
|
fdr->headerlength=80;
|
|
fdr->allways2 = 2;
|
|
fdr->payloadStart= 80;
|
|
return SCP_send(fd, fdr->payloadLength+80, reqBlock);
|
|
}
|
|
|
|
int daveSCP_receive(int h, uc * buffer) {
|
|
int res, datalen;
|
|
S7OexchangeBlock * fdr;
|
|
fdr=(S7OexchangeBlock*) buffer;
|
|
res=SCP_receive(h, 0xFFFF, &datalen, sizeof(S7OexchangeBlock), buffer);
|
|
if (daveDebug & daveDebugByte) {
|
|
_daveDump("header:",buffer, 80);
|
|
_daveDump("data:",buffer+80, fdr->payloadLength);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int DECL2 _daveConnectPLCS7online (daveConnection * dc) {
|
|
|
|
int res=0;
|
|
uc p2[]={
|
|
0x00,0x02,0x01,0x00,
|
|
0x0C,0x01,0x00,0x00,
|
|
0x00,0x06,
|
|
0x00,0x00,0x00,0x00,0x00,0x01,
|
|
0x00,0x02,0x01,
|
|
};
|
|
|
|
uc pa[]= {0xF0, 0 ,0, 1, 0, 1, 3, 0xc0,};
|
|
|
|
PDU pu2,pu1, *p;
|
|
|
|
int a;
|
|
|
|
S7OexchangeBlock * fdr;
|
|
fdr=(S7OexchangeBlock*)(dc->msgOut);
|
|
p2[9]=dc->MPIAdr; ///
|
|
dc->PDUstartI=80;
|
|
|
|
memset(fdr,0,80);
|
|
fdr->number= 110;
|
|
fdr->field6= 64;
|
|
fdr->field8= 16642;
|
|
|
|
a= _daveSCP_send(((int)dc->iface->fd.wfd), dc->msgOut);
|
|
daveSCP_receive((int)(dc->iface->fd.rfd), dc->msgIn);
|
|
|
|
memset(fdr,0,206);
|
|
fdr->number= 111;
|
|
fdr->field6= 64;
|
|
fdr->field7= 1;
|
|
fdr->field8= 16642;
|
|
fdr->validDataLength= 126;
|
|
fdr->payloadLength= 126;
|
|
fdr->field10= 1;
|
|
fdr->field13= 2;
|
|
fdr->field12= 114;
|
|
memcpy(&(fdr->payload),p2,sizeof(p2));
|
|
|
|
a= _daveSCP_send((int)(dc->iface->fd.wfd), dc->msgOut);
|
|
daveSCP_receive((int)(dc->iface->fd.rfd), dc->msgIn);
|
|
|
|
memset(fdr,0,98);
|
|
// fdr->number= 112;
|
|
fdr->field6= 64;
|
|
fdr->field7= 6;
|
|
fdr->field8= 16642;
|
|
fdr->validDataLength= 18;
|
|
fdr->payloadLength= 18;
|
|
fdr->field10= 1;
|
|
|
|
p=&pu1;
|
|
// p->header=dc->msgOut+dc->PDUstartO;
|
|
p->header=fdr->payload;
|
|
_daveInitPDUheader(p,1);
|
|
_daveAddParam(p, pa, sizeof(pa));
|
|
if (daveGetDebug() & daveDebugPDU)
|
|
_daveDumpPDU(p);
|
|
|
|
a= _daveSCP_send((int)(dc->iface->fd.wfd), dc->msgOut);
|
|
daveSCP_receive((int)(dc->iface->fd.rfd), dc->msgIn);
|
|
pu2.header=dc->msgIn+80;
|
|
_daveSetupReceivedPDU(dc, &pu2);
|
|
if (daveGetDebug() & daveDebugPDU)
|
|
_daveDumpPDU(&pu2);
|
|
|
|
memset(fdr,0,560);
|
|
fdr->number= 0;
|
|
fdr->field6= 64;
|
|
fdr->field7= 7;
|
|
fdr->field8= 16642;
|
|
fdr->payloadLength= 480;
|
|
fdr->field10= 1;
|
|
|
|
a= _daveSCP_send((int)(dc->iface->fd.wfd), dc->msgOut);
|
|
daveSCP_receive((int)(dc->iface->fd.rfd), dc->msgIn);
|
|
_daveSetupReceivedPDU(dc, &pu2);
|
|
if (daveGetDebug() & daveDebugPDU)
|
|
_daveDumpPDU(&pu2);
|
|
dc->maxPDUlength=daveGetU16from(pu2.param+6);
|
|
// if (daveDebug & daveDebugConnect)
|
|
LOG2("\n*** Partner offered PDU length: %d\n\n",dc->maxPDUlength);
|
|
return res;
|
|
|
|
/*
|
|
memset((uc*)(&giveBack)+80,0,480);
|
|
giveBack.payloadLength= 480;
|
|
return _daveNegPDUlengthRequest(dc, &pu1);
|
|
*/
|
|
}
|
|
|
|
int DECL2 _daveSendMessageS7online(daveConnection *dc, PDU *p) {
|
|
int a;
|
|
int datalen;
|
|
int len=p->hlen+p->plen+p->dlen;
|
|
uc buffer[sizeof(S7OexchangeBlock)];
|
|
S7OexchangeBlock* fdr;
|
|
fdr=(S7OexchangeBlock*)dc->msgOut;
|
|
memset(dc->msgOut,0,80);
|
|
// fdr->number= 114;
|
|
fdr->field6= 64;
|
|
fdr->field7= 6;
|
|
fdr->field8= 16642;
|
|
fdr->validDataLength= len;
|
|
fdr->payloadLength= len;
|
|
fdr->field10= 1;
|
|
|
|
// memcpy(&(fdr->payload),buffer,len);
|
|
a= _daveSCP_send((int)(dc->iface->fd.wfd), dc->msgOut);
|
|
SCP_receive((int)(dc->iface->fd.rfd), 0xFFFF, &datalen, sizeof(S7OexchangeBlock), buffer);
|
|
// daveSCP_receive(dc->iface->fd.rfd, dc->msgIn);
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveGetResponseS7online(daveConnection *dc) {
|
|
int a;
|
|
a= _daveSCP_send((int)(dc->iface->fd.rfd), dc->msgIn);
|
|
daveSCP_receive((int)(dc->iface->fd.rfd), dc->msgIn);
|
|
return 0;
|
|
}
|
|
|
|
int DECL2 _daveExchangeS7online(daveConnection * dc, PDU * p) {
|
|
int res;
|
|
res=_daveSendMessageS7online(dc, p);
|
|
dc->AnswLen=0;
|
|
res=_daveGetResponseS7online(dc);
|
|
return res;
|
|
}
|
|
|
|
int DECL2 _daveListReachablePartnersS7online (daveInterface * di, char * buf) {
|
|
int a;
|
|
S7OexchangeBlock reqBlock;
|
|
uc b1[sizeof(S7OexchangeBlock)];
|
|
S7OexchangeBlock* fdr;
|
|
fdr=&reqBlock;
|
|
|
|
memset(fdr,0,140);
|
|
fdr->number= 102;
|
|
fdr->field5= 1;
|
|
fdr->field6= 34;
|
|
fdr->field8= 16642;
|
|
fdr->payloadLength= 60;
|
|
fdr->functionCode= 0x28;
|
|
|
|
a= _daveSCP_send((int)(di->fd.wfd), (uc *) &reqBlock);
|
|
daveSCP_receive((int)(di->fd.rfd), b1);
|
|
|
|
fdr->number= 103;
|
|
fdr->field5= 1;
|
|
fdr->field6= 34;
|
|
fdr->field8= 16642;
|
|
fdr->functionCode= 0x17;
|
|
|
|
a= _daveSCP_send((int)(di->fd.wfd), (uc *) &reqBlock);
|
|
daveSCP_receive((int)(di->fd.rfd), b1);
|
|
memset(fdr,0,140);
|
|
|
|
fdr->number= 104;
|
|
fdr->field5= 1;
|
|
fdr->field6= 34;
|
|
fdr->field8= 16642;
|
|
fdr->payloadLength= 60;
|
|
fdr->functionCode= 0x28;
|
|
a= _daveSCP_send((int)(di->fd.wfd), (uc *) &reqBlock);
|
|
daveSCP_receive((int)(di->fd.rfd), b1);
|
|
|
|
memset(fdr,0,208);
|
|
fdr->number= 105;
|
|
fdr->field5= 1;
|
|
fdr->field6= 34;
|
|
fdr->field8 =16642;
|
|
fdr->payloadLength= 128;
|
|
fdr->functionCode= 0x1a;
|
|
a= _daveSCP_send((int)(di->fd.wfd), (uc *) &reqBlock);
|
|
daveSCP_receive((int)(di->fd.rfd), b1);
|
|
|
|
memcpy(buf,b1+80,126);
|
|
return 126;
|
|
}
|
|
|
|
/*
|
|
This is not quite the same as in other protocols: Normally, we have a file descriptor or
|
|
file handle in di->fd.rfd, di->fd.wfd. disconnectAdapter does something like making the
|
|
MPI adapter leaving the Profibus token ring. File descriptor remains valid until it is
|
|
closed with closePort().
|
|
In principle, instead of closing it, we could redo the sequence
|
|
daveNewInterface, initAdapter and then continue to use it.
|
|
We cannot use closePort() on a "handle" retrieved from SCP_open(). It isn't a file handle.
|
|
We cannot make closePort() treat it differently as there is no information in di->fd.rfd
|
|
WHAT it is.
|
|
- We could make di->fd.rfd a structure with extra information.
|
|
- We could pass struct di->fd (daveOSserialtype) instead of di->fd.rfd / di->fd.wfd to all
|
|
all functions dealing with di->fd.rfd. Then we could add extra information to
|
|
daveOSserialtype
|
|
- We could better pas a pointer to an extended daveOSserialtype as it makes less problems
|
|
when passing it to different programming languages.
|
|
These would be major changes. They would give up the (theroetical?) possibility to use file
|
|
handles obtained in quite a different way and to put them into daveOSserialtype.
|
|
I chose to change as little as possible for s7online and just SCP_close the handle here,
|
|
expecting no one will try to reuse after this.
|
|
|
|
Up to here is what version 0.8 does. The probleme is now that an application cannot do
|
|
daveDisconnectAdapter(), closePort() as it does for other protocols.
|
|
|
|
Now comes a second kludge for 0.8.1: We replace the "file handles" value by -1. Now we can
|
|
tell closePort() to do nothing for a value of -1.
|
|
|
|
Befor releasing that, I think it is better to use different close functions, closeS7oline
|
|
for s7online and closePort() for everything else.
|
|
*/
|
|
|
|
/*
|
|
int DECL2 _daveDisconnectAdapterS7online(daveInterface * di) {
|
|
int res;
|
|
res=SCP_close((int)(di->fd.rfd));
|
|
di->fd.rfd=-1;
|
|
di->fd.wfd=-1;
|
|
return res;
|
|
}
|
|
*/
|
|
/***
|
|
NetLink Pro
|
|
***/
|
|
#define NET
|
|
|
|
#ifndef NET
|
|
int DECL2 _daveReadMPINLpro(daveInterface * di, uc *b) {
|
|
int res=0,state=0,nr_read;
|
|
uc bcc=0;
|
|
nr_read= di->ifread(di, b, 2);
|
|
if (nr_read>=2) {
|
|
res=256*b[0]+b[1]+2;
|
|
if (res>nr_read)
|
|
nr_read+=di->ifread(di, b+nr_read, res-nr_read);
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG4("%s nr_read:%d res:%d.\n", di->name, nr_read, res);
|
|
return res-2;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef NET
|
|
#ifdef HAVE_SELECT
|
|
/*
|
|
Read one complete packet. The bytes 0 and 1 contain length information.
|
|
This version needs a socket filedescriptor that is set to O_NONBLOCK or
|
|
it will hang, if there are not enough bytes to read.
|
|
The advantage may be that the timeout is not used repeatedly.
|
|
*/
|
|
int DECL2 _daveReadMPINLpro(daveInterface * di,uc *b) {
|
|
int res,length;
|
|
fd_set FDS;
|
|
struct timeval t;
|
|
FD_ZERO(&FDS);
|
|
FD_SET(di->fd.rfd, &FDS);
|
|
|
|
t.tv_sec = di->timeout / 1000000;
|
|
t.tv_usec = di->timeout % 1000000;
|
|
if (select(di->fd.rfd + 1, &FDS, NULL, NULL, &t) <= 0) {
|
|
if (daveDebug & daveDebugByte) LOG1("timeout in ReadMPINLpro.\n");
|
|
return daveResTimeout;
|
|
} else {
|
|
res=read(di->fd.rfd, b, 2);
|
|
if (res<2) {
|
|
if (daveDebug & daveDebugByte) {
|
|
LOG2("res %d ",res);
|
|
_daveDump("readISOpacket: short packet", b, res);
|
|
}
|
|
return daveResShortPacket; /* short packet */
|
|
}
|
|
length=b[1]+0x100*b[0];
|
|
res+=read(di->fd.rfd, b+2, length);
|
|
if (daveDebug & daveDebugByte) {
|
|
LOG3("readMPINLpro: %d bytes read, %d needed\n",res, length);
|
|
_daveDump("readMPINLpro: packet", b, res);
|
|
}
|
|
return (res);
|
|
}
|
|
}
|
|
#endif /* HAVE_SELECT */
|
|
|
|
#ifdef BCCWIN
|
|
int DECL2 _daveReadMPINLpro(daveInterface * di,uc *b) {
|
|
int res,i,length;
|
|
i=recv((SOCKET)(di->fd.rfd), b, 2, 0);
|
|
res=i;
|
|
if (res <= 0) {
|
|
if (daveDebug & daveDebugByte) LOG1("timeout in ReadMPINLpro.\n");
|
|
return daveResTimeout;
|
|
} else {
|
|
if (res<2) {
|
|
if (daveDebug & daveDebugByte) {
|
|
LOG2("res %d ",res);
|
|
_daveDump("readISOpacket: short packet", b, res);
|
|
}
|
|
return daveResShortPacket; /* short packet */
|
|
}
|
|
length=b[1]+0x100*b[0];
|
|
i=recv((SOCKET)(di->fd.rfd), b+2, length, 0);
|
|
res+=i;
|
|
if (daveDebug & daveDebugByte) {
|
|
LOG3("readMPINLpro: %d bytes read, %d needed\n",res, length);
|
|
_daveDump("readMPIpro: packet", b, res);
|
|
}
|
|
return (res);
|
|
}
|
|
}
|
|
|
|
#endif /* */
|
|
#endif
|
|
|
|
|
|
/*
|
|
This initializes the MPI adapter. Andrew's version.
|
|
*/
|
|
|
|
int DECL2 _daveInitAdapterNLpro(daveInterface * di) /* serial interface */
|
|
{
|
|
uc b3[]={
|
|
0x01,0x03,0x02,0x27, 0x00,0x9F,0x01,0x14,
|
|
0x00,0x90,0x01,0xc, 0x00, /* ^^^ MaxTsdr */
|
|
0x00,0x5,
|
|
0x02,/* Bus speed */
|
|
|
|
0x00,0x0F,0x05,0x01,0x01,0x03,0x81,/* from topserverdemo */
|
|
/*^^ - Local mpi */
|
|
};
|
|
|
|
int res;
|
|
b3[16]=di->localMPI;
|
|
if (di->speed==daveSpeed500k)
|
|
b3[7]=0x64;
|
|
if (di->speed==daveSpeed1500k)
|
|
b3[7]=0x96;
|
|
b3[15]=di->speed;
|
|
|
|
// res=_daveInitStep(di, 1, b3, sizeof(b3),"initAdapter()");
|
|
res=_daveInitStepNLpro(di, 1, b3, sizeof(b3),"initAdapter()", NULL);
|
|
|
|
// res= _daveReadMPINLpro(di, b1);
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s initAdapter() success.\n", di->name);
|
|
// _daveSendSingle(di,DLE);
|
|
di->users=0; /* there cannot be any connections now */
|
|
return 0;
|
|
}
|
|
|
|
|
|
void DECL2 _daveSendSingleNLpro(daveInterface * di, /* serial interface */
|
|
uc c /* chracter to be send */
|
|
)
|
|
{
|
|
unsigned long i;
|
|
uc c3[3];
|
|
c3[0]=0;
|
|
c3[1]=1;
|
|
c3[2]=c;
|
|
// di->ifwrite(di, c3, 3);
|
|
#ifdef HAVE_SELECT
|
|
daveWriteFile(di->fd.wfd, c3, 3, i);
|
|
#endif
|
|
#ifdef BCCWIN
|
|
send((unsigned int)(di->fd.wfd), c3, 3, 0);
|
|
#endif
|
|
|
|
}
|
|
|
|
/*
|
|
int DECL2 _daveSendISOPacket(daveConnection * dc, int size) {
|
|
unsigned long i;
|
|
size+=4;
|
|
*(dc->msgOut+3)=size % 0x100; //was %0xFF, certainly a bug
|
|
*(dc->msgOut+2)=size / 0x100;
|
|
*(dc->msgOut+1)=0;
|
|
*(dc->msgOut+0)=3;
|
|
if (daveDebug & daveDebugByte)
|
|
_daveDump("send packet: ",dc->msgOut,size);
|
|
#ifdef HAVE_SELECT
|
|
daveWriteFile(dc->iface->fd.wfd, dc->msgOut, size, i);
|
|
#endif
|
|
#ifdef BCCWIN
|
|
send((unsigned int)(dc->iface->fd.wfd), dc->msgOut, size, 0);
|
|
#endif
|
|
return 0;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
This sends a string after doubling DLEs in the String
|
|
and adding DLE,ETX and bcc.
|
|
*/
|
|
int DECL2 _daveSendWithCRCNLpro(daveInterface * di, /* serial interface */
|
|
uc *b, /* a buffer containing the message */
|
|
int size /* the size of the string */
|
|
)
|
|
{
|
|
uc target[daveMaxRawLen];
|
|
int i,targetSize=2;
|
|
target[0]=size / 256;
|
|
target[1]=size % 256;
|
|
|
|
// int bcc=DLE^ETX; /* preload */
|
|
for (i=0; i<size; i++) {
|
|
target[targetSize]=b[i];targetSize++;
|
|
};
|
|
// targetSize+=0;
|
|
// di->ifwrite(di, target, targetSize);
|
|
#ifdef HAVE_SELECT
|
|
daveWriteFile(di->fd.wfd, target, targetSize, i);
|
|
#endif
|
|
#ifdef BCCWIN
|
|
send((unsigned int)(di->fd.wfd), target, targetSize, 0);
|
|
#endif
|
|
|
|
if (daveDebug & daveDebugPacket)
|
|
_daveDump("_daveSendWithCRCNLpro",target, targetSize);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Send a string of init data to the MPI adapter.
|
|
*/
|
|
int DECL2 _daveInitStepNLpro(daveInterface * di, int nr, uc *fix, int len, char * caller, uc * buffer ) {
|
|
uc res[500];
|
|
int i;
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG4("%s %s step %d.\n", di->name, caller, nr);
|
|
|
|
_daveSendWithCRCNLpro(di, fix, len);
|
|
i=_daveReadMPINLpro(di, (buffer != NULL) ? buffer : res );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Open connection to a PLC. This assumes that dc is initialized by
|
|
daveNewConnection and is not yet used.
|
|
(or reused for the same PLC ?)
|
|
*/
|
|
int DECL2 _daveConnectPLCNLpro(daveConnection * dc) {
|
|
int res;
|
|
PDU p1;
|
|
uc b4[]={
|
|
0x04,0x80,0x80,0x0D,0x00,0x14,0xE0,0x04,
|
|
0x00,0x80,0x00,0x02,
|
|
0x00,
|
|
0x02,
|
|
0x01,0x00,
|
|
0x01,0x00,
|
|
};
|
|
|
|
us t4[]={
|
|
0x04,0x80,0x180,0x0C,0x114,0x103,0xD0,0x04, // 1/10/05 trying Andrew's patch
|
|
0x00,0x80,
|
|
0x00,0x02,0x00,0x02,0x01,
|
|
0x00,0x01,0x00,
|
|
};
|
|
uc b5[]={
|
|
0x05,0x07,
|
|
};
|
|
us t5[]={
|
|
0x04,
|
|
0x80,
|
|
0x180,0x0C,0x114,0x103,0x05,0x01,
|
|
};
|
|
b4[1]|=dc->MPIAdr;
|
|
b4[5]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
|
|
|
|
t4[1]|=dc->MPIAdr;
|
|
t5[1]|=dc->MPIAdr;
|
|
_daveInitStepNLpro(dc->iface, 1, b4, sizeof(b4),"connectPLC(1)", dc->msgIn);
|
|
|
|
// first 2 bytes of msgIn[] contain packet length
|
|
dc->connectionNumber2=dc->msgIn[2+5]; // 1/10/05 trying Andrew's patch
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC(1) step 4.\n", dc->iface->name);
|
|
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC() step 5.\n", dc->iface->name);
|
|
|
|
_daveSendWithPrefixNLpro(dc, b5, sizeof(b5));
|
|
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC() step 6.\n", dc->iface->name);
|
|
res= _daveReadMPINLpro(dc->iface,dc->msgIn);
|
|
if (daveDebug & daveDebugConnect)
|
|
LOG2("%s daveConnectPLC() step 7.\n", dc->iface->name);
|
|
res= _daveNegPDUlengthRequest(dc, &p1);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Executes part of the dialog necessary to send a message:
|
|
*/
|
|
int DECL2 _daveSendDialogNLpro(daveConnection * dc, int size)
|
|
{
|
|
if (size>5){
|
|
dc->needAckNumber=dc->messageNumber;
|
|
dc->msgOut[dc->iface->ackPos+1]=_daveIncMessageNumber(dc);
|
|
}
|
|
_daveSendWithPrefix2NLpro(dc, size);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Sends a message and gets ackknowledge:
|
|
*/
|
|
int DECL2 _daveSendMessageNLpro(daveConnection * dc, PDU * p) {
|
|
if (daveDebug & daveDebugExchange) {
|
|
LOG2("%s enter _daveSendMessageNLpro\n", dc->iface->name);
|
|
}
|
|
if (_daveSendDialogNLpro(dc, /*2+*/p->hlen+p->plen+p->dlen)) {
|
|
LOG2("%s *** _daveSendMessageMPI error in _daveSendDialog.\n",dc->iface->name);
|
|
// return -1;
|
|
}
|
|
if (daveDebug & daveDebugExchange) {
|
|
LOG3("%s _daveSendMessageMPI send done. needAck %x\n", dc->iface->name,dc->needAckNumber);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int DECL2 _daveExchangeNLpro(daveConnection * dc, PDU * p) {
|
|
_daveSendMessageNLpro(dc, p);
|
|
dc->AnswLen=0;
|
|
return _daveGetResponseNLpro(dc);
|
|
}
|
|
|
|
int DECL2 _daveGetResponseNLpro(daveConnection *dc) {
|
|
int res;
|
|
if (daveDebug & daveDebugExchange) {
|
|
LOG2("%s _daveGetResponseNLpro receive message.\n", dc->iface->name);
|
|
}
|
|
res = _daveReadMPINLpro(dc->iface,dc->msgIn);
|
|
if (res<0) {
|
|
return res;
|
|
}
|
|
if (res==0) {
|
|
if (daveDebug & daveDebugPrintErrors) {
|
|
LOG2("%s *** _daveGetResponseNLpro no answer data.\n", dc->iface->name);
|
|
}
|
|
return -3;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int DECL2 _daveSendWithPrefixNLpro(daveConnection * dc, uc *b, int size)
|
|
{
|
|
uc target[daveMaxRawLen];
|
|
// uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14};
|
|
uc fix[]= {0x4,0x80,0x80,0x0C,0x14,0x14};
|
|
fix[4]=dc->connectionNumber2; // 1/10/05 trying Andrew's patch
|
|
fix[5]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
|
|
memcpy(target,fix,sizeof(fix));
|
|
memcpy(target+sizeof(fix),b,size);
|
|
target[1]|=dc->MPIAdr;
|
|
// target[2]|=dc->iface->localMPI;
|
|
memcpy(target+sizeof(fix),b,size);
|
|
return _daveSendWithCRCNLpro(dc->iface,target,size+sizeof(fix));
|
|
}
|
|
|
|
|
|
int DECL2 _daveSendWithPrefix2NLpro(daveConnection * dc, int size)
|
|
{
|
|
// uc fix[]= {04,0x80,0x80,0x0C,0x03,0x14};
|
|
uc fix[]= {0x14,0x80,0x80,0x0C,0x14,0x14};
|
|
fix[4]=dc->connectionNumber2; // 1/10/05 trying Andrew's patch
|
|
fix[5]=dc->connectionNumber; // 1/10/05 trying Andrew's patch
|
|
memcpy(dc->msgOut, fix, sizeof(fix));
|
|
dc->msgOut[1]|=dc->MPIAdr;
|
|
// dc->msgOut[2]|=dc->iface->localMPI; //???
|
|
/// dc->msgOut[sizeof(fix)]=0xF1;
|
|
/* if (daveDebug & daveDebugPacket)
|
|
_daveDump("_daveSendWithPrefix2",dc->msgOut,size+sizeof(fix)); */
|
|
return _daveSendWithCRCNLpro(dc->iface, dc->msgOut, size+sizeof(fix));
|
|
// }
|
|
return -1; /* shouldn't happen. */
|
|
}
|
|
|
|
int DECL2 _daveDisconnectPLCNLpro(daveConnection * dc)
|
|
{
|
|
int res;
|
|
uc m[]={
|
|
0x80
|
|
};
|
|
uc b1[daveMaxRawLen];
|
|
|
|
_daveSendSingleNLpro(dc->iface, STX);
|
|
|
|
res=_daveReadMPINLpro(dc->iface,b1);
|
|
_daveSendWithPrefixNLpro(dc, m, 1);
|
|
|
|
res=_daveReadMPINLpro(dc->iface,b1);
|
|
/*
|
|
res=_daveReadMPI(dc->iface,b1);
|
|
if (daveDebug & daveDebugConnect)
|
|
_daveDump("got",b1,10);
|
|
_daveSendSingle(dc->iface, DLE);
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
It seems to be better to complete this subroutine even if answers
|
|
from adapter are not as expected.
|
|
*/
|
|
int DECL2 _daveDisconnectAdapterNLpro(daveInterface * di) {
|
|
int res;
|
|
uc m2[]={
|
|
1,4,2
|
|
};
|
|
|
|
uc b1[daveMaxRawLen];
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s enter DisconnectAdapter()\n", di->name);
|
|
// _daveSendSingleNLpro(di, STX);
|
|
// res=_daveReadMPINLpro(di,b1);
|
|
/* if ((res!=1)||(b1[0]!=DLE)) return -1; */
|
|
_daveSendWithCRCNLpro(di, m2, sizeof(m2));
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s daveDisconnectAdapter() step 1.\n", di->name);
|
|
res=_daveReadMPINLpro(di, b1);
|
|
/* if ((res!=1)||(b1[0]!=DLE)) return -2; */
|
|
/*
|
|
res=_daveReadMPI(di, b1);
|
|
*/
|
|
/* if ((res!=1)||(b1[0]!=STX)) return -3; */
|
|
/*
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
LOG2("%s daveDisconnectAdapter() step 2.\n", di->name);
|
|
_daveSendSingle(di, DLE);
|
|
_daveReadChars2(di, b1, daveMaxRawLen);
|
|
// _daveReadChars(di, b1, tmo_normal, daveMaxRawLen);
|
|
_daveSendSingle(di, DLE);
|
|
if (daveDebug & daveDebugInitAdapter)
|
|
_daveDump("got",b1,10);
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
*/
|
|
int DECL2 _daveListReachablePartnersNLpro(daveInterface * di,char * buf) {
|
|
uc b1[daveMaxRawLen];
|
|
uc m1[]={1,7,2};
|
|
int res;
|
|
_daveSendWithCRCNLpro(di, m1, sizeof(m1));
|
|
res=_daveReadMPINLpro(di,b1);
|
|
// LOG2("res: %d\n", res);
|
|
if(135==res){
|
|
memcpy(buf,b1+8,126);
|
|
return 126;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
_openFunc SCP_open;
|
|
_closeFunc SCP_close;
|
|
_sendFunc SCP_send;
|
|
_receiveFunc SCP_receive;
|
|
//_SetHWndMsgFunc SetSinecHWndMsg;
|
|
//_SetHWndFunc SetSinecHWnd;
|
|
_get_errnoFunc SCP_get_errno;
|
|
|
|
|
|
/*
|
|
Changes:
|
|
09/09/04 applied patch for variable Profibus speed from Andrew Rostovtsew.
|
|
12/09/04 removed debug printf from daveConnectPLC.
|
|
12/09/04 found and fixed a bug in daveFreeResults(): The result set is provided by the
|
|
application and not necessarily dynamic. So we shall not free() it.
|
|
12/10/04 added single bit read/write functions.
|
|
12/12/04 added Timer/Counter read functions.
|
|
12/13/04 changed dumpPDU to dump multiple results from daveFuncRead
|
|
12/15/04 changed comments to pure C style
|
|
12/15/04 replaced calls to write() with makro daveWriteFile.
|
|
12/15/04 removed daveSendDialog. Was only used in 1 place.
|
|
12/16/04 removed daveReadCharsPPI. It is replaced by daveReadChars.
|
|
12/30/04 Read Timers and Counters from 200 family. These are different internal types!
|
|
01/02/05 Hopefully fixed local MPI<>0.
|
|
01/10/05 Fixed some debug levels in connectPLCMPI
|
|
01/10/05 Splitted daveExchangeMPI into the send and receive parts. They are separately
|
|
useable when communication is initiated by PLC.
|
|
01/10/05 Code cleanup. Some more things in connectPLC can be done using genaral
|
|
MPI communication subroutines.
|
|
01/10/05 Partially applied changes from Andrew Rostovtsew for multiple MPI connections
|
|
over the same adapter.
|
|
01/11/05 Lasts steps in connect PLC can be done with exchangeMPI.
|
|
01/26/05 replaced _daveConstructReadRequest by the sequence prepareReadRequest, addVarToReadRequest
|
|
01/26/05 added multiple write
|
|
02/02/05 added readIBHpacket
|
|
02/05/05 merged in fixes for (some?) ARM processors.
|
|
02/06/05 Code cleanup.
|
|
03/06/05 Fixed disconnectPLC_IBH for MPI adresses other than 2.
|
|
03/12/05 clear answLen before read
|
|
03/12/05 reset templ.packetNumber in connectPLC_IBH. This is necessary to reconnect if the
|
|
connection has been interrupted.
|
|
03/23/05 fixes for target PPI addresses other than 2.
|
|
|
|
04/05/05 reworked error reporting.
|
|
04/06/05 renamed swap functions. When I began libnodave on little endian i386 and Linux, I used
|
|
used Linux bswap functions. Then users (and later me) tried other systems without
|
|
a bswap. I also cannot use inline functions in Pascal. So I made my own bswaps. Then
|
|
I, made the core of my own swaps dependent of DAVE_LITTLE_ENDIAN conditional to support also
|
|
bigendien systems. Now I want to rename them from bswap to something else to avoid
|
|
confusion for LINUX/UNIX users. The new names are daveSwapIed_16 and daveSwapIed_32. This
|
|
shall say swap "if endianness differs". While I could have used similar functions
|
|
from the network API (htons etc.) on Unix and Win32, they may not be present on
|
|
e.g. microcontrollers.
|
|
I highly recommend to use these functions even when writing software for big endian
|
|
systems, where they effectively do nothing, as it will make your software portable.
|
|
04/09/05 removed template IBH_MPI header from daveConnection. Much of the information is
|
|
also available from other fields and the structure is simpler to define in other
|
|
languages.
|
|
04/09/05 removed CYGWIN defines. As there were no more differences against LINUX, it should
|
|
work with LINUX defines.
|
|
04/21/05 renamed LITTLEENDIAN to DAVE_LITTLE_ENDIAN because it seems to conflict with
|
|
another #define in winsock2.h.
|
|
05/09/05 renamed more functions to daveXXX.
|
|
05/11/05 added some functions for the convenience of usage with .net or mono. The goal is
|
|
that the application doesn't have to use members of data structures defined herein
|
|
directly. This avoids "unsafe" pointer expressions in .net/MONO. It should also ease
|
|
porting to VB or other languages for which it could be difficult to define byte by
|
|
byte equivalents of these structures.
|
|
05/12/05 applied some bug fixes from Axel Kinting.
|
|
05/12/05 applied bug fix from Lutz Nitzsche in daveSendISOpacket.
|
|
07/31/05 added message string copying for Visual Basic.
|
|
09/09/05 added code to ignore 7 byte packets from soft PLC 6ES7-4PY00-0YB7 in ISO_TCP.
|
|
09/10/05 added explicit type casts for pointers optained from malloc and calloc.
|
|
09/11/05 added read/write functions for long blocks of data.
|
|
09/24/05 Code clean up:
|
|
- Pointers to basic read/functions allow to redirect these functions, e.g. to libusb.
|
|
- More common code. Only the very fundamental read/functions differ between Linux and Win32.
|
|
09/24/05 added MPI protocol version 3. This is what Step7 talks to MPI adapters and seems
|
|
to be the only thing the Siemens USB-MPI adapter understands. This adapter is
|
|
currently only useable under Linux via libusb.
|
|
09/27/05 added bug fix from Renato Gartmann: freeResults didn't free() the memory used for
|
|
the result pointer array.
|
|
09/29/05 hopefully fixed superfluos STX in daveConnectPLCMPI2.
|
|
10/04/05 No there are adapters which want it...
|
|
10/05/05 Added first helper functions to use s7onlinx.dll for transport.
|
|
10/06/05 Added standard protocol specific functions to use s7onlinx.dll for transport.
|
|
10/06/05 renamed LITTLE_ENDIAN to DAVE_LITTLE_ENDIAN because it conflicts with
|
|
another #define in some headers on some ARM systems.
|
|
10/10/05 change some pointer increments for gcc-4.0.2 compatibility.
|
|
10/18/05 Indroduced a (temporary?) work around to allow applications to use normal sequence
|
|
disconnectAdapter/closePort also with s7online.
|
|
02/20/06 Added code to support NetLink Pro.
|
|
05/15/06 Applied changes from Ken Wenzel for NetLink Pro.
|
|
07/28/06 Added CRC calculation code from Peter Etheridge.
|
|
|
|
11/21/06 Hope to have fixed PDU length problem with IBHLink reported by Axel Kinting.
|
|
|
|
01/04/07 Set last byte of resp09 to don't care as reported by Axel Kinting.
|
|
02/07/08 Removed patch from Keith Harris for RTS line.
|
|
Version 0.8.4.5
|
|
07/10/09 Changed readISOpacket for Win32 to select() before recv().
|
|
07/10/09 Added daveCopyRAMtoROM
|
|
07/11/09 Changed calculation of netLen in doUpload()
|
|
*/
|