Initial Version

This commit is contained in:
2015-03-16 10:19:07 +00:00
parent 0b01f73db9
commit 14b3903e41
138 changed files with 39231 additions and 0 deletions

112
ChangeLog Normal file
View File

@@ -0,0 +1,112 @@
08/26/2003 partly unified versions for MPI adapter and IBH NetLink. Common stuff went into
nodaveCommon.c.
It should now work with S7-400 PLCs without a patch, but I've not tested this.
09/19/2003 Found remedy to prevent hangups in nodaveIBH.c
10/04/2003 Added the stuff for S7-200 over PPI cable.
10/05/2003 Wrote a test program for S7-200 over PPI cable.
03/13/2004 New functions to load a program block from the CPU.
03/14/2004 New area type system data (SD), daveArea_SD
03/26/2004 Retry PPI send, when response times out.
05/20/2004 Working ISO/TCP transport with CPs x43
06/14/2004 Fixed a bug regarding initialization of MPI/TS adapter
09/09/2004 Applied patch for variable Profibus speed from Andrew Rostovtsew.
The Profibus speed is a new parameter to daveNewInterface,
so you will need to change existing programs :-(
The parameter is only meaningful for the MPI protocol. It is meaningful for but
not yet supported by nodaveIBH. For all other protocols, any integer will do.
09/09/2004 Partly applied patch for compilation under Windows using Cygwin. Non-inline
data conversion functions replace the inlined ones and the byteswap.h from Linux.
09/12/2004 Implemented the read from multiple addresses.
10/10/2004 Made a unified version for all protocols (not yet IBH)
Added targets: make install, make dynamic to Makefile
12/01/2004 Fixed a bug in nodave.c that made compilers complain.
12/08/2004 Make read of analog inputs and outputs of 2xx work.
12/09/2004 Rmoved some debug printf()s.
12/09/2004 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/19/2004 First version for WIN32 and C
01/11/2005 Some code cleanup. Can now load program blocks into CPU 315. Win32 version
COM port settings fixed.
01/16/2005 Fixed reception of SZL-lists and block lists that don't fir into a single PDU.
01/26/2005 Multiple write implemented.
01/31/2005 Fixed isotest4.c, which could not be compiled with most C-compilers but mine,
however.
02/06/2005 Added workarounds to run on ARM processors.
02/12/2005 Integrated code for IBH NetLink.
02/13/2005 Put benchmarks into a separate file.
02/21/2005 Put program download and RUN/STOP functions into main test programs.
03/03/2005 Added fix from Axel Kinting that speeds up connections via IBH-NetLink.
03/03/2005 Added an option to set slot number in testISO_TCP.
03/03/2005 Changed dword to longword in pascal/nodave.pas for Delphi compatibility.
03/18/2005 Added code to access S7-200 with PPI protocol over IBH/MHJ-NetLink gateways.
03/18/2005 Changed unified benchmark code to use different memory areas on 200 or 300/400 CPUs.
04/06/2005 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 LITTLEENDIAN 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 swapIed_16 and swapIed_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/06/05 removed daveGetByte(), daveGetWORD(), daveGetWORDat(), getByteAt(), getInteger().
I first named these function according to Linux gcc C or PLC data types. But the
problem is that e.g. an INT on Siemens PLCs is a 16bit signed value. That's in turn
a short on gcc, an integer on Turbo Pascal, Is it still an integer in Delphi? So the
newer funtions are named after bit length and signedness of what they transfer to/from
PLC data copies. So daveGetU16 means get an unsigned 16 bit value.
04/10/05 Hope I fixed a bug in daveWriteBytes(), daveWriteBits(): In multiple write, if the
data to be written is of odd length, a 0 byte is appended before further data
follows. Versions 0.6.x appended such a byte to each write request if data length
is odd, but this fails under some circumstances.
This version appends the 0 byte only before further data is appended.
04/11/05 Fixed some bugs in test programs: Test programs (and all other applications) may
only retrieve data from internal buffer (via daveGetU8 etc.) if read result is 0.
Otherwise they would read from an undefined pointer.
04/21/05 renamed LITTLEENDIAN to LITTLE_ENDIAN because it seems to conflict with
another #define in winsock2.h.
04/29/05 Added a function to set Outputs in S7-200 when the PLC is in Stop mode. I called
this function "daveForce", but it DOES not force the values in the sense that the
forced state would override the result of the PLC program. Instead, as soon as you
switch to RUN, these settings are of no more effect. When you switch back to stop,
all outputs will be 0 again.
05/06/05 Because some users reported difficulties to use the prebuilt .dll and .lib file
created with Borland tools, I cared to add a .dll and .lib file compiled with MSVC.
I could download a VCToolkitSetup and the Platform SDK from Microsoft for free.
There is neither an IDE nor a make utility. So I used GNU-make and wine on Linux
to find out how to build the .dll and test programs.
The Makefile for GNU make is MAKEFILE.VC.WINE.
I cannot write a Makefile for MS-make without having it. So, for people which want
to use MSVC but cannot find out how to set options to the IDE or build a Makefile,
I provide a batch file which will just rebuild all.
05/11/05 Added a free function to dispose memory reserved by the library.
08/05/05 Added a string copy function.
08/06/05 Added daveGetProgramBlock() and daveFetMaxPDULen()
08/06/05 Added a PERL interface and example code.
08/07/05 Added a VB interface and example code.
10/06/05 Added usage of Siemens libraries for transport.
03/30/06 Bug fixes for .NET
05/15/06 Applied patch from Ken Wenzel for NETLink PRO
05/15/06 Applied PERL patches from H.-J. Beie
05/29/06 Bug fixes for AS511
Version 0.8.4.5
07/10/09 Changed readISOpacket for Win32 to select() before recv().
07/10/09 Added closeSocket()
07/10/09 Added daveCopyRAMtoROM
07/12/09 Changed flags in setport.c. Older version does not work with recent Linux
kernels.
07/12/09 Removed C-Sharp subdirectory. These were my first experiments with that
language.

View File

@@ -0,0 +1,75 @@
/*
Very simple program to read from a 300/400 PLC via MPI adapter.
This shall provide users with a simple starting point for their own
applications.
Part of Libnodave, a free communication libray for Siemens S7 200/300/400
(C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2005
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.
*/
using System;
class test {
static libnodave.daveOSserialType fds;
static libnodave.daveInterface di;
static libnodave.daveConnection dc;
static int rack=0;
static int slot=2;
public static int Main (string[] args)
{
int i,a=0,j,res,b=0,c=0;
float d=0;
fds.rfd=libnodave.openSocket(102,args[0]);
fds.wfd=fds.rfd;
if (fds.rfd>0) {
di =new libnodave.daveInterface(fds, "IF1", 0, libnodave.daveProtoISOTCP, libnodave.daveSpeed187k);
di.setTimeout(1000000);
// res=di.initAdapter(); // does nothing in ISO_TCP. But call it to keep your programs indpendent of protocols
// if(res==0) {
dc = new libnodave.daveConnection(di,0 , rack, slot);
if (0==dc.connectPLC()) {
res=dc.readBytes(libnodave.daveFlags, 0, 0, 16, null);
if (res==0) {
a=dc.getS32();
b=dc.getS32();
c=dc.getS32();
d=dc.getFloat();
Console.WriteLine("FD0: " + a);
Console.WriteLine("FD4: " + b);
Console.WriteLine("FD8: " + c);
Console.WriteLine("FD12: " + d);
} else
Console.WriteLine("error "+res+" "+libnodave.daveStrerror(res));
}
dc.disconnectPLC();
// }
// di.disconnectAdapter(); // does nothing in ISO_TCP. But call it to keep your programs indpendent of protocols
libnodave.closeSocket(fds.rfd);
} else {
Console.WriteLine("Couldn't open TCP connaction to "+args[0]);
return -1;
}
return 0;
}
}
/*
Version 0.8.4.5
07/10/09 Added closeSocket()
*/

BIN
Dot.NET/CS/simpleISO_TCP.exe Executable file

Binary file not shown.

70
Dot.NET/CS/simpleMPI.cs Normal file
View File

@@ -0,0 +1,70 @@
/*
Very simple program to read from a 300/400 PLC via MPI adapter.
This shall provide users with a simple starting point for their own
applications.
Part of Libnodave, a free communication libray for Siemens S7 200/300/400
(C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2005
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.
*/
using System;
class test {
static libnodave.daveOSserialType fds;
static libnodave.daveInterface di;
static libnodave.daveConnection dc;
static int localMPI=0;
static int plcMPI=2;
public static int Main (string[] args)
{
int i,a=0,j,res,b=0,c=0;
float d=0;
fds.rfd=libnodave.setPort(args[0],"38400",'O');
fds.wfd=fds.rfd;
if (fds.rfd>0) {
di =new libnodave.daveInterface(fds, "IF1", localMPI, libnodave.daveProtoMPI, libnodave.daveSpeed187k);
di.setTimeout(1000000);
res=di.initAdapter();
if(res==0) {
dc = new libnodave.daveConnection(di,plcMPI, 0, 0);
if (0==dc.connectPLC()) {
res=dc.readBytes(libnodave.daveFlags, 0, 0, 16, null);
if (res==0) {
a=dc.getS32();
b=dc.getS32();
c=dc.getS32();
d=dc.getFloat();
Console.WriteLine("FD0: " + a);
Console.WriteLine("FD4: " + b);
Console.WriteLine("FD8: " + c);
Console.WriteLine("FD12: " + d);
} else
Console.WriteLine("error "+res+" "+libnodave.daveStrerror(res));
}
dc.disconnectPLC();
}
di.disconnectAdapter();
libnodave.closePort(fds.rfd);
} else {
Console.WriteLine("Couldn't open serial port "+args[0]);
return -1;
}
return 0;
}
}

BIN
Dot.NET/CS/simpleMPI.exe Executable file

Binary file not shown.

70
Dot.NET/CS/simplePPI.cs Normal file
View File

@@ -0,0 +1,70 @@
/*
Very simple program to read from a 200 PLC via PPI cable.
This shall provide users with a simple starting point for their own
applications.
Part of Libnodave, a free communication libray for Siemens S7 200/300/400
(C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2005
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.
*/
using System;
class test {
static libnodave.daveOSserialType fds;
static libnodave.daveInterface di;
static libnodave.daveConnection dc;
static int localPPI=0;
static int plcPPI=2;
public static int Main (string[] args)
{
int i,a=0,j,res,b=0,c=0;
float d=0;
fds.rfd=libnodave.setPort(args[0],"9600",'E');
fds.wfd=fds.rfd;
if (fds.rfd>0) {
di =new libnodave.daveInterface(fds, "IF1", localPPI, libnodave.daveProtoPPI, libnodave.daveSpeed187k);
di.setTimeout(1000000);
// res=di.initAdapter(); // does nothing in PPI. But call it to keep your programs indpendent of protocols
// if(res==0) {
dc = new libnodave.daveConnection(di,plcPPI, 0, 0);
if (0==dc.connectPLC()) {
res=dc.readBytes(libnodave.daveFlags, 0, 0, 16, null);
if (res==0) {
a=dc.getS32();
b=dc.getS32();
c=dc.getS32();
d=dc.getFloat();
Console.WriteLine("FD0: " + a);
Console.WriteLine("FD4: " + b);
Console.WriteLine("FD8: " + c);
Console.WriteLine("FD12: " + d);
} else
Console.WriteLine("error "+res+" "+libnodave.daveStrerror(res));
}
dc.disconnectPLC();
// }
// di.disconnectAdapter(); // does nothing in PPI. But call it to keep your programs indpendent of protocols
libnodave.closePort(fds.rfd);
} else {
Console.WriteLine("Couldn't open serial port "+args[0]);
return -1;
}
return 0;
}
}

BIN
Dot.NET/CS/simplePPI.exe Executable file

Binary file not shown.

702
Dot.NET/CS/testMPI.cs Normal file
View File

@@ -0,0 +1,702 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
class test {
static void wait() {
}
static void usage() {
Console.WriteLine("Usage: testMPI [-d] [-w] serial port.");
Console.WriteLine("-w will try to write to Flag words. It will overwrite FB0 to FB15 (MB0 to MB15) !");
Console.WriteLine("-d will produce a lot of debug messages.");
Console.WriteLine("-b will run benchmarks. Specify -b and -w to run write benchmarks.");
Console.WriteLine("-z will read some SZL list items (diagnostic information).");
Console.WriteLine("-2 uses a slightly different version of the MPI protocol. Try it, if your Adapter doesn't work.");
Console.WriteLine("-m will run a test for multiple variable reads.");
Console.WriteLine("-c will write 0 to the PLC memory used in write tests.");
Console.WriteLine("-n will test newly added functions.");
Console.WriteLine("-a will read out everything from system state lists(SZLs).");
Console.WriteLine("-s stops the PLC.");
Console.WriteLine("-r tries to put the PLC in run mode.");
Console.WriteLine("--readout read program and data blocks from PLC.");
Console.WriteLine("--readoutall read all program and data blocks from PLC. Includes SFBs and SFCs.");
Console.WriteLine("-<number> will set the speed of MPI/PROFIBUS network to this value (in kBaud). Default is 187. Supported values are 9, 19, 45, 93, 187, 500 and 1500.");
Console.WriteLine("--mpi=<number> will use number as the MPI adddres of the PLC. Default is 2.");
Console.WriteLine("--mpi2=<number> Use this option to test simultaneous connections to 2 PLCs.");
Console.WriteLine(" It will use number as the MPI adddres of the 2nd PLC. Default is no 2nd PLC.");
Console.WriteLine(" Most tests are executed with the first PLC. The first read test is also done with");
Console.WriteLine(" with the 2nd one to demonstrate that this works.");
Console.WriteLine("--local=<number> will set the local MPI adddres to number. Default is 0.");
Console.WriteLine("--debug=<number> will set daveDebug to number.");
//#ifdef UNIX_STYLE
Console.WriteLine("Example: testMPI -w /dev/ttyS0");
//#endif
//#ifdef WIN_STYLE
Console.WriteLine("Example: testMPI -w COM1");
//#endif
}
static int initSuccess=0;
static int localMPI=0;
static int plcMPI=2;
static int plc2MPI=-1;
static int adrPos=0;
static int useProto=libnodave.daveProtoMPI;
static int speed=libnodave.daveSpeed187k;
static libnodave.daveOSserialType fds;
static libnodave.daveInterface di;
static libnodave.daveConnection dc;
static bool doWrite=false;
static bool doClear=false;
static bool doRun=false;
static bool doStop=false;
static bool doWbit=false;
static int wbit;
static bool doSZLread=false;
static bool doSZLreadAll=false;
static bool doBenchmark=false;
static bool doReadout=false;
static bool doSFBandSFC=false;
static bool doExperimental=false;
static bool doMultiple=false;
static bool doNewfunctions=false;
static void readSZL(libnodave.daveConnection dc, int id, int index) {
int res, SZLid, indx, SZcount, SZlen,i,j,len;
byte[] ddd=new byte[3000];
Console.WriteLine(String.Format("Trying to read SZL-ID {0:X04}, index {1:X02}",id,index));
res=dc.readSZL(id,index, ddd, 3000);
Console.WriteLine("Function result: "+res+" "+libnodave.daveStrerror(res)+" len:"+dc.getAnswLen());
if (dc.getAnswLen()>=4) {
len=dc.getAnswLen()-8;
SZLid=libnodave.getU16from(ddd,0);
indx=libnodave.getU16from(ddd,2);
Console.WriteLine(String.Format("result SZL ID {0:X04}, index {1:X02}",SZLid,indx));
int d=8;
if (dc.getAnswLen()>=8) {
SZlen=libnodave.getU16from(ddd,4);
SZcount=libnodave.getU16from(ddd,6);
Console.WriteLine(" "+SZcount+" elements of "+SZlen+" bytes");
if(len>0){
for (i=0;i<SZcount;i++){
if(len>0){
for (j=0; j<SZlen; j++){
if(len>0){
Console.Write(String.Format("{0:X02},",ddd[d]));
d++;
}
len--;
}
Console.WriteLine(" ");
}
}
}
}
}
Console.WriteLine(" ");
}
static void readSZLAll(libnodave.daveConnection dc) {
byte[] d=new byte[1000];
int res, SZLid, indx, SZcount, SZlen,i,j, rid, rind;
res=dc.readSZL(0,0, d, 1000);
Console.WriteLine(" "+res+" "+dc.getAnswLen());
if ((dc.getAnswLen())>=4) {
SZLid=dc.getU16();
indx=dc.getU16();
Console.WriteLine(String.Format("result SZL ID {0:X04} index {1:X02}",SZLid,indx));
if ((dc.getAnswLen())>=8) {
SZlen=0x100*d[4]+d[5];
SZcount=0x100*d[6]+d[7];
Console.WriteLine("%d elements of %d bytes\n",SZcount,SZlen);
for (i=0;i<SZcount;i++){
rid=libnodave.getU16from(d,i*SZlen+8);
rind=0;
Console.WriteLine(String.Format("\nID:{0:X04} index {1:X02}",rid,rind));
readSZL(dc, rid, rind);
}
}
}
Console.WriteLine("\n");
}
static void loadBlocksOfType(libnodave.daveConnection dc, int blockType, bool doReadout) {
int j, i, len, k;
int res;
byte[] blockBuffer=new byte[20000];
byte[] dbe=new byte[2000];
j=dc.ListBlocksOfType(blockType, dbe);
if (j<0) {
Console.WriteLine("error "+(-j)+" = " + libnodave.daveStrerror(-j));
return;
}
string blockName=libnodave.blockName(blockType);
Console.WriteLine(j+" blocks of type "+blockName);
for(i=0; i<j; i++) {
int number=BitConverter.ToInt16(dbe,4*i+0);
Console.WriteLine(libnodave.blockName(blockType)+" "+number);
if(doReadout) {
len=1111;
dc.getProgramBlock(blockType, number, blockBuffer, ref(len));
blockName=libnodave.blockName(blockType)+number+".mc7";
FileStream fs= new FileStream(blockName,FileMode.CreateNew);
BinaryWriter w= new BinaryWriter(fs);
for (k=0; k<len; k++)
w.Write(blockBuffer[k]);
}
}
}
static void rBenchmark(libnodave.daveConnection dc, int bmArea) {
int i,res,maxReadLen,areaNumber;
double usec;
long t1, t2;
libnodave.resultSet rs=new libnodave.resultSet();
maxReadLen=dc.getMaxPDULen()-46;
areaNumber=0;
if(bmArea==libnodave.daveDB) areaNumber=1;
Console.WriteLine("Now going to do read benchmark with minimum block length of 1.\n");
t1=Environment.TickCount;
for (i=1;i<101;i++) {
dc.readBytes(bmArea, areaNumber,0, 1, null);
if (i%10==0) {
Console.Write("..."+i);
}
}
t2=Environment.TickCount;
usec = 0.001 * (t2 - t1);
Console.WriteLine(" 100 reads took "+usec+" secs.");
Console.WriteLine("Now going to do read benchmark with shurely supported block length "+maxReadLen);
t1=Environment.TickCount;
for (i=1;i<101;i++) {
dc.readBytes(bmArea, areaNumber, 0, maxReadLen, null);
if (i%10==0) {
Console.Write("..."+i);
}
}
t2=Environment.TickCount;
usec = 0.001 * (t2 - t1);
Console.WriteLine(" 100 reads took "+usec+" secs. ");
Console.WriteLine("Now going to do read benchmark with 5 variables in a single request.");
Console.WriteLine("running...");
t1=Environment.TickCount;
for (i=1;i<101;i++) {
libnodave.PDU p=dc.prepareReadRequest();
p.addVarToReadRequest(libnodave.daveInputs,0,0,6);
p.addVarToReadRequest(libnodave.daveFlags,0,0,6);
p.addVarToReadRequest(libnodave.daveFlags,0,6,6);
p.addVarToReadRequest(bmArea,areaNumber,4,54);
p.addVarToReadRequest(bmArea,areaNumber,4,4);
res=dc.execReadRequest(p, rs);
if (res!=0) Console.WriteLine("\nerror "+res+" = "+libnodave.daveStrerror(res));
if (i%10==0) {
Console.Write("..."+i);
}
}
t2=Environment.TickCount;
usec = 0.001 * (t2 - t1);
Console.WriteLine(" 100 reads took "+usec+" secs.");
}
public static int Main (string[] args)
{
int i,a=0,j,res,b=0,c=0;
float d=0;
byte[] buf1=new byte[libnodave.davePartnerListSize];
if (args.Length <1) {
usage();
return -1;
}
while (args[adrPos][0]=='-') {
if (args[adrPos].StartsWith("--debug=")) {
libnodave.daveSetDebug(Convert.ToInt32(args[adrPos].Substring(8)));
Console.WriteLine("setting debug to: ",Convert.ToInt32(args[adrPos].Substring(8)));
} else if (args[adrPos].StartsWith("-d")) {
libnodave.daveSetDebug(libnodave.daveDebugAll);
} else if (args[adrPos].StartsWith("-s")) {
doStop=true;
} else if (args[adrPos].StartsWith("-w")) {
doWrite=true;
} else if (args[adrPos].StartsWith("-b")) {
doBenchmark=true;
} else if (args[adrPos].StartsWith("--readoutall")) {
doReadout=true;
doSFBandSFC=true;
} else if (args[adrPos].StartsWith("--readout")) {
doReadout=true;
} else if (args[adrPos].StartsWith("-r")) {
doRun=true;
} else if (args[adrPos].StartsWith("-e")) {
doExperimental=true;
} else if (args[adrPos].StartsWith("--local=")) {
localMPI=Convert.ToInt32(args[adrPos].Substring(8));
Console.WriteLine("setting local MPI address to: "+localMPI);
} else if (args[adrPos].StartsWith("--mpi=")) {
plcMPI=Convert.ToInt32(args[adrPos].Substring(6));
Console.WriteLine("setting MPI address of PLC to: "+plcMPI);
} else if (args[adrPos].StartsWith("--mpi2=")) {
plc2MPI=Convert.ToInt32(args[adrPos].Substring(7));
Console.WriteLine("setting MPI address of 2md PLC to: "+plc2MPI);
} else if (args[adrPos].StartsWith("--wbit=")) {
wbit=Convert.ToInt32(args[adrPos].Substring(7));
Console.WriteLine("setting bit number: "+wbit);
doWbit=true;
} else if (args[adrPos].StartsWith("-z")) {
doSZLread=true;
} else if (args[adrPos].StartsWith("-a")) {
doSZLreadAll=true;
} else if (args[adrPos].StartsWith("-m")) {
doMultiple=true;
} else if (args[adrPos].StartsWith("-c")) {
doClear=true;
} else if (args[adrPos].StartsWith("-n")) {
doNewfunctions=true;
} else if (args[adrPos].StartsWith("-2")) {
useProto=libnodave.daveProtoMPI2;
} else if (args[adrPos].StartsWith("-3")) {
useProto=libnodave.daveProtoMPI3;
} else if (args[adrPos].StartsWith("-9")) {
speed=libnodave.daveSpeed9k;
} else if (args[adrPos].StartsWith("-19")) {
speed=libnodave.daveSpeed19k;
} else if (args[adrPos].StartsWith("-45")) {
speed=libnodave.daveSpeed45k;
} else if (args[adrPos].StartsWith("-93")) {
speed=libnodave.daveSpeed93k;
} else if (args[adrPos].StartsWith("-500")) {
speed=libnodave.daveSpeed500k;
} else if (args[adrPos].StartsWith("-1500")) {
speed=libnodave.daveSpeed1500k;
}
adrPos++;
if (args.Length<=adrPos) {
usage();
return -1;
}
}
fds.rfd=libnodave.setPort(args[adrPos],"38400",'O');
fds.wfd=fds.rfd;
if (fds.rfd>0) {
di =new libnodave.daveInterface(fds, "IF1", localMPI, useProto, speed);
di.setTimeout(5000000);
for (i=0; i<3; i++) {
if (0==di.initAdapter()) {
initSuccess=1;
// a= libnodave.daveListReachablePartners(di,buf1);
a= di.listReachablePartners(buf1);
Console.WriteLine("daveListReachablePartners List length: "+a);
if (a>0) {
for (j=0;j<a;j++) {
if (buf1[j]==libnodave.daveMPIReachable)
Console.WriteLine("Device at address: "+j);
}
}
break;
} else di.disconnectAdapter();
}
if (initSuccess==0) {
Console.WriteLine("Couldn't connect to Adapter!.\n Please try again. You may also try the option -2 for some adapters.");
return -3;
}
dc = new libnodave.daveConnection(di,plcMPI,0,0);
if (0==dc.connectPLC()) {;
res=dc.readBytes(libnodave.daveFlags, 0, 0, 16, null);
if (res==0) {
a=dc.getS32();
b=dc.getS32();
c=dc.getS32();
d=dc.getFloat();
Console.WriteLine("FD0: " + a);
Console.WriteLine("FD4: " + b);
Console.WriteLine("FD8: " + c);
Console.WriteLine("FD12: " + d);
} else
Console.WriteLine("error "+res+" "+libnodave.daveStrerror(res));
if(doExperimental) {
Console.WriteLine("Trying to read outputs");
res=dc.readBytes(libnodave.daveOutputs, 0, 0, 2, null);
Console.WriteLine("function result: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
if (res==0) {
Console.Write("Bytes:");
for (b=0; b<dc.getAnswLen(); b++) {
c=dc.getU8();
Console.Write(String.Format(" {0:X0}, ",c));
}
Console.WriteLine("");
}
a=0x01;
Console.WriteLine("Trying to write outputs");
res=dc.writeBytes(libnodave.daveOutputs, 0, 0, 1, BitConverter.GetBytes(a));
Console.WriteLine("function result: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
libnodave.daveSetDebug(libnodave.daveDebugAll);
res=dc.force200(libnodave.daveOutputs,0,0);
Console.WriteLine("function result: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
libnodave.daveSetDebug(0);
res=dc.force200(libnodave.daveOutputs,0,1);
Console.WriteLine("function result of force: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
wait();
res=dc.force200(libnodave.daveOutputs,0,2);
Console.WriteLine("function result of force: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
wait();
res=dc.force200(libnodave.daveOutputs,0,3);
Console.WriteLine("function result of force: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
wait();
res=dc.force200(libnodave.daveOutputs,1,4);
Console.WriteLine("function result of force: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
wait();
res=dc.force200(libnodave.daveOutputs,2,5);
Console.WriteLine("function result of force: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
wait();
res=dc.force200(libnodave.daveOutputs,3,7);
Console.WriteLine("function result of force: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
wait();
Console.WriteLine("Trying to read outputs again\n");
res=dc.readBytes(libnodave.daveOutputs, 0, 0, 4, null);
Console.WriteLine("function result: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
if (res==0) {
Console.Write("Bytes:");
for (b=0; b<dc.getAnswLen(); b++) {
c=dc.getU8();
Console.Write(String.Format(" {0:X0}, ",c));
}
Console.WriteLine("");
}
}
if(doWrite) {
Console.WriteLine("Now we write back these data after incrementing the integers by 1,2 and 3 and the float by 1.1.\n");
wait();
/*
Attention! you need to daveSwapIed little endian variables before using them as a buffer for
daveWriteBytes() or before copying them into a buffer for daveWriteBytes()!
*/
a=libnodave.daveSwapIed_32(a+1);
dc.writeBytes(libnodave.daveFlags,0,0,4,BitConverter.GetBytes(a));
b=libnodave.daveSwapIed_32(b+2);
dc.writeBytes(libnodave.daveFlags,0,4,4,BitConverter.GetBytes(b));
c=libnodave.daveSwapIed_32(c+3);
dc.writeBytes(libnodave.daveFlags,0,8,4,BitConverter.GetBytes(c));
d=libnodave.toPLCfloat(d+1.1f);
dc.writeBytes(libnodave.daveFlags,0,12,4,BitConverter.GetBytes(d));
/*
* Read back and show the new values, so users may notice the difference:
*/
dc.readBytes(libnodave.daveFlags,0,0,16, null);
a=dc.getU32();
b=dc.getU32();
c=dc.getU32();
d=dc.getFloat();
Console.WriteLine("FD0: "+a);
Console.WriteLine("FD4: "+b);
Console.WriteLine("FD8: "+c);
Console.WriteLine("FD12: "+d);
} // doWrite
if(doClear) {
Console.WriteLine("Now writing 0 to the bytes FB0...FB15.\n");
// wait();
byte[] aa={0,0,0,0};
dc.writeBytes(libnodave.daveFlags,0,0,4,aa);
dc.writeBytes(libnodave.daveFlags,0,4,4,aa);
dc.writeBytes(libnodave.daveFlags,0,8,4,aa);
dc.writeBytes(libnodave.daveFlags,0,12,4,aa);
dc.readBytes(libnodave.daveFlags,0,0,16, null);
a=dc.getU32();
b=dc.getU32();
c=dc.getU32();
d=dc.getFloat();
Console.WriteLine("FD0: "+a);
Console.WriteLine("FD4: "+b);
Console.WriteLine("FD8: "+c);
Console.WriteLine("FD12: "+d);
} // doClear
if(doSZLread) {
readSZL(dc,0x92,0x0);
readSZL(dc,0xB4,0x1024);
readSZL(dc,0x111,0x1);
readSZL(dc,0xD91,0x0);
readSZL(dc,0x232,0x4);
readSZL(dc,0x1A0,0x0);
readSZL(dc,0x0A0,0x0);
}
if(doSZLreadAll) {
readSZLAll(dc);
}
if(doStop) {
dc.stop();
}
if(doRun) {
dc.start();
}
if(doBenchmark) {
rBenchmark(dc,libnodave.daveFlags);
}
if(doReadout) {
loadBlocksOfType(dc, libnodave.daveBlockType_OB, doReadout);
loadBlocksOfType(dc, libnodave.daveBlockType_FC, doReadout);
loadBlocksOfType(dc, libnodave.daveBlockType_FB, doReadout);
loadBlocksOfType(dc, libnodave.daveBlockType_DB, doReadout);
loadBlocksOfType(dc, libnodave.daveBlockType_SDB, doReadout);
}
if(doNewfunctions) {
int saveDebug=libnodave.daveGetDebug();
Console.WriteLine("Trying to read two consecutive bits from DB11.DBX0.1");;
res=dc.readBits(libnodave.daveDB, 11, 1, 2, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
Console.WriteLine("Trying to read no bit (length 0) from DB17.DBX0.1");
res=dc.readBits(libnodave.daveDB, 17, 1, 0, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
libnodave.daveSetDebug(libnodave.daveGetDebug()|libnodave.daveDebugPDU);
Console.WriteLine("Trying to read a single bit from DB17.DBX0.3\n");
res=dc.readBits(libnodave.daveDB, 17, 3, 1, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
Console.WriteLine("Trying to read a single bit from E0.2\n");
res=dc.readBits(libnodave.daveInputs, 0, 2, 1, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
a=0;
Console.WriteLine("Writing 0 to EB0\n");
res=dc.writeBytes(libnodave.daveOutputs, 0, 0, 1, BitConverter.GetBytes(libnodave.daveSwapIed_32(a)));
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
a=1;
Console.WriteLine("Trying to set single bit E0.5\n");
res=dc.writeBits(libnodave.daveOutputs, 0, 5, 1, BitConverter.GetBytes(libnodave.daveSwapIed_32(a)));
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
Console.WriteLine("Trying to read 1 byte from AAW0\n");
res=dc.readBytes(libnodave.daveAnaIn, 0, 0, 2, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
a=2341;
Console.WriteLine("Trying to write 1 word (2 bytes) to AAW0\n");
res=dc.writeBytes(libnodave.daveAnaOut, 0, 0, 2, BitConverter.GetBytes(libnodave.daveSwapIed_32(a)));
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
Console.WriteLine("Trying to read 4 items from Timers\n");
res=dc.readBytes(libnodave.daveTimer, 0, 0, 4, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
if(res==0) {
d=dc.getSeconds();
Console.WriteLine("Time: %0.3f, ",d);
d=dc.getSeconds();
Console.WriteLine("%0.3f, ",d);
d=dc.getSeconds();
Console.WriteLine("%0.3f, ",d);
d=dc.getSeconds();
Console.WriteLine(" %0.3f\n",d);
d=dc.getSecondsAt(0);
Console.WriteLine("Time: %0.3f, ",d);
d=dc.getSecondsAt(2);
Console.WriteLine("%0.3f, ",d);
d=dc.getSecondsAt(4);
Console.WriteLine("%0.3f, ",d);
d=dc.getSecondsAt(6);
Console.WriteLine(" %0.3f\n",d);
}
Console.WriteLine("Trying to read 4 items from Counters\n");
res=dc.readBytes(libnodave.daveCounter, 0, 0, 4, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
if(res==0) {
c=dc.getCounterValue();
Console.WriteLine("Count: %d, ",c);
c=dc.getCounterValue();
Console.WriteLine("%d, ",c);
c=dc.getCounterValue();
Console.WriteLine("%d, ",c);
c=dc.getCounterValue();
Console.WriteLine(" %d\n",c);
c=dc.getCounterValueAt(0);
Console.WriteLine("Count: %d, ",c);
c=dc.getCounterValueAt(2);
Console.WriteLine("%d, ",c);
c=dc.getCounterValueAt(4);
Console.WriteLine("%d, ",c);
c=dc.getCounterValueAt(6);
Console.WriteLine(" %d\n",c);
}
libnodave.PDU p=dc.prepareReadRequest();
p.addVarToReadRequest(libnodave.daveInputs,0,0,1);
p.addVarToReadRequest(libnodave.daveFlags,0,0,4);
p.addVarToReadRequest(libnodave.daveDB,6,20,2);
p.addVarToReadRequest(libnodave.daveTimer,0,0,4);
p.addVarToReadRequest(libnodave.daveTimer,0,1,4);
p.addVarToReadRequest(libnodave.daveTimer,0,2,4);
p.addVarToReadRequest(libnodave.daveCounter,0,0,4);
p.addVarToReadRequest(libnodave.daveCounter,0,1,4);
p.addVarToReadRequest(libnodave.daveCounter,0,2,4);
libnodave.resultSet rs=new libnodave.resultSet();
res=dc.execReadRequest(p, rs);
libnodave.daveSetDebug(saveDebug);
}
// System.GarbageCollect();
if(doMultiple) {
Console.WriteLine("Now testing read multiple variables.\n"
+"This will read 1 Byte from inputs,\n"
+"4 bytes from flags, 2 bytes from DB6,\n"
+"and other 2 bytes from flags");
wait();
libnodave.PDU p=dc.prepareReadRequest();
p.addVarToReadRequest(libnodave.daveInputs,0,0,1);
p.addVarToReadRequest(libnodave.daveFlags,0,0,4);
p.addVarToReadRequest(libnodave.daveDB,6,20,2);
p.addVarToReadRequest(libnodave.daveFlags,0,12,2);
p.addBitVarToReadRequest(libnodave.daveFlags, 0, 25 /* 25 is 3.1*/, 1);
libnodave.resultSet rs=new libnodave.resultSet();
res=dc.execReadRequest(p, rs);
Console.Write("Input Byte 0: ");
res=dc.useResult(rs, 0); // first result
if (res==0) {
a=dc.getU8();
Console.WriteLine(a);
} else
Console.WriteLine("*** Error: "+libnodave.daveStrerror(res));
Console.Write("Flag DWord 0: ");
res=dc.useResult(rs, 1); // 2nd result
if (res==0) {
a=dc.getS16();
Console.WriteLine(a);
} else
Console.WriteLine("*** Error: "+libnodave.daveStrerror(res));
Console.Write("DB 6 Word 20: ");
res=dc.useResult(rs, 2); // 3rd result
if (res==0) {
a=dc.getS16();
Console.WriteLine(a);
} else
Console.WriteLine("*** Error: "+libnodave.daveStrerror(res));
Console.Write("Flag Word 12: ");
res=dc.useResult(rs, 3); // 4th result
if (res==0) {
a=dc.getU16();
Console.WriteLine(a);
} else
Console.WriteLine("*** Error: "+libnodave.daveStrerror(res));
Console.Write("Flag F3.1: ");
res=dc.useResult(rs, 4); // 4th result
if (res==0) {
a=dc.getU8();
Console.WriteLine(a);
} else
Console.WriteLine("*** Error: "+libnodave.daveStrerror(res));
Console.Write("non existing result (we read 5 items, but try to use a 6th one): ");
res=dc.useResult(rs, 5); // 5th result
if (res==0) {
a=dc.getU16();
Console.WriteLine(a);
} else
Console.WriteLine("*** Error: "+libnodave.daveStrerror(res));
if (doWrite){
Console.WriteLine("Now testing write multiple variables:\n"
+"IB0, FW0, QB0, DB6:DBW20 and DB20:DBD24 in a single multiple write.");
wait();
// libnodave.daveSetDebug(0xffff);
byte[] aa={0};
libnodave.PDU p2=dc.prepareWriteRequest();
p2.addVarToWriteRequest(libnodave.daveInputs,0,0,1, aa);
p2.addVarToWriteRequest(libnodave.daveFlags,0,4,2, aa);
p2.addVarToWriteRequest(libnodave.daveOutputs,0,0,2, aa);
p2.addVarToWriteRequest(libnodave.daveDB,6,20,2, aa);
p2.addVarToWriteRequest(libnodave.daveDB,20,24,4, aa);
aa[0]=1;
p2.addBitVarToWriteRequest(libnodave.daveFlags, 0, 27 /* 27 is 3.3*/, 1, aa);
rs =new libnodave.resultSet();
res=dc.execWriteRequest(p2, rs);
Console.WriteLine("Result code for the entire multiple write operation: "+res+"="+libnodave.daveStrerror(res));
/*
// I could list the single result codes like this, but I want to tell
// which item should have been written, so I do it in 5 individual lines:
for (i=0;i<rs.numResults;i++){
res=rs.results[i].error;
Console.WriteLine("result code from writing item %d: %d=%s\n",i,res,libnodave.libnodave.daveStrerror(res));
}
*/
int err=rs.getErrorOfResult(0);
Console.WriteLine("Result code for writing IB0: "+err+"="+libnodave.daveStrerror(err));
err=rs.getErrorOfResult(1);
Console.WriteLine("Result code for writing FW4: "+err+"="+libnodave.daveStrerror(err));
err=rs.getErrorOfResult(2);
Console.WriteLine("Result code for writing QB0: "+err+"="+libnodave.daveStrerror(err));
err=rs.getErrorOfResult(3);
Console.WriteLine("Result code for writing DB6:DBW20: "+err+"="+libnodave.daveStrerror(err));
err=rs.getErrorOfResult(4);
Console.WriteLine("Result code for writing DB20:DBD24:"+err+"="+libnodave.daveStrerror(err));
err=rs.getErrorOfResult(5);
Console.WriteLine("Result code for writing F3.3: "+err+"="+libnodave.daveStrerror(err));
/*
* Read back and show the new values, so users may notice the difference:
*/
dc.readBytes(libnodave.daveFlags,0,0,16, null);
a=dc.getU32();
b=dc.getU32();
c=dc.getU32();
d=dc.getFloat();
Console.WriteLine("FD0: %d\n",a);
Console.WriteLine("FD4: %d\n",b);
Console.WriteLine("FD8: %d\n",c);
Console.WriteLine("FD12: %f\n",d);
} // doWrite
}
dc.disconnectPLC();
}
di.disconnectAdapter();
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Here we are");
} else {
Console.WriteLine("Couldn't open serial port "+args[adrPos]);
return -1;
}
return 0;
}
}

BIN
Dot.NET/CS/testMPI.exe Executable file

Binary file not shown.

650
Dot.NET/CS/testS7online.cs Normal file
View File

@@ -0,0 +1,650 @@
using System;
using System.Runtime.InteropServices;
class test {
static void wait() {
}
static void usage() {
Console.WriteLine("Usage: testS7online [-d] [-w] access point");
Console.WriteLine("-w will try to write to Flag words. It will overwrite FB0 to FB15 (MB0 to MB15) !");
Console.WriteLine("-d will produce a lot of debug messages.");
Console.WriteLine("-b will run benchmarks. Specify -b and -w to run write benchmarks.");
Console.WriteLine("-z will read some SZL list items (diagnostic information).");
Console.WriteLine("-2 uses a slightly different version of the MPI protocol. Try it, if your Adapter doesn't work.");
Console.WriteLine("-m will run a test for multiple variable reads.");
Console.WriteLine("-c will write 0 to the PLC memory used in write tests.");
Console.WriteLine("-n will test newly added functions.");
Console.WriteLine("-a will read out everything from system state lists(SZLs).");
Console.WriteLine("-s stops the PLC.");
Console.WriteLine("-r tries to put the PLC in run mode.");
Console.WriteLine("--readout read program and data blocks from PLC.");
Console.WriteLine("--readoutall read all program and data blocks from PLC. Includes SFBs and SFCs.");
Console.WriteLine("-<number> will set the speed of MPI/PROFIBUS network to this value (in kBaud). Default is 187. Supported values are 9, 19, 45, 93, 187, 500 and 1500.");
Console.WriteLine("--mpi=<number> will use number as the MPI adddres of the PLC. Default is 2.");
Console.WriteLine("--mpi2=<number> Use this option to test simultaneous connections to 2 PLCs.");
Console.WriteLine(" It will use number as the MPI adddres of the 2nd PLC. Default is no 2nd PLC.");
Console.WriteLine(" Most tests are executed with the first PLC. The first read test is also done with");
Console.WriteLine(" with the 2nd one to demonstrate that this works.");
Console.WriteLine("--local=<number> will set the local MPI adddres to number. Default is 0.");
Console.WriteLine("--debug=<number> will set daveDebug to number.");
Console.WriteLine("Example: testS7online -w /S7ONLINE");
}
static int initSuccess=0;
static int localMPI=0;
static int plcMPI=2;
static int plc2MPI=-1;
static int adrPos=0;
static int useProto=libnodave.daveProtoS7online;
static int speed=libnodave.daveSpeed187k;
static libnodave.daveOSserialType fds;
static libnodave.daveInterface di;
static libnodave.daveConnection dc;
static bool doWrite=false;
static bool doClear=false;
static bool doRun=false;
static bool doStop=false;
static bool doWbit=false;
static int wbit;
static bool doSZLread=false;
static bool doSZLreadAll=false;
static bool doBenchmark=false;
static bool doReadout=false;
static bool doSFBandSFC=false;
static bool doExperimental=false;
static bool doMultiple=false;
static bool doNewfunctions=false;
static void readSZL(libnodave.daveConnection dc, int id, int index) {
int res, SZLid, indx, SZcount, SZlen,i,j,len;
byte[] ddd=new byte[3000];
Console.WriteLine(String.Format("Trying to read SZL-ID {0:X04}, index {1:X02}",id,index));
res=dc.readSZL(id,index, ddd,3000);
Console.WriteLine("Function result: "+res+" "+libnodave.daveStrerror(res)+" len:"+dc.getAnswLen());
if (dc.getAnswLen()>=4) {
len=dc.getAnswLen()-8;
SZLid=libnodave.getU16from(ddd,0);
indx=libnodave.getU16from(ddd,2);
Console.WriteLine(String.Format("result SZL ID {0:X04}, index {1:X02}",SZLid,indx));
int d=8;
if (dc.getAnswLen()>=8) {
SZlen=libnodave.getU16from(ddd,4);
SZcount=libnodave.getU16from(ddd,6);
Console.WriteLine(" "+SZcount+" elements of "+SZlen+" bytes");
if(len>0){
for (i=0;i<SZcount;i++){
if(len>0){
for (j=0; j<SZlen; j++){
if(len>0){
Console.Write(String.Format("{0:X02},",ddd[d]));
d++;
}
len--;
}
Console.WriteLine(" ");
}
}
}
}
}
Console.WriteLine(" ");
}
static void readSZLAll(libnodave.daveConnection dc) {
byte[] d=new byte[1000];
int res, SZLid, indx, SZcount, SZlen,i,j, rid, rind;
res=dc.readSZL(0,0, d, 1000);
Console.WriteLine(" "+res+" "+dc.getAnswLen());
if ((dc.getAnswLen())>=4) {
SZLid=dc.getU16();
indx=dc.getU16();
Console.WriteLine(String.Format("result SZL ID {0:X04} index {1:X02}",SZLid,indx));
if ((dc.getAnswLen())>=8) {
SZlen=0x100*d[4]+d[5];
SZcount=0x100*d[6]+d[7];
Console.WriteLine("%d elements of %d bytes\n",SZcount,SZlen);
for (i=0;i<SZcount;i++){
rid=libnodave.getU16from(d,i*SZlen+8);
rind=0;
Console.WriteLine(String.Format("\nID:{0:X04} index {1:X02}",rid,rind));
readSZL(dc, rid, rind);
}
}
}
Console.WriteLine("\n");
}
static void rBenchmark(libnodave.daveConnection dc, int bmArea) {
int i,res,maxReadLen,areaNumber;
double usec;
long t1, t2;
libnodave.resultSet rs=new libnodave.resultSet();
maxReadLen=dc.getMaxPDULen()-46;
areaNumber=0;
if(bmArea==libnodave.daveDB) areaNumber=1;
Console.WriteLine("Now going to do read benchmark with minimum block length of 1.\n");
t1=Environment.TickCount;
for (i=1;i<101;i++) {
dc.readBytes(bmArea, areaNumber,0, 1, null);
if (i%10==0) {
Console.Write("..."+i);
}
}
t2=Environment.TickCount;
usec = 0.001 * (t2 - t1);
Console.WriteLine(" 100 reads took "+usec+" secs.");
Console.WriteLine("Now going to do read benchmark with shurely supported block length "+maxReadLen);
t1=Environment.TickCount;
for (i=1;i<101;i++) {
dc.readBytes(bmArea, areaNumber, 0, maxReadLen, null);
if (i%10==0) {
Console.Write("..."+i);
}
}
t2=Environment.TickCount;
usec = 0.001 * (t2 - t1);
Console.WriteLine(" 100 reads took "+usec+" secs. ");
Console.WriteLine("Now going to do read benchmark with 5 variables in a single request.");
Console.WriteLine("running...");
t1=Environment.TickCount;
for (i=1;i<101;i++) {
libnodave.PDU p=dc.prepareReadRequest();
p.addVarToReadRequest(libnodave.daveInputs,0,0,6);
p.addVarToReadRequest(libnodave.daveFlags,0,0,6);
p.addVarToReadRequest(libnodave.daveFlags,0,6,6);
p.addVarToReadRequest(bmArea,areaNumber,4,54);
p.addVarToReadRequest(bmArea,areaNumber,4,4);
res=dc.execReadRequest(p, rs);
if (res!=0) Console.WriteLine("\nerror "+res+" = "+libnodave.daveStrerror(res));
if (i%10==0) {
Console.Write("..."+i);
}
}
t2=Environment.TickCount;
usec = 0.001 * (t2 - t1);
Console.WriteLine(" 100 reads took "+usec+" secs.");
}
public static int Main (string[] args)
{
int i,a=0,j,res,b=0,c=0;
float d=0;
byte[] buf1=new byte[libnodave.davePartnerListSize];
if (args.Length <1) {
usage();
return -1;
}
while (args[adrPos][0]=='-') {
if (args[adrPos].StartsWith("--debug=")) {
libnodave.daveSetDebug(Convert.ToInt32(args[adrPos].Substring(8)));
Console.WriteLine("setting debug to: ",Convert.ToInt32(args[adrPos].Substring(8)));
} else if (args[adrPos].StartsWith("-d")) {
libnodave.daveSetDebug(libnodave.daveDebugAll);
} else if (args[adrPos].StartsWith("-s")) {
doStop=true;
} else if (args[adrPos].StartsWith("-w")) {
doWrite=true;
} else if (args[adrPos].StartsWith("-b")) {
doBenchmark=true;
} else if (args[adrPos].StartsWith("--readoutall")) {
doReadout=true;
doSFBandSFC=true;
} else if (args[adrPos].StartsWith("--readout")) {
doReadout=true;
} else if (args[adrPos].StartsWith("-r")) {
doRun=true;
} else if (args[adrPos].StartsWith("-e")) {
doExperimental=true;
} else if (args[adrPos].StartsWith("--local=")) {
localMPI=Convert.ToInt32(args[adrPos].Substring(8));
Console.WriteLine("setting local MPI address to: "+localMPI);
} else if (args[adrPos].StartsWith("--mpi=")) {
plcMPI=Convert.ToInt32(args[adrPos].Substring(6));
Console.WriteLine("setting MPI address of PLC to: "+plcMPI);
} else if (args[adrPos].StartsWith("--mpi2=")) {
plc2MPI=Convert.ToInt32(args[adrPos].Substring(7));
Console.WriteLine("setting MPI address of 2md PLC to: "+plc2MPI);
} else if (args[adrPos].StartsWith("--wbit=")) {
wbit=Convert.ToInt32(args[adrPos].Substring(7));
Console.WriteLine("setting bit number: "+wbit);
doWbit=true;
} else if (args[adrPos].StartsWith("-z")) {
doSZLread=true;
} else if (args[adrPos].StartsWith("-a")) {
doSZLreadAll=true;
} else if (args[adrPos].StartsWith("-m")) {
doMultiple=true;
} else if (args[adrPos].StartsWith("-c")) {
doClear=true;
} else if (args[adrPos].StartsWith("-n")) {
doNewfunctions=true;
} else if (args[adrPos].StartsWith("-9")) {
speed=libnodave.daveSpeed9k;
} else if (args[adrPos].StartsWith("-19")) {
speed=libnodave.daveSpeed19k;
} else if (args[adrPos].StartsWith("-45")) {
speed=libnodave.daveSpeed45k;
} else if (args[adrPos].StartsWith("-93")) {
speed=libnodave.daveSpeed93k;
} else if (args[adrPos].StartsWith("-500")) {
speed=libnodave.daveSpeed500k;
} else if (args[adrPos].StartsWith("-1500")) {
speed=libnodave.daveSpeed1500k;
}
adrPos++;
if (args.Length<=adrPos) {
usage();
return -1;
}
}
fds.rfd=libnodave.openS7online(args[adrPos]);
fds.wfd=fds.rfd;
if (fds.rfd>=0) {
di =new libnodave.daveInterface(fds, "IF1", localMPI, useProto, speed);
di.setTimeout(5000000);
for (i=0; i<3; i++) {
if (0==di.initAdapter()) {
initSuccess=1;
a= di.listReachablePartners(buf1);
Console.WriteLine("daveListReachablePartners List length: "+a);
if (a>0) {
for (j=0;j<a;j++) {
if (buf1[j]==libnodave.daveMPIReachable)
Console.WriteLine("Device at address: "+j);
}
}
break;
} else di.disconnectAdapter();
}
if (initSuccess==0) {
Console.WriteLine("Couldn't connect to Adapter!.\n Please try again. You may also try the option -2 for some adapters.");
return -3;
}
dc = new libnodave.daveConnection(di,plcMPI,0,0);
if (0==dc.connectPLC()) {;
res=dc.readBytes(libnodave.daveFlags, 0, 0, 16, null);
if (res==0) {
a=dc.getS32();
b=dc.getS32();
c=dc.getS32();
d=dc.getFloat();
Console.WriteLine("FD0: " + a);
Console.WriteLine("FD4: " + b);
Console.WriteLine("FD8: " + c);
Console.WriteLine("FD12: " + d);
} else
Console.WriteLine("error "+res+" "+libnodave.daveStrerror(res));
if(doExperimental) {
Console.WriteLine("Trying to read outputs");
res=dc.readBytes(libnodave.daveOutputs, 0, 0, 2, null);
Console.WriteLine("function result: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
if (res==0) {
Console.Write("Bytes:");
for (b=0; b<dc.getAnswLen(); b++) {
c=dc.getU8();
Console.Write(String.Format(" {0:X0}, ",c));
}
Console.WriteLine("");
}
a=0x01;
Console.WriteLine("Trying to write outputs");
res=dc.writeBytes(libnodave.daveOutputs, 0, 0, 1, BitConverter.GetBytes(a));
Console.WriteLine("function result: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
libnodave.daveSetDebug(libnodave.daveDebugAll);
res=dc.force200(libnodave.daveOutputs,0,0);
Console.WriteLine("function result: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
libnodave.daveSetDebug(0);
res=dc.force200(libnodave.daveOutputs,0,1);
Console.WriteLine("function result of force: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
wait();
res=dc.force200(libnodave.daveOutputs,0,2);
Console.WriteLine("function result of force: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
wait();
res=dc.force200(libnodave.daveOutputs,0,3);
Console.WriteLine("function result of force: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
wait();
res=dc.force200(libnodave.daveOutputs,1,4);
Console.WriteLine("function result of force: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
wait();
res=dc.force200(libnodave.daveOutputs,2,5);
Console.WriteLine("function result of force: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
wait();
res=dc.force200(libnodave.daveOutputs,3,7);
Console.WriteLine("function result of force: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
wait();
Console.WriteLine("Trying to read outputs again\n");
res=dc.readBytes(libnodave.daveOutputs, 0, 0, 4, null);
Console.WriteLine("function result: "+res+"="+libnodave.daveStrerror(res)+" "+dc.getAnswLen());
if (res==0) {
Console.Write("Bytes:");
for (b=0; b<dc.getAnswLen(); b++) {
c=dc.getU8();
Console.Write(String.Format(" {0:X0}, ",c));
}
Console.WriteLine("");
}
}
if(doWrite) {
Console.WriteLine("Now we write back these data after incrementing the integers by 1,2 and 3 and the float by 1.1.\n");
wait();
/*
Attention! you need to daveSwapIed little endian variables before using them as a buffer for
daveWriteBytes() or before copying them into a buffer for daveWriteBytes()!
*/
a=libnodave.daveSwapIed_32(a+1);
dc.writeBytes(libnodave.daveFlags,0,0,4,BitConverter.GetBytes(a));
b=libnodave.daveSwapIed_32(b+2);
dc.writeBytes(libnodave.daveFlags,0,4,4,BitConverter.GetBytes(b));
c=libnodave.daveSwapIed_32(c+3);
dc.writeBytes(libnodave.daveFlags,0,8,4,BitConverter.GetBytes(c));
d=libnodave.toPLCfloat(d+1.1f);
dc.writeBytes(libnodave.daveFlags,0,12,4,BitConverter.GetBytes(d));
/*
* Read back and show the new values, so users may notice the difference:
*/
dc.readBytes(libnodave.daveFlags,0,0,16, null);
a=dc.getU32();
b=dc.getU32();
c=dc.getU32();
d=dc.getFloat();
Console.WriteLine("FD0: "+a);
Console.WriteLine("FD4: "+b);
Console.WriteLine("FD8: "+c);
Console.WriteLine("FD12: "+d);
} // doWrite
if(doClear) {
Console.WriteLine("Now writing 0 to the bytes FB0...FB15.\n");
// wait();
byte[] aa={0,0,0,0};
dc.writeBytes(libnodave.daveFlags,0,0,4,aa);
dc.writeBytes(libnodave.daveFlags,0,4,4,aa);
dc.writeBytes(libnodave.daveFlags,0,8,4,aa);
dc.writeBytes(libnodave.daveFlags,0,12,4,aa);
dc.readBytes(libnodave.daveFlags,0,0,16, null);
a=dc.getU32();
b=dc.getU32();
c=dc.getU32();
d=dc.getFloat();
Console.WriteLine("FD0: "+a);
Console.WriteLine("FD4: "+b);
Console.WriteLine("FD8: "+c);
Console.WriteLine("FD12: "+d);
} // doClear
if(doSZLread) {
readSZL(dc,0x92,0x0);
readSZL(dc,0xB4,0x1024);
readSZL(dc,0x111,0x1);
readSZL(dc,0xD91,0x0);
readSZL(dc,0x232,0x4);
readSZL(dc,0x1A0,0x0);
readSZL(dc,0x0A0,0x0);
}
if(doSZLreadAll) {
readSZLAll(dc);
}
if(doStop) {
dc.stop();
}
if(doRun) {
dc.start();
}
if(doBenchmark) {
rBenchmark(dc,libnodave.daveFlags);
}
if(doNewfunctions) {
int saveDebug=libnodave.daveGetDebug();
Console.WriteLine("Trying to read two consecutive bits from DB11.DBX0.1");;
res=dc.readBits(libnodave.daveDB, 11, 1, 2, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
Console.WriteLine("Trying to read no bit (length 0) from DB17.DBX0.1");
res=dc.readBits(libnodave.daveDB, 17, 1, 0, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
libnodave.daveSetDebug(libnodave.daveGetDebug()|libnodave.daveDebugPDU);
Console.WriteLine("Trying to read a single bit from DB17.DBX0.3\n");
res=dc.readBits(libnodave.daveDB, 17, 3, 1, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
Console.WriteLine("Trying to read a single bit from E0.2\n");
res=dc.readBits(libnodave.daveInputs, 0, 2, 1, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
a=0;
Console.WriteLine("Writing 0 to EB0\n");
res=dc.writeBytes(libnodave.daveOutputs, 0, 0, 1, BitConverter.GetBytes(libnodave.daveSwapIed_32(a)));
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
a=1;
Console.WriteLine("Trying to set single bit E0.5\n");
res=dc.writeBits(libnodave.daveOutputs, 0, 5, 1, BitConverter.GetBytes(libnodave.daveSwapIed_32(a)));
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
Console.WriteLine("Trying to read 1 byte from AAW0\n");
res=dc.readBytes(libnodave.daveAnaIn, 0, 0, 2, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
a=2341;
Console.WriteLine("Trying to write 1 word (2 bytes) to AAW0\n");
res=dc.writeBytes(libnodave.daveAnaOut, 0, 0, 2, BitConverter.GetBytes(libnodave.daveSwapIed_32(a)));
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
Console.WriteLine("Trying to read 4 items from Timers\n");
res=dc.readBytes(libnodave.daveTimer, 0, 0, 4, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
if(res==0) {
d=dc.getSeconds();
Console.WriteLine("Time: %0.3f, ",d);
d=dc.getSeconds();
Console.WriteLine("%0.3f, ",d);
d=dc.getSeconds();
Console.WriteLine("%0.3f, ",d);
d=dc.getSeconds();
Console.WriteLine(" %0.3f\n",d);
d=dc.getSecondsAt(0);
Console.WriteLine("Time: %0.3f, ",d);
d=dc.getSecondsAt(2);
Console.WriteLine("%0.3f, ",d);
d=dc.getSecondsAt(4);
Console.WriteLine("%0.3f, ",d);
d=dc.getSecondsAt(6);
Console.WriteLine(" %0.3f\n",d);
}
Console.WriteLine("Trying to read 4 items from Counters\n");
res=dc.readBytes(libnodave.daveCounter, 0, 0, 4, null);
Console.WriteLine("function result:" + res+ "="+libnodave.daveStrerror(res));
if(res==0) {
c=dc.getCounterValue();
Console.WriteLine("Count: %d, ",c);
c=dc.getCounterValue();
Console.WriteLine("%d, ",c);
c=dc.getCounterValue();
Console.WriteLine("%d, ",c);
c=dc.getCounterValue();
Console.WriteLine(" %d\n",c);
c=dc.getCounterValueAt(0);
Console.WriteLine("Count: %d, ",c);
c=dc.getCounterValueAt(2);
Console.WriteLine("%d, ",c);
c=dc.getCounterValueAt(4);
Console.WriteLine("%d, ",c);
c=dc.getCounterValueAt(6);
Console.WriteLine(" %d\n",c);
}
libnodave.PDU p=dc.prepareReadRequest();
p.addVarToReadRequest(libnodave.daveInputs,0,0,1);
p.addVarToReadRequest(libnodave.daveFlags,0,0,4);
p.addVarToReadRequest(libnodave.daveDB,6,20,2);
p.addVarToReadRequest(libnodave.daveTimer,0,0,4);
p.addVarToReadRequest(libnodave.daveTimer,0,1,4);
p.addVarToReadRequest(libnodave.daveTimer,0,2,4);
p.addVarToReadRequest(libnodave.daveCounter,0,0,4);
p.addVarToReadRequest(libnodave.daveCounter,0,1,4);
p.addVarToReadRequest(libnodave.daveCounter,0,2,4);
libnodave.resultSet rs=new libnodave.resultSet();
res=dc.execReadRequest(p, rs);
libnodave.daveSetDebug(saveDebug);
}
// System.GarbageCollect();
if(doMultiple) {
Console.WriteLine("Now testing read multiple variables.\n"
+"This will read 1 Byte from inputs,\n"
+"4 bytes from flags, 2 bytes from DB6,\n"
+"and other 2 bytes from flags");
wait();
libnodave.PDU p=dc.prepareReadRequest();
p.addVarToReadRequest(libnodave.daveInputs,0,0,1);
p.addVarToReadRequest(libnodave.daveFlags,0,0,4);
p.addVarToReadRequest(libnodave.daveDB,6,20,2);
p.addVarToReadRequest(libnodave.daveFlags,0,12,2);
p.addBitVarToReadRequest(libnodave.daveFlags, 0, 25 /* 25 is 3.1*/, 1);
libnodave.resultSet rs=new libnodave.resultSet();
res=dc.execReadRequest(p, rs);
Console.Write("Input Byte 0: ");
res=dc.useResult(rs, 0); // first result
if (res==0) {
a=dc.getU8();
Console.WriteLine(a);
} else
Console.WriteLine("*** Error: "+libnodave.daveStrerror(res));
Console.Write("Flag DWord 0: ");
res=dc.useResult(rs, 1); // 2nd result
if (res==0) {
a=dc.getS16();
Console.WriteLine(a);
} else
Console.WriteLine("*** Error: "+libnodave.daveStrerror(res));
Console.Write("DB 6 Word 20: ");
res=dc.useResult(rs, 2); // 3rd result
if (res==0) {
a=dc.getS16();
Console.WriteLine(a);
} else
Console.WriteLine("*** Error: "+libnodave.daveStrerror(res));
Console.Write("Flag Word 12: ");
res=dc.useResult(rs, 3); // 4th result
if (res==0) {
a=dc.getU16();
Console.WriteLine(a);
} else
Console.WriteLine("*** Error: "+libnodave.daveStrerror(res));
Console.Write("Flag F3.1: ");
res=dc.useResult(rs, 4); // 4th result
if (res==0) {
a=dc.getU8();
Console.WriteLine(a);
} else
Console.WriteLine("*** Error: "+libnodave.daveStrerror(res));
Console.Write("non existing result (we read 5 items, but try to use a 6th one): ");
res=dc.useResult(rs, 5); // 5th result
if (res==0) {
a=dc.getU16();
Console.WriteLine(a);
} else
Console.WriteLine("*** Error: "+libnodave.daveStrerror(res));
if (doWrite){
Console.WriteLine("Now testing write multiple variables:\n"
+"IB0, FW0, QB0, DB6:DBW20 and DB20:DBD24 in a single multiple write.");
wait();
// libnodave.daveSetDebug(0xffff);
byte[] aa={0};
libnodave.PDU p2=dc.prepareWriteRequest();
p2.addVarToWriteRequest(libnodave.daveInputs,0,0,1, aa);
p2.addVarToWriteRequest(libnodave.daveFlags,0,4,2, aa);
p2.addVarToWriteRequest(libnodave.daveOutputs,0,0,2, aa);
p2.addVarToWriteRequest(libnodave.daveDB,6,20,2, aa);
p2.addVarToWriteRequest(libnodave.daveDB,20,24,4, aa);
aa[0]=1;
p2.addBitVarToWriteRequest(libnodave.daveFlags, 0, 27 /* 27 is 3.3*/, 1, aa);
rs =new libnodave.resultSet();
res=dc.execWriteRequest(p2, rs);
Console.WriteLine("Result code for the entire multiple write operation: "+res+"="+libnodave.daveStrerror(res));
/*
// I could list the single result codes like this, but I want to tell
// which item should have been written, so I do it in 5 individual lines:
for (i=0;i<rs.numResults;i++){
res=rs.results[i].error;
Console.WriteLine("result code from writing item %d: %d=%s\n",i,res,libnodave.libnodave.daveStrerror(res));
}
*/
int err=rs.getErrorOfResult(0);
Console.WriteLine("Result code for writing IB0: "+err+"="+libnodave.daveStrerror(err));
err=rs.getErrorOfResult(1);
Console.WriteLine("Result code for writing FW4: "+err+"="+libnodave.daveStrerror(err));
err=rs.getErrorOfResult(2);
Console.WriteLine("Result code for writing QB0: "+err+"="+libnodave.daveStrerror(err));
err=rs.getErrorOfResult(3);
Console.WriteLine("Result code for writing DB6:DBW20: "+err+"="+libnodave.daveStrerror(err));
err=rs.getErrorOfResult(4);
Console.WriteLine("Result code for writing DB20:DBD24:"+err+"="+libnodave.daveStrerror(err));
err=rs.getErrorOfResult(5);
Console.WriteLine("Result code for writing F3.3: "+err+"="+libnodave.daveStrerror(err));
/*
* Read back and show the new values, so users may notice the difference:
*/
dc.readBytes(libnodave.daveFlags,0,0,16, null);
a=dc.getU32();
b=dc.getU32();
c=dc.getU32();
d=dc.getFloat();
Console.WriteLine("FD0: %d\n",a);
Console.WriteLine("FD4: %d\n",b);
Console.WriteLine("FD8: %d\n",c);
Console.WriteLine("FD12: %f\n",d);
} // doWrite
}
dc.disconnectPLC();
}
di.disconnectAdapter();
libnodave.closeS7online(fds.rfd);
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Here we are");
} else {
Console.WriteLine("Couldn't open s7 online "+args[adrPos]);
return -1;
}
return 0;
}
}

BIN
Dot.NET/CS/testS7online.exe Executable file

Binary file not shown.

5
Dot.NET/MSnet.bat Executable file
View File

@@ -0,0 +1,5 @@
csc /t:library libnodave.net.cs
vbc /r:libnodave.net.dll VB/simpleMPI.vb
csc /r:libnodave.net CS/testMPI.cs
csc /r:libnodave.net CS/testS7online.cs

6
Dot.NET/VB/readme Executable file
View File

@@ -0,0 +1,6 @@
The test programs for VB.Net are far less complete than those for C#. The reason is that I compile
and test under MONO, a free .Net clone. MONO's Basic compiler is in an early stage and if I add
some more lines, it doesn't work any more.
If you have #Develop: A user reported that you can put a CSharp program into #Develop and have it
converted to VB without any problem or even manual work.

80
Dot.NET/VB/simpleMPI.vb Executable file
View File

@@ -0,0 +1,80 @@
'
' Very simple program to read from a 300/400 PLC via MPI adapter.
' This shall provide users wit a simple starting point for their own
' applications.
'
' Part of Libnodave, a free communication libray for Siemens S7 200/300/400
'
' (C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2005
'
' 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.
'
Imports System
Imports Microsoft.VisualBasic
Class test
Public Shared Function Main(args() As String) As Integer
Dim localMPI As Integer = 0, plcMPI As Integer = 2
Dim fds As libnodave.daveOSserialType
Dim di as libnodave.daveInterface
Dim dc as libnodave.daveConnection
Dim res As Integer
Dim a,b,c As Integer, d As Single
Dim buf(1000) as byte
Dim s As string
Console.WriteLine("Hello World!")
fds.rfd=libnodave.setPort(args(0), "38400", AscW("O")) ' step 1, open a connection
fds.wfd=fds.rfd
if fds.rfd>0 then ' if step 1 is ok
di =new libnodave.daveInterface(fds, "My Interface 1", localMPI, libnodave.daveProtoMPI, libnodave.daveSpeed187k)
di.setTimeout(1000000) ' Make this longer if you have a very long response time
res = di.initAdapter
'
if res=0 then ' init Adapter is ok
dc = new libnodave.daveConnection(di, plcMPI, 0, 0) ' rack amd slot don't matter in case of MPI
res=dc.connectPLC()
if res=0 then
res=dc.readBytes(libnodave.daveFlags, 0, 0, 16, buf)
if res=0 then
a=dc.getS32
b=dc.getS32
c=dc.getS32
d=dc.getFloat
Console.WriteLine("FD0: {0:d}", a)
Console.WriteLine("FD4: {0:d}", b)
Console.WriteLine("FD8: {0:d}", c)
Console.WriteLine("FD12: {0:f}", d)
else
Console.WriteLine("Error {0:d}={1:s} in readBytes.", res, libnodave.daveStrerror(res))
end if
dc.disconnectPLC()
else
Console.WriteLine("Error {0:d}={1:s} in connectPLC.", res, libnodave.daveStrerror(res))
end if
di.disconnectAdapter() ' End connection to adapter
else
Console.WriteLine("Error {0:d}={1:s} in initAdapter.", res, libnodave.daveStrerror(res))
end if
libnodave.closePort(fds.rfd) ' Clean up
else
Console.WriteLine("Couldn't open serial port {0:s}", args(0))
return -1
end if 'fds.rfd >0
end Function 'Main
END class

BIN
Dot.NET/VB/simpleMPI2.exe Executable file

Binary file not shown.

90
Dot.NET/VB/simpleMPI2.vb Executable file
View File

@@ -0,0 +1,90 @@
'
' Very simple program to read from a 300/400 PLC via MPI adapter.
' This shall provide users wit a simple starting point for their own
' applications.
'
' Part of Libnodave, a free communication libray for Siemens S7 200/300/400
'
' (C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2005
'
' 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.
'
Imports System
Imports Microsoft.VisualBasic
Class test
Public Shared Function Main(args() As String) As Integer
Dim localMPI As Integer = 0, plcMPI As Integer = 2
Dim fds As libnodave.daveOSserialType
Dim di as libnodave.daveInterface
Dim dc as libnodave.daveConnection
Dim res As Integer
Dim a,b,c As Integer, d As Single
Dim buf(1000) as byte
Dim s As string
Console.WriteLine("Hello World!")
fds.rfd=libnodave.setPort(args(0), "38400", AscW("O")) ' step 1, open a connection
fds.wfd=fds.rfd
if fds.rfd>0 then ' if step 1 is ok
di =new libnodave.daveInterface(fds, "My Interface 1", localMPI, libnodave.daveProtoMPI, libnodave.daveSpeed187k)
di.setTimeout(1000000) ' Make this longer if you have a very long response time
res = di.initAdapter
'
if res=0 then ' init Adapter is ok
dc = new libnodave.daveConnection(di, plcMPI, 0, 0) ' rack amd slot don't matter in case of MPI
res=dc.connectPLC()
if res=0 then
res=dc.readBytes(libnodave.daveFlags, 0, 0, 16, buf)
if res=0 then
a=dc.getS32
b=dc.getS32
c=dc.getS32
d=dc.getFloat
Console.WriteLine("FD0: {0:d}", a)
Console.WriteLine("FD4: {0:d}", b)
Console.WriteLine("FD8: {0:d}", c)
Console.WriteLine("FD12: {0:f}", d)
else
s=libnodave.daveStrerror(res)
' Console.WriteLine("Error {0:d}={1:s} in readBytes.", res, libnodave.daveStrerror(res))
Console.Write("Error {0:d} in readBytes.", res)
Console.WriteLine(s)
end if
dc.disconnectPLC()
else
s=libnodave.daveStrerror(res)
' Console.WriteLine("Error {0:d}={1:s} in connectPLC.", res, libnodave.daveStrerror(res))
Console.Write("Error {0:d} in connectPLC.", res)
Console.WriteLine(s)
end if
di.disconnectAdapter() ' End connection to adapter
else
s=libnodave.daveStrerror(res)
' Console.WriteLine("Error {0:d}={1:s} in initAdapter.", res, libnodave.daveStrerror(res))
' Console.WriteLine("Error {0:d}={1:s} in initAdapter.", res, s)
Console.Write("Error {0:d} in initAdapter.", res)
Console.WriteLine(s)
end if
libnodave.closePort(fds.rfd) ' Clean up
else
Console.WriteLine("Couldn't open serial port {0:s}", args(0))
return -1
end if 'fds.rfd >0
end Function 'Main
END class

815
Dot.NET/libnodave.net.cs Normal file
View File

@@ -0,0 +1,815 @@
/*
This implements a "glue" layer between libnodave.dll and applications written
in MS .Net languages.
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
or VIPA Speed7 with builtin ethernet support.
(C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2002..2005
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.
*/
using System;
using System.Runtime.InteropServices;
public class libnodave {
/*
This struct contains whatever your Operating System uses to hold an in and outgoing
connection to external devices.
*/
public struct daveOSserialType {
public int rfd;
public int wfd;
}
/*
Protocol types to be used with new daveInterface:
*/
public static readonly int daveProtoMPI=0; /* MPI for S7 300/400 */
public static readonly int daveProtoMPI2 = 1; /* MPI for S7 300/400, "Andrew's version" */
public static readonly int daveProtoMPI3 = 2; /* MPI for S7 300/400, Step 7 Version, experimental */
public static readonly int daveProtoMPI4 = 3; /* MPI for S7 300/400, "Andrew's version" with STX */
public static readonly int daveProtoPPI = 10; /* PPI for S7 200 */
public static readonly int daveProtoAS511 = 20; /* S5 via programming interface */
public static readonly int daveProtoS7online = 50; /* use s7onlinx.dll for transport */
public static readonly int daveProtoISOTCP = 122; /* ISO over TCP */
public static readonly int daveProtoISOTCP243 = 123; /* ISO over TCP with CP243 */
public static readonly int daveProtoMPI_IBH = 223; /* MPI with IBH NetLink MPI to ethernet gateway */
public static readonly int daveProtoPPI_IBH = 224; /* PPI with IBH NetLink PPI to ethernet gateway */
public static readonly int daveProtoUserTransport = 255; /* Libnodave will pass the PDUs of */
/* S7 Communication to user defined */
/* call back functions. */
/*
* ProfiBus speed constants. This is the baudrate on MPI network, NOT between adapter and PC:
*/
public static readonly int daveSpeed9k = 0;
public static readonly int daveSpeed19k = 1;
public static readonly int daveSpeed187k = 2;
public static readonly int daveSpeed500k = 3;
public static readonly int daveSpeed1500k = 4;
public static readonly int daveSpeed45k = 5;
public static readonly int daveSpeed93k = 6;
/*
Some function codes (yet unused ones may be incorrect).
*/
public static readonly int daveFuncOpenS7Connection = 0xF0;
public static readonly int daveFuncRead = 0x04;
public static readonly int daveFuncWrite = 0x05;
public static readonly int daveFuncRequestDownload = 0x1A;
public static readonly int daveFuncDownloadBlock = 0x1B;
public static readonly int daveFuncDownloadEnded = 0x1C;
public static readonly int daveFuncStartUpload = 0x1D;
public static readonly int daveFuncUpload = 0x1E;
public static readonly int daveFuncEndUpload = 0x1F;
public static readonly int daveFuncInsertBlock = 0x28;
/*
S7 specific constants:
*/
public static readonly int daveBlockType_OB = '8';
public static readonly int daveBlockType_DB = 'A';
public static readonly int daveBlockType_SDB = 'B';
public static readonly int daveBlockType_FC = 'C';
public static readonly int daveBlockType_SFC = 'D';
public static readonly int daveBlockType_FB = 'E';
public static readonly int daveBlockType_SFB = 'F';
/*
Use these constants for parameter "area" in daveReadBytes and daveWriteBytes
*/
public static readonly int daveSysInfo = 0x3; /* System info of 200 family */
public static readonly int daveSysFlags = 0x5; /* System flags of 200 family */
public static readonly int daveAnaIn = 0x6; /* analog inputs of 200 family */
public static readonly int daveAnaOut = 0x7; /* analog outputs of 200 family */
public static readonly int daveP = 0x80; /* direct peripheral access */
public static readonly int daveInputs = 0x81;
public static readonly int daveOutputs = 0x82;
public static readonly int daveFlags = 0x83;
public static readonly int daveDB = 0x84; /* data blocks */
public static readonly int daveDI = 0x85; /* instance data blocks */
public static readonly int daveLocal = 0x86; /* not tested */
public static readonly int daveV = 0x87; /* don't know what it is */
public static readonly int daveCounter = 28; /* S7 counters */
public static readonly int daveTimer = 29; /* S7 timers */
public static readonly int daveCounter200 = 30; /* IEC counters (200 family) */
public static readonly int daveTimer200 = 31; /* IEC timers (200 family) */
/**
Library specific:
**/
/*
Result codes. Genarally, 0 means ok,
>0 are results (also errors) reported by the PLC
<0 means error reported by library code.
*/
public static readonly int daveResOK = 0; /* means all ok */
public static readonly int daveResNoPeripheralAtAddress = 1; /* CPU tells there is no peripheral at address */
public static readonly int daveResMultipleBitsNotSupported = 6; /* CPU tells it does not support to read a bit block with a */
/* length other than 1 bit. */
public static readonly int daveResItemNotAvailable200 = 3; /* means a a piece of data is not available in the CPU, e.g. */
/* when trying to read a non existing DB or bit bloc of length<>1 */
/* This code seems to be specific to 200 family. */
public static readonly int daveResItemNotAvailable = 10; /* means a a piece of data is not available in the CPU, e.g. */
/* when trying to read a non existing DB */
public static readonly int daveAddressOutOfRange = 5; /* means the data address is beyond the CPUs address range */
public static readonly int daveWriteDataSizeMismatch = 7; /* means the write data size doesn't fit item size */
public static readonly int daveResCannotEvaluatePDU = -123; /* PDU is not understood by libnodave */
public static readonly int daveResCPUNoData = -124;
public static readonly int daveUnknownError = -125;
public static readonly int daveEmptyResultError = -126;
public static readonly int daveEmptyResultSetError = -127;
public static readonly int daveResUnexpectedFunc = -128;
public static readonly int daveResUnknownDataUnitSize = -129;
public static readonly int daveResShortPacket = -1024;
public static readonly int daveResTimeout = -1025;
/*
Error code to message string conversion:
Call this function to get an explanation for error codes returned by other functions.
*/
/*
[DllImport("libnodave.dll")]
public static extern string
daveStrerror(int res);
*/
[DllImport("libnodave.dll", EntryPoint="daveStrerror" )]
public static extern IntPtr
_daveStrerror(int res);
public static string daveStrerror(int res) {
// return Marshal.PtrToStringAuto(_daveStrerror(res));
return Marshal.PtrToStringAnsi(_daveStrerror(res)); //hope this fixes bug with intzerpreting string as unicode. Thanks to Luca Domenichini
}
/*
Copy an internal String into an external string buffer. This is needed to interface
with Visual Basic. Maybe it is helpful elsewhere, too.
C# can well work with C strings.
*/
//EXPORTSPEC void DECL2 daveStringCopy(char * intString, char * extString);
/*
Max number of bytes in a single message.
*/
public static readonly int daveMaxRawLen = 2048;
/*
Some definitions for debugging:
*/
public static readonly int daveDebugRawRead = 0x01; /* Show the single bytes received */
public static readonly int daveDebugSpecialChars = 0x02; /* Show when special chars are read */
public static readonly int daveDebugRawWrite = 0x04; /* Show the single bytes written */
public static readonly int daveDebugListReachables = 0x08; /* Show the steps when determine devices in MPI net */
public static readonly int daveDebugInitAdapter = 0x10; /* Show the steps when Initilizing the MPI adapter */
public static readonly int daveDebugConnect = 0x20; /* Show the steps when connecting a PLC */
public static readonly int daveDebugPacket = 0x40;
public static readonly int daveDebugByte = 0x80;
public static readonly int daveDebugCompare = 0x100;
public static readonly int daveDebugExchange = 0x200;
public static readonly int daveDebugPDU = 0x400; /* debug PDU handling */
public static readonly int daveDebugUpload = 0x800; /* debug PDU loading program blocks from PLC */
public static readonly int daveDebugMPI = 0x1000;
public static readonly int daveDebugPrintErrors = 0x2000; /* Print error messages */
public static readonly int daveDebugPassive = 0x4000;
public static readonly int daveDebugErrorReporting = 0x8000;
public static readonly int daveDebugOpen = 0x10000;
public static readonly int daveDebugAll = 0x1ffff;
/*
set and read debug level:
*/
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
public static extern void daveSetDebug(int newDebugLevel);
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
public static extern int daveGetDebug();
public static int daveMPIReachable = 0x30;
public static int daveMPIunused = 0x10;
public static int davePartnerListSize = 126;
/*
This wrapper class is used to avoid dealing with "unsafe" pointers to libnodave
internal structures. More wrapper classes are derived from this for the different
structures. Constructors of derived classes will call functions in libnodave that
allocate internal structures via malloc. The functions used return integers by
declaration. These integers are stored in "pointer" In fact, these integers contain
the "bit patterns" of the pointers. The compiler is deceived about the real nature of
the return values. This is ok as long as the pointers are only used in libnodave,
because libnodave routines are assumed to know what they may do with them.
The destructor here passes the pointers back to libnodave's daveFree to release memory
when the C# object is destructed.
*/
public class pseudoPointer {
public IntPtr pointer;
[DllImport("libnodave.dll"/*, PreserveSig=false*/)]
protected static extern int daveFree(IntPtr p);
~pseudoPointer(){
// Console.WriteLine("~pseudoPointer()"+pointer);
daveFree(pointer);
}
}
public class daveInterface: pseudoPointer {
// [DllImport("libnodave.dll"//, PreserveSig=false)]
/*
I cannot say why, but when I recompiled the existing code with latest libnodave.dll
(after using stdcall so that VC++ producs these "decorated names", I got a runtime
error about not finding daveNewInterface. When I state full name entry point explicitly,
(like below) it runs. The most strange thing is that all other functions work well...
*/
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
static extern IntPtr daveNewInterface(
daveOSserialType fd,
string name,
int localMPI,
int useProto,
int speed
);
public daveInterface(daveOSserialType fd,
string name,
int localMPI,
int useProto,
int speed) {
pointer=daveNewInterface(fd, name, localMPI, useProto, speed);
}
/*
This was just here to check inheritance
~daveInterface(){
Console.WriteLine("destructor("+daveGetName(pointer)+")");
Console.WriteLine("~daveInterface()"+pointer);
daveFree(pointer);
}
*/
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveInitAdapter(IntPtr di);
public int initAdapter() {
return daveInitAdapter(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveListReachablePartners(IntPtr di, byte[] buffer);
public int listReachablePartners(byte[] buffer) {
return daveListReachablePartners(pointer,buffer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern void daveSetTimeout(IntPtr di, int time);
public void setTimeout(int time) {
daveSetTimeout(pointer, time);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetTimeout(IntPtr di);
public int getTimeout() {
return daveGetTimeout(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern IntPtr daveDisconnectAdapter(IntPtr di);
public IntPtr disconnectAdapter() {
return daveDisconnectAdapter(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern string daveGetName(IntPtr di);
public string getName() {
return daveGetName(pointer);
}
}
public class daveConnection:pseudoPointer {
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern IntPtr daveNewConnection(
IntPtr di,
int MPI,
int rack,
int slot
);
public daveConnection(
daveInterface di,
int MPI,
int rack,
int slot
) {
pointer=daveNewConnection(
di.pointer, MPI, rack, slot
);
}
/* This wa here to test inheritance
~daveConnection(){
Console.WriteLine("~daveConnection()"+pointer);
daveFree(pointer);
daveFree(pointer);
}
*/
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveConnectPLC(IntPtr dc);
public int connectPLC(){
return daveConnectPLC(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveDisconnectPLC(IntPtr dc);
public int disconnectPLC() {
return daveDisconnectPLC(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveReadBytes(IntPtr dc, int area, int DBnumber, int start, int len, byte[] buffer);
public int readBytes(int area, int DBnumber, int start, int len, byte[] buffer) {
return daveReadBytes(pointer, area, DBnumber, start, len, buffer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveReadManyBytes(IntPtr dc, int area, int DBnumber, int start, int len, byte[] buffer);
public int readManyBytes(int area, int DBnumber, int start, int len, byte[] buffer) {
return daveReadManyBytes(pointer, area, DBnumber, start, len, buffer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveReadBits(IntPtr dc, int area, int DBnumber, int start, int len, byte[] buffer);
public int readBits(int area, int DBnumber, int start, int len, byte[] buffer) {
return daveReadBits(pointer, area, DBnumber, start, len, buffer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveWriteBytes(IntPtr dc, int area, int DBnumber, int start, int len, byte[] buffer);
public int writeBytes(int area, int DBnumber, int start, int len, byte[] buffer) {
return daveWriteBytes(pointer, area, DBnumber, start, len, buffer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveWriteManyBytes(IntPtr dc, int area, int DBnumber, int start, int len, byte[] buffer);
public int writeManyBytes(int area, int DBnumber, int start, int len, byte[] buffer) {
return daveWriteManyBytes(pointer, area, DBnumber, start, len, buffer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveWriteBits(IntPtr dc, int area, int DBnumber, int start, int len, byte[] buffer);
public int writeBits(int area, int DBnumber, int start, int len, byte[] buffer) {
return daveWriteBits(pointer, area, DBnumber, start, len, buffer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetS32(IntPtr dc);
public int getS32() {
return daveGetS32(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetU32(IntPtr dc);
public int getU32() {
return daveGetU32(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetS16(IntPtr dc);
public int getS16() {
return daveGetS16(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetU16(IntPtr dc);
public int getU16() {
return daveGetU16(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetS8(IntPtr dc);
public int getS8() {
return daveGetS8(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetU8(IntPtr dc);
public int getU8() {
return daveGetU8(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern float daveGetFloat(IntPtr dc);
public float getFloat() {
return daveGetFloat(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetCounterValue(IntPtr dc);
public int getCounterValue() {
return daveGetCounterValue(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern float daveGetSeconds(IntPtr dc);
public float getSeconds() {
return daveGetSeconds(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetS32At(IntPtr dc,int pos);
public int getS32At(int pos) {
return daveGetS32At(pointer, pos);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetU32At(IntPtr dc, int pos);
public int getU32At(int pos) {
return daveGetU32At(pointer, pos);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetS16At(IntPtr dc, int pos);
public int getS16At(int pos) {
return daveGetS16At(pointer, pos);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetU16At(IntPtr dc, int pos);
public int getU16At(int pos) {
return daveGetU16At(pointer, pos);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetS8At(IntPtr dc, int pos);
public int getS8At(int pos) {
return daveGetS8At(pointer, pos);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetU8At(IntPtr dc, int pos);
public int getU8At(int pos) {
return daveGetU8At(pointer, pos);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern float daveGetFloatAt(IntPtr dc, int pos);
public float getFloatAt(int pos) {
return daveGetFloatAt(pointer, pos);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetCounterValueAt(IntPtr dc, int pos);
public int getCounterValueAt(int pos) {
return daveGetCounterValueAt(pointer, pos);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern float daveGetSecondsAt(IntPtr dc, int pos);
public float getSecondsAt(int pos) {
return daveGetSecondsAt(pointer, pos);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetAnswLen(IntPtr dc);
public int getAnswLen() {
return daveGetAnswLen(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetMaxPDULen(IntPtr dc);
public int getMaxPDULen() {
return daveGetMaxPDULen(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int davePrepareReadRequest(IntPtr dc, IntPtr p);
public PDU prepareReadRequest() {
PDU p=new PDU();
davePrepareReadRequest(pointer, p.pointer);
return p;
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int davePrepareWriteRequest(IntPtr dc, IntPtr p);
public PDU prepareWriteRequest() {
PDU p=new PDU();
davePrepareWriteRequest(pointer, p.pointer);
return p;
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveExecReadRequest(IntPtr dc, IntPtr p, IntPtr rl);
public int execReadRequest(PDU p, resultSet rl) {
return daveExecReadRequest(pointer, p.pointer, rl.pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveExecWriteRequest(IntPtr dc, IntPtr p, IntPtr rl);
public int execWriteRequest(PDU p, resultSet rl) {
return daveExecWriteRequest(pointer, p.pointer, rl.pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveUseResult(IntPtr dc, IntPtr rs, int number);
public int useResult(resultSet rs, int number) {
return daveUseResult(pointer, rs.pointer, number);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveReadSZL(IntPtr dc,int id,int index,byte[] ddd, int len);
public int readSZL(int id,int index,byte[] ddd, int len) {
return daveReadSZL(pointer,id,index, ddd, len);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveStart(IntPtr dc);
public int start() {
return daveStart(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveStop(IntPtr dc);
public int stop() {
return daveStop(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveForce200(IntPtr dc, int area, int start, int val);
public int force200(int area, int start, int val) {
return daveForce200(pointer, area, start, val);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveForceDisconnectIBH(IntPtr dc, int src, int dest, int MPI);
public int forceDisconnectIBH(int src, int dest, int MPI) {
return daveForceDisconnectIBH(pointer, src, dest, MPI);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetResponse(IntPtr dc);
public int getGetResponse() {
return daveGetResponse(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveSendMessage(IntPtr dc, IntPtr p);
public int getMessage(PDU p) {
return daveSendMessage(pointer, p.pointer);
}
[DllImport("libnodave.dll")]
protected static extern int daveGetProgramBlock(IntPtr dc, int blockType, int number, byte[] buffer, ref int length);
public int getProgramBlock(int blockType, int number, byte[] buffer, ref int length) {
Console.WriteLine("length:"+length);
int a=daveGetProgramBlock(pointer, blockType, number, buffer, ref length);
Console.WriteLine("length:"+length);
return a;
}
[DllImport("libnodave.dll")]
protected static extern int daveListBlocksOfType(IntPtr dc, int blockType, byte[] buffer);
public int ListBlocksOfType(int blockType, byte[] buffer) {
return daveListBlocksOfType(pointer, blockType, buffer);
// return -1;
}
}
public class PDU:pseudoPointer {
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern IntPtr daveNewPDU();
public PDU() {
pointer=daveNewPDU();
}
/* ~PDU(){
Console.WriteLine("~PDU()");
daveFree(pointer);
}
*/
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern void daveAddVarToReadRequest(IntPtr p, int area, int DBnum, int start, int bytes);
public void addVarToReadRequest(int area, int DBnum, int start, int bytes) {
daveAddVarToReadRequest(pointer, area, DBnum, start, bytes);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern void daveAddBitVarToReadRequest(IntPtr p, int area, int DBnum, int start, int bytes);
public void addBitVarToReadRequest(int area, int DBnum, int start, int bytes) {
daveAddBitVarToReadRequest(pointer, area, DBnum, start, bytes);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern void daveAddVarToWriteRequest(IntPtr p, int area, int DBnum, int start, int bytes, byte[] buffer);
public void addVarToWriteRequest(int area, int DBnum, int start, int bytes, byte[] buffer) {
daveAddVarToWriteRequest(pointer, area, DBnum, start, bytes, buffer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern void daveAddBitVarToWriteRequest(IntPtr p, int area, int DBnum, int start, int bytes, byte[] buffer);
public void addBitVarToWriteRequest(int area, int DBnum, int start, int bytes, byte[] buffer) {
daveAddBitVarToWriteRequest(pointer, area, DBnum, start, bytes, buffer);
}
} // class PDU
public class resultSet:pseudoPointer {
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern IntPtr daveNewResultSet();
public resultSet() {
pointer=daveNewResultSet();
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern void daveFreeResults(IntPtr rs);
~resultSet(){
// Console.WriteLine("~resultSet(1)");
daveFreeResults(pointer);
// Console.WriteLine("~resultSet(2)");
// daveFree(pointer);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
protected static extern int daveGetErrorOfResult(IntPtr rs, int number);
public int getErrorOfResult(int number) {
return daveGetErrorOfResult(pointer, number);
}
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
public static extern int setPort(
[MarshalAs(UnmanagedType.LPStr)] string portName,
[MarshalAs(UnmanagedType.LPStr)] string baud,
int parity
);
[DllImport("libnodave.dll" /*, PreserveSig=false */ )]
public static extern int openSocket(
int port,
[MarshalAs(UnmanagedType.LPStr)] string portName
);
[DllImport("libnodave.dll" /*, PreserveSig=false */ )]
public static extern int openS7online(
[MarshalAs(UnmanagedType.LPStr)] string portName
);
[DllImport("libnodave.dll" /*, PreserveSig=false */ )]
public static extern int closePort(
int port
);
[DllImport("libnodave.dll" /*, PreserveSig=false */ )]
public static extern int closeSocket(
int port
);
[DllImport("libnodave.dll" )]
public static extern int closeS7online(
int port
);
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
public static extern float toPLCfloat(float f);
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
public static extern int daveToPLCfloat(float f);
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
public static extern int daveSwapIed_32(int i);
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
public static extern int daveSwapIed_16(int i);
public static int getS16from(byte[] b, int pos) {
if (BitConverter.IsLittleEndian) {
byte[] b1=new byte[2];
b1[1]=b[pos+0];
b1[0]=b[pos+1];
return BitConverter.ToInt16(b1, 0);
}
else
return BitConverter.ToInt16(b, pos);
}
public static int getU16from(byte[] b, int pos) {
if (BitConverter.IsLittleEndian) {
byte[] b1=new byte[2];
b1[1]=b[pos+0];
b1[0]=b[pos+1];
return BitConverter.ToUInt16(b1, 0);
}
else
return BitConverter.ToUInt16(b, pos);
}
public static int getS32from(byte[] b, int pos) {
if (BitConverter.IsLittleEndian) {
byte[] b1=new byte[4];
b1[3]=b[pos];
b1[2]=b[pos+1];
b1[1]=b[pos+2];
b1[0]=b[pos+3];
return BitConverter.ToInt32(b1, 0);
}
else
return BitConverter.ToInt32(b, pos);
}
public static uint getU32from(byte[] b, int pos) {
if (BitConverter.IsLittleEndian) {
byte[] b1=new byte[4];
b1[3]=b[pos];
b1[2]=b[pos+1];
b1[1]=b[pos+2];
b1[0]=b[pos+3];
return BitConverter.ToUInt32(b1, 0);
}
else
return BitConverter.ToUInt32(b, pos);
}
public static float getFloatfrom(byte[] b, int pos) {
if (BitConverter.IsLittleEndian) {
byte[] b1=new byte[4];
b1[3]=b[pos];
b1[2]=b[pos+1];
b1[1]=b[pos+2];
b1[0]=b[pos+3];
return BitConverter.ToSingle(b1, 0);
}
else
return BitConverter.ToSingle(b, pos);
}
[DllImport("libnodave.dll"/*, PreserveSig=false */ )]
private static extern int daveAreaName(int area);
[DllImport("libnodave.dll")]
private static extern int daveBlockName(int blockType);
[DllImport("libnodave.dll")]
private static extern void daveStringCopy(int i, byte[] c);
public static string blockName(int blockType) {
byte[] s=new byte[255];
int i=daveBlockName(blockType);
daveStringCopy(i, s);
string st="";
i=0;
while (s[i]!=0) {
st=st+(char)s[i];
i++;
}
return st;
}
public static string areaName(int blockType) {
byte[] s=new byte[255];
int i=daveAreaName(blockType);
daveStringCopy(i, s);
string st="";
i=0;
while (s[i]!=0) {
st=st+(char)s[i];
i++;
}
return st;
}
}
/* Changes:
03/30/2006 bug fix: removed double calls to daveFree in nested destructors.
11/21/2006 bug fix: applied patches from Johann Gail.
Version 0.8.4.5
07/10/09 Added closeSocket()
*/

BIN
Dot.NET/libnodave.net.dll Executable file

Binary file not shown.

2
Dot.NET/mono.bat Executable file
View File

@@ -0,0 +1,2 @@
bin\mcs -t:library libnodave.net.cs

7
Dot.NET/mono.sh Executable file
View File

@@ -0,0 +1,7 @@
mcs /t:library libnodave.net.cs
mcs /r:libnodave.net.dll CS/testMPI.cs
mcs /r:libnodave.net.dll CS/testS7online.cs
mcs /r:libnodave.net.dll CS/simpleMPI.cs
mcs /r:libnodave.net.dll CS/simplePPI.cs
mcs /r:libnodave.net.dll CS/simpleISO_TCP.cs
mbas /r:./libnodave.net.dll VB/simpleMPI2.vb

189
FAQ.de.txt Normal file
View File

@@ -0,0 +1,189 @@
Fragen, die mir schon oefter gestellt wurden und die Antworten:
Q: Meine VB,.NET oder Delphi-Anwendung funktioniert nicht. Ich vermute, Libnodave funktioniert
nicht mit meiner Hardware oder funktioniert gar nicht. Kannst du mir helfen?
A: Du kannst ganz einfach herausfinden, ob Libnodave mit deiner Hardware, SPS, Adapter
usw. arbeitet: Probierer es mit den vorkompilierten Testprogrammen. Wenn die nicht funktionieren
(auch die vorgeschlagenen Optionen probieren, wenn welche vorgeschlagen werden), ist es ganz
klar ein Fehler in Libnodave.
Wenn sie funktionieren, liegt es an deiner Anwendung (oder, selten, koennte das Problem in
der Schnittstelle zu der verwendeten Programmiersprache liegen).
Q: Kannst du mir helfen?
A: Bevor du fragst, probiere die Testprogramme wie oben beschrieben. Wenn die bei dir nicht
funktionieren, schicke mir die Ausgabe mit der Debug-Option, z.B.:
testMPI -d COM1 >debugout.txt
Wenn sie funktionieren, deine Anwendung aber nicht, probier mal:
Fuege die Zeile "daveSetDebug(daveDebugAll)" vor allen anderen Aufrufen von Libnodave in
deinen Code ein.
Dann versuche:
deineAnwendung >debugout.txt
Fuer die Leute, die mit Maeusen gross wurden und die Kommandozeile nicht mehr kennen:
Das funktioniert sogar mit MS-Excel. Erzeuge eine neue Tabelle, importiere das VBA_Modul,
- nimm das "rem" weg vor "call daveSetDebug(daveDebugAll)" am Anfang von sub initialize,
- Speichere das Arbeitsblatt.
- Starte dann von der Kommandozeile (Dos-Box, Ausfuehren/cmd):
excel testsheet >debugout.txt
Alle Debug-Ausgaben gelangen in die Datei debugout.txt.
Wenn meine Zeit knapp wird, schmeisse ich Mails ohne Debug-Ausgabe weg!
Q: Welches E-mail Format bevorzugst du?
A: Na ja, niemand hat danach gefragt, aber ich ziehe PUREN TEXT vor.
Q: Ich moechte auslesen, ob meine CPU in RUN oder STOP ist. Gibt es dafuer eine Funktion?
A: Nein, keine spezialisierte. Verwende bei S7-300/400 daveReadSZL, um die SZL Systemzustandslisten
zu lesen. Step7 macht das auch so. Informationen ueber IDs und Indizes findest du in der Siemens
Dokumentation. Der Zustand aller CPU-LEDs ist in ID 25 (19hex), index 0.
Bei der S7-200 befindet sich diese Information irgendwo in den Systemdaten.
Q: Ich versuche, Libnodave mit einem Debugger nachzuvollziehen. Ich brauche Hilfe?
A: Es gibt kaum einen Grund, Libnodave mit einem Debugger zu untersuchen, ausser du vermutest eine
der fogenden Sachen:
- Speicherprobleme (memory leaks)
- Bereichsueberschreitungen (range overflows) bei Zahlen oder Array-Indizes.
- Probleme bei der Uebergabe von Parametern an Bibliotheksfunktionen
Im letzteren Fall uebersetze besser Libnodave neu, nachdem du das Kommentarzeichen vor
#define DEBUG_CALLS in nodave.c entfernt hast.
Um Probleme mit SPS und Adaptern zu finden, ist die Debug-Ausgabe der weit bessere Weg.
Sie zeigt dir alles, was von und zu SPS/Adapter gesendet und empfangen wird und sie
zeigt dir das, was wichtig ist, anstatt es aus Speicher und Registern herauszufinden.
Q: Kannst du mir eine Dokumentation ueber die S7-Kommunikation oder das MPI-Protokoll geben?
A: Nein, kann ich nicht. Was ich darueber weiss, stammt aus "reverse engineering". Das
bedeutet, eine Menge Pakete mitzuschreiben, versuchen, einen Sinn hineinzuinterpretieren
und sie mit eigenem Code nachzubilden. Wenn in Libnodave Dinge Namen haben, so geben diese
meine gegenwaertige Hypothese wieder. Ich koennte versuchen, Dokumentation zu schreiben, aber
die wuerde immer einen Schritt hinter dem Code hinterherhinken: Der Code kann an der gegebenen
Hardware, also an der Realitaet, getestet werden, die Dokumentation nicht. Und der Code ist
recht gut dokumentiert...
Q: Also, warum gibt es diese komplizierten Strukturen (structs)?
A: Was kompliziert erscheint, sind wahrscheinlich zunaechst die Zeiger auf protokollspezifische
Funktionen. In Wahrheit machen sie die Bibliothek einfacher in zweierlei Hinsicht:
- Erstens trennen sie die Bildung der Pakete vom Transport. Wenn jemand herausfindet, wie man
mit der S7 etwas neues machen kann, reicht es, die Paketbildung zu implementieren und schon
wird es hoechstwahrscheinlich mit jedem Transportprotokoll funktionieren. Wenn andererseits
ein neuer Transportmechanismus hinzukommt, werden wahrscheinlich alle Functionen damit
funktionieren, sobald der Transport an sich funktioniert.
Version 0.8 zeigt das klar anhand der Implementierung des Transports ueber s7online.dll.
- Kommerzielle Bibliotheken, die ich gesehen habe, haben meist getrennte Saetze von Funktionen
fuer die 200 und die 300/400 Familie. Libnodave hat das nicht. So musst du weniger ueber das
API der Bibliothek lernen waehrend deine Anwendung ohne Veraenderung des Codes mehr kann.
Q: Warum exportierst du alle diese Strukturen, wenn du sagst, man braucht sie fuer keinerlei
Anwendung?
A: Sie sind nicht ganz bedeutungslos. Es sind auch eine Menge Funktionen dabei, die
"Zwischenschritte" duechfhren un normalerweise fr den Endanwender von keinem Nutzen sind.
Dennoch werden sie aus der .dll exportiert.
- Das ist alles verfuegbar - im Sinne von Open Source - fueer diejenigen, die eigene
Experimente machen wollen. Es mag ja Moeglichkeiten gebn, an die ich nicht gedacht habe.
- Wenigstens einmal konnte ich einem Benutzer helfen, eine neue Funktion (daveForce200) zu
implementiern. Er mute nur ca. 20 Zeilen in sein Programm einfuegen, die zum groessten Teil
solche Zwischenschritte beim Zusammenstellen eines Pakets und zur Analyse der Antwort
aufriefen, wie z.B. _daveAddData.
Q: Warum gibts keine Funktionen wie readOutputs, readInputs, readData ?
A: Andere Bibliotheken haben ueblicherweise separate Functionen um Eingaenge, Ausgaenge,
Merker, Datenbausteine zu lesen. Libnodave hat das nicht. Schon wieder musst du weniger
ueber das API lernen.
Q: Warum gibt es keine Funktionen wie readBytes, readIntegers, readDWords, readFloat ?
A: Andere Bibliotheken haben oft separate Funktionen um Integers, Worte oder Fliesskomma-
zahlen zu lesen. Libnodave hat das nicht. Der Grund ist: Wenn du einen DB (oder einen
anderen Speicherbereich in der SPS) hast, der Daten verschiedener Types enthaelt, koenntest
du mit solchen Functionen nur Teile davon lesen. Oder du benutzt readBytes um den Speicher
als eine Reihe von Bytes zu lesen und bist bei der Umwandlung auf dich allein gestellt.
Libnodave liest generell den ganzen Block als Bytes und stellt dir dann Funktionen zur
Umwandlung von/in SPS-Datentypen und Byte-Anordnung zur Verfuegung.
Q: Ich versuche, meinen Quellcode zu kompilieren, der nodave.h einbindet. Warum erhalte ich:
"#error Fill in what you need for your OS or API" ?
A: Libnodave mu unter verschiedenen Betriebssystemen verschiedene Funktionen nutzen,
um Daten byteweise von seriellen Schnittstellen oder TCP/IP streams zu lesen.
Diese Funktionen sind betriebssystemspezifisch. Du mut spezifizieren fr welches BS du
kompilierst. Definiere BCCWIN fuer Windows und LINUX fuer Linux oder andere Unix-artige
BS. Ich benutze -DBCCWIN oder -DLINUX beim Aufruf der Compiler aus einem Makefile. Wenn du
eine IDE (integrierte Entwicklungsumgebung) nutzt, entnimm der Dokumentation der IDE wie das
dort geht. Wenn du es nicht herausfinden kannst, kannst du dir auch helfen indem du die
#defines in deinen Quellcode schreibst (was bedeutet, dass dein Quellcode nicht mehr
ohne Aenderungen auf ein anderes System portiert werden kann). Beispiel:
...
#define BCCWIN // if you work on windows
#include nodave.h // or nodavesimple.h, but NEVER both
#include setport.h // if your code works with serial connections
#include openSocket.h // if your code works with TCP/IP
...
Q: Ich versuche, libnodave aus dem Quellcode zu kompilieren. Es gibt Probleme.
A: - Du brauchst Libnodave nicht selbst zu kompilieren. Du wrdest das mit anderen DLLs (Windows)
oderr .sos (gemeinsam genutzte Bibliotheken unter Linux) auch nicht machen. Normalerweise
linkt man solche Bibliotheken dynamisch zum eigenen Code.
- Nichtsdestotrotz steht dir der Quellcode zur Verfuegunge und du moechtes ihn oder nur die
benoetigten Teile rekompilieren usw..
- Du solltest wissen, dass die S7 die Motorola- (big endian, hchstwertiges byte zuerst)
Byteanordnung (endianness) verwendet, waehrend Intel-Prozessoren Intel-Byteanordnung
(little endian, niederwertigstes Byte zuerst). Auf Intel- und aehnlichen Maschinen
muss Libnodave Mehbyte-Werte umsortieren. Dies geschieht mittels der Funktionen daveGetS2(),
daveGetU2(), daveGetFloat() usw.. Der Code zur Konvertierung wird nur mitkompiliert
wenn du DAVE_LITTLE_ENDIAN definierst. Ansonsten wird "big endian" vorausgesetzt.
- Beachte, dass ich die publizierten Versionen von Libnodave mit gcc (fuer Linux) und MSVC++
(fuer windows) kompiliere. Dazu verwende ich das Skript buildall um Linux und Windows
Release-Versionen und Testprogramme auf einem Linux-Rechner zu erzeugen. Buildall benutzt
seinerseits das make von Linux mit MAKEFILE.VC.WINE um den MSVC++-Compiler fuer die
Windows-Versionen aufzurufen. Es gibt auch ein MAKEFILE.VC das auf einem Windows-System
mittels nmake dasselbe leisten sollte, aber es koennte nicht auf dem neuesten Stand sein :-(
Wenn du Zweifel hast, welche Compileroptionen oder Quelldateien noetig sind, schau in
MAKEFILE.VC.WINE nach!
Q: Ich moechte Libnodave fuer ein anderes Betriebssystem kompilieren. Welche Anpassungen
muss ich vornehmen?
A: - Ich nehme mal an, dass dein Betriebssystem ein 32-Bit-System ist und der zugehoerige
Compiler int als 32-Bit-Zahl behandelt. Wenn nicht, wird es schwierig!
- Du brauchst funktionsgleichen Ersatz fuer setport.c oder setportw.c und openSocket.c oder
openSocketw.c
- nodave.h: Du musst ein struct _daveOSserialType definieren, dass folgende Elemente beinhaltet:
rfd: Ein Datentyp, der eine serielle Schnittstelle oder ein TCP socket bei einem Lesezugriff
indentifiziert.
wfd: Ein Datentyp, der eine serielle Schnittstelle oder ein TCP socket bei einem Schreibzugriff
indentifiziert.
- nodave.c: Du musst die Funktionen stdread() und stdwrite() implementieren, die Bytes
von/an eine serielle Schnittstelle in/aus einem Puffer lesen/schreiben.
Q: Gibt es eine Funktion um die Struktur eines Datenbausteins zu lesen? Variablen und ihre
Datentypen ?
A: Nein. Die Strukturinformation steht nur in Step7 und nur vor der Compilierung verfuegbar.
Sie wird nicht in die SPS selbst uebertragen. Es ist dasselbe, wenn du ein neues Projekt
anlegst und den Programmcode aus der SPS laedtst: Von den Datenbausteinen bleiben nur
Byte-Arrays.
Q: Ich habe Probleme, auf Eingaenge zu schreiben. Koennen Sie mir hefen?
A: Zunaechst gibt es zwei Typen von "Eingaengen": 1. Periphere Adressen. Das ist das, was Sie
mit daveP ansprechen, oder, in einen SPS-Programm, mittels L PEW <adresse>. Zugriffe auf
diese Adressen sind echte Schreib- oder Lesezugriffe auf die Hardware und es gibt keine
Moeglichkeit, die Hardware von Eingaengen zu schreiben.
2. Prozessabbild der Ein- und Ausgaenge. Das ist was Programmierer von Siemens-SPS
normalerweise als Ein- und Ausgaenge ansprechen, wie bei:
L EW <Adresse>
or
U E 2.5
Zugriffe auf diese Adressen sprechen NICHT die Hardware sondern einen speziellen Speicherbereich
an. Der Zustand der Eingaenge wird in jedem Zyklus in diesen Speicherbereich kopiert, bevor
das Anwenderprogramm ausgefhrt wird. Wenn Sie mittels daveInputs dorthin schreiben, werden die
Werte vor dem naechsten Zyklus ueberschrieben.
Ob Ihr SPS-Programm oder Ihre Libnodave-Anwendung die geschriebenen Werte "sehen" wird, ist
ein zeitlicher Wettlauf.
Q: Ist es moeglich, mehrere (bis zu 3) Bits gleichzeitig in die SPS zu schreiben?
Ich moechte "Read-modify-write"-Techniken vermeiden und das Schreiben so "atomar" wie moeglich
halten.
A: daveWriteBits() stellt eine "atomare" Schreibfunktion bereit, aber nur fr ein einzelnes Bit.
Der Parameter "length" bei daveWriteBits() muss 1 sein, andere Werte liefern Fehler bei den
CPUs die ich kenne. Warum also ist der Parameter vorhanden? Es gibt ein entsprechendes Feld
in der Nachricht, das in anderen Faellen die Zahl der Bits angibt.
Q: Kann das ("atomares" Schreiben auf 3 Bits) mit daveWriteBits() erreicht werden oder muss
ich mehrere Variablen mit einer PDU schreiben?
A: Es kann NICHT mit daveWriteBits() erreicht werden, jedenfalls mit den CPUs die ich kenne. Ob
das I know. Whether Schreiben mehrerer Variablen mit einer PDU atomar ist, weiss ich nicht.
Wenn Sie das SPS-Programm verndern koennen, denke ich das Beste waere, die 3 Bits in ein Byte
zu packen, dessen andere Bits nicht gebraucht werden.

122
MAKEFILE.VC.WINE Normal file
View File

@@ -0,0 +1,122 @@
#
# Makefile to build a Windows Version of LIBNODAVE using VC++
# from Microsoft under wine. The Makefile itself is executed by
# good old GNU make.
#
#
# Make the "alien" suffixes known:
#
.SUFFIXES:
.SUFFIXES: .obj .h .c .cpp
#
# type 'make' to build libnodave.dll and some statically linked test programs.
# type 'make dynamic' to make libnodave.dll and some dynamically linked test programs.
#
# The directories where the windows tools and libs are:
#
#VCPATH=e:\programme\microsoft visual C++ Toolkit 2003
#SDKPATH=e:\programme\microsoft platform sdk
VCPATH=c:/vc
SDKPATH=c:/psdk
INCPATH=$(VCPATH)/include
INCPATH2="$(SDKPATH)/include"
INCPATH3="$(SDKPATH)/include/crt"
CC=wine $(VCPATH)/bin/cl
LL=wine $(VCPATH)/bin/link
CFLAGS= -I$(INCPATH) -I$(INCPATH2) -I$(INCPATH3) -c -DBCCWIN -DDAVE_LITTLE_ENDIAN -TC
CPPFLAGS= -I$(INCPATH) -I$(INCPATH2) -I$(INCPATH3) -c -DBCCWIN -DDAVE_LITTLE_ENDIAN
LFLAGS = /LIBPATH:"$(VCPATH)\lib"
LLFLAGS = /LIBPATH:"$(VCPATH)\lib" /DEF:libnodave.DEF
L2FLAGS = /LIBPATH:"$(VCPATH)\lib" /DEF:s7onlinx.DEF
PROGRAMS=testMPI.exe testPPI.exe testAS511.exe \
testPPI_IBH.exe testPPI_IBHload.exe testPPIload.exe \
testMPIload.exe testISO_TCP.exe testISO_TCPload.exe testIBH.exe testMPI_IBHload.exe \
testS7online.exe testNLpro.exe
DYNAMIC_PROGRAMS=testMPId.exe testPPId.exe testISO_TCPd.exe testIBHd.exe testPPI_IBHd.exe
LIBRARIES=libnodave.dll
SERIAL_OBJECTS=nodave.obj setportw.obj
all: $(LIBRARIES) $(PROGRAMS)
dynamic: $(DYNAMIC_PROGRAMS)
testISO_TCP.exe: nodave.obj openSocketw.obj testISO_TCP.obj
$(LL) $(LFLAGS) testISO_TCP.obj openSocketw.obj nodave.obj "$(SDKPATH)\lib\ws2_32.lib" /OUT:$@
testMPI.exe: testMPI.obj $(SERIAL_OBJECTS)
$(LL) $(LFLAGS) $(SERIAL_OBJECTS) testMPI.obj "$(SDKPATH)\lib\ws2_32.lib" /OUT:$@
testIBH.exe: openSocketw.obj nodave.obj testIBH.obj
$(LL) $(LFLAGS) testIBH.obj openSocketw.obj nodave.obj "$(SDKPATH)\lib\ws2_32.lib" /OUT:$@
testIBHd.exe: libnodave.dll testIBH.obj
$(LL) $(LFLAGS) testIBH.obj libnodave.lib /OUT:$@
testPPI_IBH.exe: openSocketw.obj nodave.obj testPPI_IBH.obj
$(LL) $(LFLAGS) openSocketw.obj nodave.obj testPPI_IBH.obj "C:\\psdk\\lib\\ws2_32.lib" /OUT:$@
testPPI_IBHd.exe: libnodave.dll testPPI_IBH.obj
$(LL) $(LFLAGS) testPPI_IBH.obj libnodave.lib /OUT:$@
testMPId.exe: libnodave.dll testMPI.obj
$(LL) $(LFLAGS) testMPI.obj libnodave.lib /OUT:$@
testPPI.exe: testPPI.obj nodave.obj setportw.obj
$(LL) $(LFLAGS) setportw.obj nodave.obj testPPI.obj "C:\\psdk\\lib\\ws2_32.lib" /out:$@
testPPId.exe: libnodave.dll testPPI.obj
$(LL) $(LFLAGS) testPPI.obj libnodave.lib /OUT:$@
testISO_TCPd.exe: libnodave.dll testISO_TCP.obj
$(LL) $(LFLAGS) testISO_TCP.obj libnodave.lib /OUT:$@
testPPIload.exe: testPPIload.obj $(SERIAL_OBJECTS)
$(LL) $(LFLAGS) testPPIload.obj $(SERIAL_OBJECTS) "$(SDKPATH)\lib\ws2_32.lib" /OUT:$@
testMPIload.exe: testMPIload.obj $(SERIAL_OBJECTS)
$(LL) $(LFLAGS) testMPIload.obj $(SERIAL_OBJECTS) "$(SDKPATH)\lib\ws2_32.lib" /OUT:$@
testISO_TCPload.exe: nodave.obj openSocketw.obj testISO_TCPload.obj
$(LL) $(LFLAGS) testISO_TCPload.obj openSocketw.obj nodave.obj "C:\\psdk\\lib\\ws2_32.lib" /out:testISO_TCPload.exe
testMPI_IBHload.exe: nodave.obj openSocketw.obj testMPI_IBHload.obj
$(LL) $(LFLAGS) openSocketw.obj nodave.obj testMPI_IBHload.obj "C:\\psdk\\lib\\ws2_32.lib" /out:testMPI_IBHload.exe
testPPI_IBHload.exe: nodave.obj openSocketw.obj testPPI_IBHload.obj
$(LL) $(LFLAGS) openSocketw.obj nodave.obj testPPI_IBHload.obj "C:\\psdk\\lib\\ws2_32.lib" /out:testPPI_IBHload.exe
testAS511.exe: testAS511.obj nodave.obj setportw.obj
$(LL) $(LFLAGS) setportw.obj nodave.obj testAS511.obj "C:\\psdk\\lib\\ws2_32.lib" /out:$@
testNLpro.exe: testNLpro.obj nodave.obj openSocketw.obj
$(LL) $(LFLAGS) openSocketw.obj nodave.obj testNLpro.obj "C:\\psdk\\lib\\ws2_32.lib" /out:$@
libnodave.dll: nodave.obj setportw.obj openSocketw.obj openS7online.obj
$(LL) $(LLFLAGS) /DLL nodave.obj setportw.obj openSocketw.obj openS7online.obj "C:\\psdk\\lib\\ws2_32.lib" /OUT:libnodave.dll
testS7online.exe: testS7online.obj nodave.obj openS7online.obj
$(LL) $(LFLAGS) nodave.obj testS7online.obj openS7online.obj "C:\\psdk\\lib\\ws2_32.lib" "C:\\psdk\\lib\\User32.lib" /out:$@
#
# delete all but the sources:
#
clean:
rm -f *.tds
rm -f *.il?
rm -f *.lib
rm -f *.obj
rm -f *.dll
rm -f *.exe
rm -f *.map
rm -f *.exp
#
# The general rule to build create an .obj:
#
.c.obj:
$(CC) $(CFLAGS) $<
.cpp.obj:
$(CC) $(CPPFLAGS) $<
#
# Special rules for those objects that are part of the .dll:
#
nodave.obj: nodave.c nodave.h
$(CC) $(CFLAGS) -DDOEXPORT nodave.c
setportw.obj: setportw.c
$(CC) $(CFLAGS) -DDOEXPORT setportw.c
openSocketw.obj: openSocketw.c
$(CC) $(CFLAGS) -DDOEXPORT openSocketw.c
openS7online.obj: openS7online.c openS7online.h
$(CC) $(CFLAGS) -DDOEXPORT openS7online.c
s7fake.obj: s7fake.c
$(CC) $(CFLAGS) -DDOEXPORT s7fake.c
getHandle.obj: getHandle.c
$(CC) $(CFLAGS) -DDOEXPORT getHandle.c

86
MAKEFILE.WINE Normal file
View File

@@ -0,0 +1,86 @@
#
# Makefile to build a Windows Version of LIBNODAVE using the free command line tools
# from Borland under LINUX and WINE.
# For usage with WINE under Linux only.
# Whether YOU can use it may depend on your WINE setup.
# So don't blame me!
#
#
# type 'make' to build libnodave.dll and some statically linked test programs.
# type 'make dynamic' to make libnodave.dll and some dynamically linked test programs.
#
# The directory where the tools are:
#
#BCCPATH=../bin
BCCPATH=c:/BCC55/bin
INCPATH=c:/BCC55
LIBPATH=C:\BCC55\lib
CC=wine $(BCCPATH)/bcc32
LL=$(BCCPATH)/ilink32
CFLAGS= -I$(INCPATH)/include -c -DBCCWIN -DLITTLE_ENDIAN
#CFLAGS= -I..\include -c -DBCCWIN -DLITTLE_ENDIAN
PROGRAMS=testMPI.exe testPPI.exe \
testPPI_IBH.exe testPPI_IBHload.exe testPPIload.exe \
testMPIload.exe testISO_TCP.exe testISO_TCPload.exe testIBH.exe testMPI_IBHload.exe
DYNAMIC_PROGRAMS=testMPId.exe testPPId.exe testISO_TCPd.exe testIBHd.exe testPPI_IBHd.exe
LIBRARIES=libnodave.dll
all: $(PROGRAMS) $(LIBRARIES)
dynamic: $(DYNAMIC_PROGRAMS)
testISO_TCP.exe: nodave.obj openSocketw.obj testISO_TCP.obj
$(LL) /r /Tpe /L$(LIBPATH) $(LIBPATH)\c0x32 testISO_TCP.obj openSocketw.obj nodave.obj, testISO_TCP.exe,,CW32 IMPORT32
testMPI.exe: setportw.obj nodave.obj testMPI.obj
$(LL) /r /Tpe /L$(LIBPATH) c0x32 setportw.obj nodave.obj testMPI.obj, testMPI.exe,,CW32 IMPORT32 ws2_32
testIBH.exe: openSocketw.obj nodave.obj testIBH.obj
$(LL) /r /Tpe /L$(LIBPATH) ..\lib\c0x32 openSocketw.obj nodave.obj testIBH.obj, testIBH.exe,,CW32 IMPORT32 ws2_32
testIBHd.exe: libnodave.dll testIBH.obj
$(LL) /r /Tpe /L$(LIBPATH) ..\lib\c0x32 testIBH.obj, testIBHd.exe,,CW32 IMPORT32 ws2_32 libnodave.lib
testPPI_IBH.exe: openSocketw.obj nodave.obj testPPI_IBH.obj
$(LL) /r /Tpe /L$(LIBPATH) ..\lib\c0x32 openSocketw.obj nodave.obj testPPI_IBH.obj, testPPI_IBH.exe,,CW32 IMPORT32 ws2_32
testPPI_IBHd.exe: libnodave.dll testPPI_IBH.obj
$(LL) /r /Tpe /L$(LIBPATH) ..\lib\c0x32 testPPI_IBH.obj, testPPI_IBHd.exe,,CW32 IMPORT32 ws2_32 libnodave.lib
testMPId.exe: libnodave.dll testMPI.obj
$(LL) /r /Tpe /L$(LIBPATH) ..\lib\c0x32 testMPI.obj , testMPId.exe,,CW32 IMPORT32 ws2_32 libnodave.lib
testPPI.exe: testPPI.obj nodave.obj setportw.obj
$(LL) /r /Tpe /L$(LIBPATH) ..\lib\c0x32 setportw.obj nodave.obj testPPI.obj, testPPI.exe,,CW32 IMPORT32
testPPId.exe: libnodave.dll testPPI.obj
$(LL) /r /Tpe /L$(LIBPATH) ..\lib\c0x32 testPPI.obj, testPPId.exe,,libnodave CW32 IMPORT32 ws2_32
testISO_TCPd.exe: libnodave.dll testISO_TCP.obj
$(LL) /r /Tpe /L$(LIBPATH) ..\lib\c0x32 testISO_TCP.obj, testISO_TCPd.exe,,CW32 IMPORT32 ws2_32 libnodave.lib
testPPIload.exe: nodave.obj setportw.obj testPPIload.obj
$(LL) /r /Tpe /L$(LIBPATH) ..\lib\c0x32 setportw.obj nodave.obj testPPIload.obj, testPPIload.exe,,CW32 IMPORT32
testMPIload.exe: nodave.obj setportw.obj testMPIload.obj
$(LL) /r /Tpe /L$(LIBPATH) ..\lib\c0x32 setportw.obj nodave.obj testMPIload.obj, testMPIload.exe,,CW32 IMPORT32
testISO_TCPload.exe: nodave.obj openSocketw.obj testISO_TCPload.obj
$(LL) /r /Tpe /L$(LIBPATH) ..\lib\c0x32 openSocketw.obj nodave.obj testISO_TCPload.obj, testISO_TCPload.exe,,CW32 IMPORT32
testMPI_IBHload.exe: nodave.obj openSocketw.obj testMPI_IBHload.obj
$(LL) /r /Tpe /L$(LIBPATH) ..\lib\c0x32 openSocketw.obj nodave.obj testMPI_IBHload.obj, testMPI_IBHload.exe,,CW32 IMPORT32
testPPI_IBHload.exe: nodave.obj openSocketw.obj testPPI_IBHload.obj
$(LL) /r /Tpe /L$(LIBPATH) ..\lib\c0x32 openSocketw.obj nodave.obj testPPI_IBHload.obj, testPPI_IBHload.exe,,CW32 IMPORT32
libnodave.dll: nodave.obj setportw.obj openSocketw.obj
$(LL) /v /C /Gn /w /Gk /r /Tpd /L$(LIBPATH) ..\lib\c0d32 nodave setportw openSocketw , libnodave.dll,,IMPORT32 CW32
$(BCCPATH)/implib libnodave.lib libnodave.dll
#
# delete all but the sources:
#
clean:
del *.tds
del *.il?
del *.lib
del *.obj
del *.dll

141
Makefile Normal file
View File

@@ -0,0 +1,141 @@
#
# removed testUSB from programs. Use "make usb" to make it.
# removed definitions for CYGWIN. There were no more differences versus LINUX
#
# These were the paths and names of the ARM cross compiling tools I tried:
# The -Bstatic option was necessary to get statically linked files needed for ARM-simulator
#
# CC=/usr/local/arm/2.95.3/bin/arm-linux-gcc
# LD=/usr/local/arm/2.95.3/bin/arm-linux-ld -Bstatic
# LDFLAGS= -Wl,-Bstatic
##LD=ld
#
# To test with g++ which does stricter type checking:
#
##CC=g++
CFLAGS=-Wall -Winline -DLINUX -fPIC -DDAVE_LITTLE_ENDIAN
CTFLAGS=-Wall -Winline -fPID -fPIC -DLINUX -DDAVE_LITTLE_ENDIAN
CPPFLAGS=-fno-stack-protector -nostdlib -Wall -Winline -DLINUX -fPIC -DDAVE_LITTLE_ENDIAN
#
# The following is needed to enable workarounds for statements that do
# not work on (some?) ARM processors:
# It also helped on some machine running HP-UX.
#
#CFLAGS+=-DARM_FIX
#-static -Wl,static -lc.a -static -lpthread.a -nostdlib
#CFLAGS=-O0 -Wall -Winline
PROGRAMS=testIBH testISO_TCP testMPI testPPI \
testPPIload testMPIload ibhsim5 \
isotest4 \
testISO_TCPload testMPI_IBHload testPPI_IBHload testPPI_IBH \
testNLpro \
testAS511
# testISO2
# testMPImin
# testPPI_IBH
DYNAMIC_PROGRAMS=testMPId testPPId testISO_TCPd
LIBRARIES=libnodave.so
all: $(PROGRAMS) $(LIBRARIES)
install: libnodave.so
cp libnodave.so /usr/lib
cp nodave.h /usr/include
ldconfig
dynamic: $(DYNAMIC_PROGRAMS)
usb: testUSB
nodave.o: nodave.h log2.h
openSocket.o: openSocket.h nodave.h log2.h
testISO_TCP.o: benchmark.c nodavesimple.h
testPPI.o: benchmark.c nodavesimple.h
testPPIcpp.o: benchmark.c nodavesimple.h
testMPI.o: benchmark.c nodavesimple.h
testIBH.o: benchmark.c nodavesimple.h
testPPI_IBH.o: benchmark.c nodavesimple.h
testISO_TCPload.o: nodave.h
testPPIload.o: nodave.h
testMPIload.o: nodave.h
testMPI_IBHload.o: nodave.h
testPPI_IBHload.o: nodave.h
testNLpro.o: benchmark.c nodavesimple.h
testISO_TCP: nodave.o openSocket.o testISO_TCP.o
$(CC) $(LDFLAGS) nodave.o openSocket.o testISO_TCP.o -o testISO_TCP
testISO2: nodave.o openSocket.o testISO2.o
$(CC) $(LDFLAGS) nodave.o openSocket.o testISO2.o -o testISO2
testISO_TCPd: nodave.o openSocket.o testISO_TCP.o
$(CC) -lnodave testISO_TCP.o -o testISO_TCPd
testPPIload: nodave.o setport.o testPPIload.o
$(CC) $(LDFLAGS) nodave.o setport.o testPPIload.o -o testPPIload
testMPI: setport.o testMPI.o nodave.o
$(CC) $(LDFLAGS) setport.o nodave.o testMPI.o -o testMPI
testMPImin: setport.o testMPImin.o nodave.o
$(CC) $(LDFLAGS) setport.o nodave.o testMPImin.o -o testMPImin
testMPId: setport.o testMPI.o nodave.o
$(CC) -lnodave testMPI.o -o testMPId
testMPIload: nodave.o setport.o testMPIload.o
$(CC) $(LDFLAGS) nodave.o setport.o testMPIload.o -o testMPIload
testMPI_IBHload: nodave.o openSocket.o testMPI_IBHload.o
$(CC) $(LDFLAGS) nodave.o openSocket.o testMPI_IBHload.o -o testMPI_IBHload
testPPI: nodave.o setport.o testPPI.o
$(CC) $(LDFLAGS) nodave.o setport.o testPPI.o -o testPPI
testPPId: nodave.o setport.o testPPI.o
$(CC) -lnodave testPPI.o -o testPPId
testISO_TCPload: nodave.o openSocket.o testISO_TCPload.o
$(CC) $(LDFLAGS) nodave.o openSocket.o testISO_TCPload.o -o testISO_TCPload
testIBH: openSocket.o testIBH.o nodave.o
$(CC) $(LDFLAGS) openSocket.o nodave.o testIBH.o -o testIBH
testPPI_IBH: openSocket.o testPPI_IBH.o nodave.o
$(CC) $(LDFLAGS) openSocket.o nodave.o testPPI_IBH.o -o testPPI_IBH
testPPI_IBHload: openSocket.o testPPI_IBHload.o nodave.o
$(CC) $(LDFLAGS) openSocket.o nodave.o testPPI_IBHload.o -o testPPI_IBHload
testPPIcpp: nodave.o setport.o testPPIcpp.o
$(CC) $(LDFLAGS) nodave.o setport.o testPPIcpp.o -o testPPIcpp
testMPI2: setport.o testMPI2.o nodave.o nodaveext.o
$(CC) $(LDFLAGS) setport.o nodave.o nodaveext.o testMPI2.o -o testMPI2
testAS511: setport.o testAS511.o nodave.o
$(CC) $(LDFLAGS) setport.o nodave.o testAS511.o -o testAS511
testUSB: testUSB.o nodave.o usbGlue.o usbGlue.h
$(CC) $(LDFLAGS) nodave.o testUSB.o usbGlue.o -lusb -o testUSB
testNLpro: openSocket.o testNLpro.o nodave.o
$(CC) $(LDFLAGS) openSocket.o nodave.o testNLpro.o -o testNLpro
crc: crc.o
$(CC) $(LDFLAGS) crc.o -o crc
crc3: crc3.o
$(CC) $(LDFLAGS) crc3.o -o crc3
testHTTP: nodave.o openSocket.o testHTTP.o
$(CC) $(LDFLAGS) nodave.o openSocket.o testHTTP.o -o testHTTP
ibhsim9.o: simProperties2.c blocklist.h
ibhsim9: ibhsim9.o nodave.h nodave.o openSocket.o openSocket.h blocklist.o blocklist2.o setport.o
$(CC) -pthread ibhsim9.o openSocket.o nodave.o blocklist.o blocklist2.o setport.o -o ibhsim9
ibhsim10.o: simProperties2.c blocklist.h
ibhsim10: ibhsim10.o nodave.h nodave.o openSocket.o openSocket.h blocklist.o blocklist2.o setport.o emulator.o
$(CC) -lm -pthread ibhsim10.o openSocket.o nodave.o blocklist.o blocklist2.o setport.o emulator.o -o ibhsim10
libnodave.so: nodave.o setport.o openSocket.o
$(LD) -shared nodave.o setport.o openSocket.o -o libnodave.so
ibhsim5.o: simProperties.c
ibhsim5: ibhsim5.o nodave.h nodave.o openSocket.o openSocket.h
$(CC) -pthread ibhsim5.o openSocket.o nodave.o -o ibhsim5
isotest4: isotest4.o openSocket.o nodave.o nodave.h
$(CC) $(LDFLAGS) -pthread isotest4.o openSocket.o nodave.o $(LIB) -o isotest4
clean:
rm -f $(DYNAMIC_PROGRAMS)
rm -f $(PROGRAMS)
rm -f *.o
rm -f *.so
rm -f *~

BIN
Python/.DS_Store vendored Normal file

Binary file not shown.

BIN
Python/._.DS_Store Normal file

Binary file not shown.

53
Python/.svn/all-wcprops Normal file
View File

@@ -0,0 +1,53 @@
K 25
svn:wc:ra_dav:version-url
V 39
/pydave/!svn/ver/23/branches/galvano_KW
END
lamelle.py
K 25
svn:wc:ra_dav:version-url
V 50
/pydave/!svn/ver/21/branches/galvano_KW/lamelle.py
END
nodave.db3
K 25
svn:wc:ra_dav:version-url
V 50
/pydave/!svn/ver/20/branches/galvano_KW/nodave.db3
END
nodave.py
K 25
svn:wc:ra_dav:version-url
V 49
/pydave/!svn/ver/20/branches/galvano_KW/nodave.py
END
davedefs.py
K 25
svn:wc:ra_dav:version-url
V 51
/pydave/!svn/ver/21/branches/galvano_KW/davedefs.py
END
pydave.c
K 25
svn:wc:ra_dav:version-url
V 48
/pydave/!svn/ver/23/branches/galvano_KW/pydave.c
END
nodave.cfg
K 25
svn:wc:ra_dav:version-url
V 49
/pydave/!svn/ver/4/branches/galvano_KW/nodave.cfg
END
DB.py
K 25
svn:wc:ra_dav:version-url
V 44
/pydave/!svn/ver/4/branches/galvano_KW/DB.py
END
Makefile
K 25
svn:wc:ra_dav:version-url
V 47
/pydave/!svn/ver/4/branches/galvano_KW/Makefile
END

131
Python/.svn/entries Normal file
View File

@@ -0,0 +1,131 @@
8
dir
23
https://svn.ela-devel.de:8443/pydave/branches/galvano_KW
https://svn.ela-devel.de:8443/pydave
2010-05-17T09:43:42.974289Z
23
michi
svn:special svn:externals svn:needs-lock
bfb6778f-2709-48be-b937-866231387e0f
lamelle.py
file
2010-04-28T20:20:48.000000Z
e63e3cae1e178aa7fd4e6c640d7b001b
2010-04-28T18:04:29.221042Z
21
michi
has-props
nodave.db3
file
2009-08-21T09:58:25.000000Z
08a3a5d3f63d85e928ae9f1154f848c1
2010-04-28T15:49:12.805101Z
20
michi
has-props
nodave.py
file
2010-04-29T10:52:36.000000Z
05bb9a8dd369b4aaca0cde2c3d2b2116
2010-04-28T15:49:12.805101Z
20
michi
has-props
davedefs.py
file
2010-04-28T20:20:48.000000Z
cee85a6f1621af6127bead77008aaad5
2010-04-28T18:04:29.221042Z
21
michi
pydave.c
file
2010-05-17T09:44:36.000000Z
16bf58efce37b920ba57038eb714b137
2010-05-17T09:43:42.974289Z
23
michi
nodave.cfg
file
2009-08-12T13:40:58.000000Z
060d6bb8086126ba5091f5dd87a9ad32
2009-08-06T14:59:39.609803Z
4
michi
DB.py
file
2010-04-29T10:53:31.000000Z
3e7a097c1c18ffdfb14ceb5b369c0f56
2009-08-06T14:59:39.609803Z
4
michi
has-props
initdata
dir
Makefile
file
2009-08-12T13:40:58.000000Z
61fe59324770b5163b458fe0c0b21275
2009-08-06T14:59:39.609803Z
4
michi

1
Python/.svn/format Normal file
View File

@@ -0,0 +1 @@
8

View File

@@ -0,0 +1,5 @@
K 14
svn:executable
V 1
*
END

View File

@@ -0,0 +1,5 @@
K 14
svn:executable
V 1
*
END

View File

@@ -0,0 +1,5 @@
K 13
svn:mime-type
V 24
application/octet-stream
END

View File

@@ -0,0 +1,5 @@
K 14
svn:executable
V 1
*
END

View File

@@ -0,0 +1,168 @@
#!/usr/bin/python2.4
#!/opt/local/bin/python3.0
#import sqlite3 as sqlite
from pysqlite2 import dbapi2 as sqlite
from sys import exit
import datetime
def connectdb (dbfile):
"""
Create connection to database
return DB Object
user gets information if failure
"""
try:
db = sqlite.connect (dbfile, 15.0)
return db
except:
print ("Couldnt connct to DB")
exit (1)
class DB:
def __init__ (self, dbfile):
self.db = connectdb (dbfile)
def executesql (self, dbstring):
"""
exectutes sql in dbstring
and returns cursor
"""
try:
cursor = self.db.cursor ()
cursor.execute (dbstring)
except sqlite.OperationalError:
print ("SQL exectution failed. Caused by: " + dbstring)
raise Warning
except sqlite.IntegrityError:
print ("Double entry. Caused by: " + dbstring)
raise Warning
except sqlite.ProgrammingError:
print ("Invalid SQL: " + dbstring)
raise Warning
except sqlite.Warning:
pass
self.db.commit ()
return cursor
def getallclients (self):
"""
returns all available clients
"""
try:
cursor = self.executesql ("SELECT id, client, proto, address, mpi, rack, slot FROM clients;")
except:
print ("couldn't get clients")
raise Warning
return cursor.fetchall ()
def getalllocations (self):
"""
returns all locations
"""
try:
cursor = self.executesql ("SELECT * FROM locations")
except:
print ("couldn't get locations")
raise Warning
return cursor.fetchall ()
def getiopointsclient (self, client):
"""
returns all iopoints belong to client
"""
try:
cursor = self.executesql ("SELECT id, area, dbnr, typ, address, hyst, logcmd, onmsg, offmsg, dimension, caption FROM iopoints WHERE client ='%d';" \
%client)
except:
print ("couldn't get iopoints")
raise Warning
return cursor.fetchall ()
def getallmsglogs (self):
"""
returns all msglog entrys
"""
try:
cursor = self.executesql ("SELECT m.id, c.client, i.caption, ms.msg, m.timestamp FROM msglog m \
LEFT JOIN clients c ON (c.id = m.client) \
LEFT JOIN iopoints i ON (i.id = m.iopoint) \
LEFT JOIN messages ms ON (ms.id = m.msg)")
except:
print ("couldn't get msglog")
raise Warning
return cursor.fetchall ()
def getallvaluelogs (self):
"""
returns all valuelog entrys
"""
try:
cursor = self.executesql ("SELECT v.id, c.client, i.caption, v.value, i.dimension, v.timestamp FROM valuelog v \
LEFT JOIN clients c ON (c.id = v.client) \
LEFT JOIN iopoints i ON (i.id = v.iopoint) \
LEFT JOIN dimensions d ON (d.id = i.dimension)")
except:
print ("couldn't get valuelog entrys")
raise Warning
return cursor.fetchall ()
def logmsg (self, client, iopoint, msg):
"""
save logmsg
"""
try:
cursor = self.executesql ("INSERT INTO msglog VALUES (null, %d, %d, %d, current_timestamp)" %(client, iopoint, msg))
except:
print ("couldn't insert logmsg")
raise Warning
def logvalue (self, client, iopoint, value):
"""
saves a valuelog
"""
try:
cursor = self.executesql ("INSERT INTO valuelog VALUES (null, %d, %d, %d, current_timestamp)" %(client, iopoint, value))
except:
print ("couldn't insert valuelog")
raise Warning
if __name__ == "__main__":
import sys
conn = DB ("nodave.db3")
print ("Value Logs")
print ("-------------")
for i in conn.getallvaluelogs ():
print (i)
sys.exit(0)
print ("Msg Logs")
print ("-------------")
for i in conn.getallmsglogs ():
print (i)

View File

@@ -0,0 +1,10 @@
all: pydave.o pydave.so
pydave.o: pydave.c
gcc -g -DNDEBUG -O2 -falign-loops=16 -save-temps -funroll-loops -funroll-all-loops -fno-strict-aliasing -Wall -Wstrict-prototypes -fPIC -c pydave.c -o pydave.o
pydave.so: pydave.o
ld --shared pydave.o --shared ../nodave.o --shared ../openSocket.o -o pydave.so
clean:
rm *.so *.o

View File

@@ -0,0 +1,28 @@
__author__ = "Michael Rest"
__date__ = "1 June 2000"
__email__ = "michi@rosstein.de"
__version__ = "$Revision: 1.0 $"[11:-2]
daveAreas = {
"P" : 0x80, #daveP
"E" : 0x81, #daveInputs
"A" : 0x82, #daveFlags
"M" : 0x83, #daveDB Data Blocks
"DB": 0x84, #daveDI Instance Data Blocs
"DI": 0x85, #daveLocal !!! Untestet
"C" : 0x1c, #daveCounter
"T" : 0x1d #daveTimer
}
daveTypes = {
"BOOL" : 1,
"BYTE" : 1,
"WORD" : 2,
"INT" : 2,
"DWORD" : 4,
"REAL" : 4,
"DINT" : 4
}

View File

@@ -0,0 +1,195 @@
#!/usr/bin/python
__author__ = "Michael Rest"
__date__ = "1 June 2009"
__email__ = "michi@rosstein.de"
__version__ = "$Revision: 1.0 $"[11:-2]
from pydave import pydave
from davedefs import *
from ConfigParser import *
from datetime import datetime
from time import sleep
from select import select
import sys
import os
from DB import DB
#Enable Debug prints
DEBUG = 0
def log_fifo (client, ioid, value):
"""
Log IO Value in a file (fifo)
"""
fifo = os.open('nodave.fifo', os.O_RDWR)
#fifo = open ("nodave.fifo", "a")
rdcons, wrcons, ccons = select ([], [fifo], [], 0.1)
if fifo in wrcons:
dt = datetime.now ()
os.write (fifo, "%4d-%02d-%02d %02d:%02d:%02d.%06d; " %(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond))
os.write (fifo, "Client:%d; IOID:%d; Value:%d;\n" %(client, ioid, value))
os.close (fifo)
else:
print ("FIFO is not writable")
sys.exit (0)
if __name__ == '__main__':
dbcon = DB ("nodave.db3")
clients = []
for _id, _name, _proto, _addr, _port, _mpi, _rack, _slot in [[5,'DrCutter', 'IBH_Link', '192.168.100.77', 1099, 2, 0, 0]]:
print ("Found Client id %d '%s' with proto %s on address %s Port %d with MPI %d on Rack %d Slot %d" %(_id, _name, _proto, _addr, _port, _mpi, _rack, _slot))
if _proto in ["ISO_TCP", "IBH_Link"]:
a = pydave ()
a.OpenSocket (_proto, _addr, _port, _mpi, _rack, _slot)
clients.append ((_id, a, []))
else:
print ("Invalid Proto for Client")
sys.exit (0)
for _clientid, _davecon, _vars in clients:
#print "Client", _clientid, (_davecon.ReadBytes (daveAreas["M"], 0, 255, 1))
print "Client", (_davecon.ReadBytes (daveAreas["DB"], 400, 0, 4))
_iopoints = [] # dbcon.getiopointsclient (_clientid)
_vars.append ({ "ID" : "Auto",
"Typ" : "BOOL",
"Area" : "DB",
"DBNr" : 400,
"ByteAdd": 0,
"BitAdd": 0,
"Logcmd" : 1})
_vars.append ({ "ID" : "Error",
"Typ" : "BOOL",
"Area" : "DB",
"DBNr" : 400,
"ByteAdd": 0,
"BitAdd": 1,
"Logcmd" : 1})
_vars.append ({ "ID" : "Offline",
"Typ" : "BOOL",
"Area" : "DB",
"DBNr" : 400,
"ByteAdd": 0,
"BitAdd": 2,
"Logcmd" : 1})
_vars.append ({ "ID" : "Program",
"Typ" : "BYTE",
"Area" : "DB",
"DBNr" : 400,
"ByteAdd": 1,
"BitAdd": 0,
"Logcmd" : 1})
_vars.append ({ "ID" : "Fertigteile",
"Typ" : "DINT",
"Area" : "DB",
"DBNr" : 400,
"ByteAdd": 2,
"BitAdd": 0,
"Logcmd" : 1})
_vars.append ({ "ID" : "Nio Cam1",
"Typ" : "DINT",
"Area" : "DB",
"DBNr" : 400,
"ByteAdd": 6,
"BitAdd": 0,
"Logcmd" : 1})
_vars.append ({ "ID" : "io Robby",
"Typ" : "DINT",
"Area" : "DB",
"DBNr" : 400,
"ByteAdd": 10,
"BitAdd": 0,
"Logcmd" : 1})
_vars.append ({ "ID" : "nio Einpressstation",
"Typ" : "DINT",
"Area" : "DB",
"DBNr" : 400,
"ByteAdd": 14,
"BitAdd": 0,
"Logcmd" : 1})
_vars.append ({ "ID" : "nio Cam3",
"Typ" : "DINT",
"Area" : "DB",
"DBNr" : 400,
"ByteAdd": 18,
"BitAdd": 0,
"Logcmd" : 1})
_vars.append ({ "ID" : "druck Stift 1",
"Typ" : "REAL",
"Area" : "DB",
"DBNr" : 400,
"ByteAdd": 22,
"BitAdd": 0,
"Logcmd" : 1})
_vars.append ({ "ID" : "druck Stift 3",
"Typ" : "REAL",
"Area" : "DB",
"DBNr" : 400,
"ByteAdd": 26,
"BitAdd": 0,
"Logcmd" : 1})
while (1):
print "Start"
for _id, _davecon, _vars in clients:
if DEBUG:
print "Client", _id
_counter = 0
vari = 0
for _var in _vars:
if (_counter == 0):
_davecon.PrepareReadRequest ()
if (_var["Typ"] != "BOOL"):
_davecon.AddVarToReadRequest (daveAreas [_var['Area']], _var['DBNr'], _var['ByteAdd'] , daveTypes[_var['Typ']])
else:
_davecon.AddBitVarToReadRequest (daveAreas [_var['Area']], _var['DBNr'], _var['ByteAdd'] * 8 + _var["BitAdd"] , 1)
_counter += 1
if (_counter == 19) or (_var == _vars[-1]) :
#Block requests to 19 Vals
results = _davecon.ExecReadRequest ()
if not results:
break
if len (results) != _counter:
print ("Results other size than Variables")
sys.exit (0)
_counter = 0
for i in range (len (results)):
if DEBUG:
print " IO Point ", _vars[vari]
if (_vars [vari]["Logcmd"] & 1):
#Print Value
print " IO Point ", _vars[vari]['ID']
if _vars [vari]["Typ"] == 'REAL':
print " Value", _davecon.U32toREAL (results[i])
else:
print " Value", results[i]
# increment index of _vars
vari +=1
if DEBUG:
print "Vardata"
for i in _vars:
print i
sleep (4)

View File

@@ -0,0 +1,3 @@
[SPS1]
ip = 192.168.100.72

Binary file not shown.

View File

@@ -0,0 +1,192 @@
#!/usr/bin/python
__author__ = "Michael Rest"
__date__ = "1 June 2009"
__email__ = "michi@rosstein.de"
__version__ = "$Revision: 1.0 $"[11:-2]
from pydave import pydave
from davedefs import *
from ConfigParser import *
from datetime import datetime
from time import sleep
from select import select
import sys
import os
from DB import DB
#Enable Debug prints
DEBUG = 0
def log_fifo (client, ioid, value):
"""
Log IO Value in a file (fifo)
"""
fifo = os.open('nodave.fifo', os.O_RDWR)
#fifo = open ("nodave.fifo", "a")
rdcons, wrcons, ccons = select ([], [fifo], [], 0.1)
if fifo in wrcons:
dt = datetime.now ()
os.write (fifo, "%4d-%02d-%02d %02d:%02d:%02d.%06d; " %(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond))
os.write (fifo, "Client:%d; IOID:%d; Value:%d;\n" %(client, ioid, value))
os.close (fifo)
else:
print ("FIFO is not writable")
sys.exit (0)
if __name__ == '__main__':
dbcon = DB ("nodave.db3")
clients = []
for _id, _name, _proto, _addr, _mpi, _rack, _slot in dbcon.getallclients ():
print ("Found Client id %d '%s' with proto %s on address %s with MPI %d on Rack %d Slot %d" %(_id, _name, _proto, _addr, _mpi, _rack, _slot))
if _proto in ["ISO_TCP", "IBH_Link"]:
a = pydave ()
a.OpenSocket (_proto, _addr, 102, _mpi, _rack, _slot)
clients.append ((_id, a, []))
else:
print ("Invalid Proto for Client")
sys.exit (0)
for _clientid, _davecon, _vars in clients:
#print "Client", _id, (_davecon.ReadBytes (daveAreas["M"], 0, 255, 1))
#print "Client", (_davecon.ReadBytes (daveAreas["DB"], 10, 2, 4))
_iopoints = dbcon.getiopointsclient (_clientid)
for _ioid, _area, _dbnr, _typ, _address, _hyst, _logcmd, _onmsg, _offmsg, _dimension, _caption in _iopoints:
print _ioid, _area, _dbnr, _typ, _address, _hyst, _logcmd, _onmsg, _offmsg, _dimension, _caption
if _area not in daveAreas:
print ("Invalid Area %s of IO Id %d" %(_area, _ioid))
sys.exit (0)
if (_area == "DB") and not (_dbnr > 0):
print ("DB Nr of IO Id %d must be > 0" %_ioid)
sys.exit (0)
if _typ not in daveTypes:
print ("Var Type of Id %d invalid" %_ioid)
sys.exit (0)
_byteaddress = 0
_bitaddress = 0
if '.' in _address:
_address = _address.split ('.')
try:
_byteaddress = int (_address[0])
_bitaddress = int (_address[1])
except:
print ("Error in Byte/Bitaddress conversion of IO Id %d" %_ioid)
sys.exit (0)
if (_typ != "BOOL"):
print ("Error Bitadress used for nonBOOL Type in IO Id %d" %_ioid)
_bitaddress = 0
else:
try:
_byteaddress = int (_address)
except:
print ("Error in Byteaddress conversion of IO Id %d" %_ioid)
sys.exit (0)
if (_typ == "BOOL"):
print ("Byteadress for BOOL Variable profided in IO id %d" %_ioid)
sys.exit (0)
_vars.append ({ "ID" : _ioid,
"Typ" : _typ,
"Area" : _area,
"DBNr" : _dbnr,
"ByteAdd": _byteaddress,
"BitAdd": _bitaddress,
"Hysterese" : _hyst,
"Logcmd" : _logcmd,
"OnMsg" : _onmsg,
"OffMsg" : _offmsg,
"Dimension" : _dimension,
"Value" : -99999})
while (1):
for _id, _davecon, _vars in clients:
if DEBUG:
print "Client", _id
_counter = 0
vari = 0
for _var in _vars:
if (_counter == 0):
_davecon.PrepareReadRequest ()
if (_var["Typ"] != "BOOL"):
_davecon.AddVarToReadRequest (daveAreas [_var['Area']], _var['DBNr'], _var['ByteAdd'] , daveTypes[_var['Typ']])
else:
_davecon.AddBitVarToReadRequest (daveAreas [_var['Area']], _var['DBNr'], _var['ByteAdd'] * 8 + _var["BitAdd"] , 1)
_counter += 1
if (_counter == 19) or (_var == _vars[-1]) :
#Block requests to 19 Vals
results = _davecon.ExecReadRequest ()
if not results:
break
if len (results) != _counter:
print ("Results other size than Variables")
sys.exit (0)
_counter = 0
for i in range (len (results)):
if DEBUG:
print " IO Point ", _vars[vari]
if (_vars [vari]["Logcmd"] & 1):
#Print Value
p=1
#print " PrintlogClient", _id
#print " IO Point ", _vars[vari]
#print " New Value", results[i]
if (_vars [vari]["Logcmd"] & 2):
#Log Value to Fifo
log_fifo (_id, _vars [vari]["ID"], results[i])
# Just log to DB if Hysteresis was reached
if abs (_vars [vari]["Value"] - results[i]) > _vars [vari]["Hysterese"]:
if DEBUG:
print " reached Hysteresis"
if (_vars [vari]["Logcmd"] & 4):
#Log Message (intended for Bit Values)
if DEBUG:
print " MsgLog Cmd"
if results [i] == 0:
dbcon.logmsg (_id, _vars [vari]["ID"], _vars [vari]["OffMsg"])
else:
dbcon.logmsg (_id, _vars [vari]["ID"], _vars [vari]["OnMsg"])
if (_vars [vari]["Logcmd"] & 8):
#Log Value
if DEBUG:
print " ValueLog Cmd"
dbcon.logvalue (_id, _vars [vari]["ID"], results [i])
if (_vars [vari]["Logcmd"] & 16):
#Store value
if DEBUG:
print " Valuestore Cmd"
dbcon.setvalue (_id, _vars [vari]["ID"], results [i])
_vars [vari]["Value"] = results [i]
# increment index of _vars
vari +=1
if DEBUG:
print "Vardata"
for i in _vars:
print i
sleep (4)

View File

@@ -0,0 +1,542 @@
/*
* Python Wrapper for llibnodave
*
* NOTE: !!!! Always use Prepare after a Byte Read
*/
#define LINUX
#include <python2.5/Python.h>
#include <stdlib.h>
#include <stdio.h>
#include "../nodave.h"
#include "../nodavesimple.h"
#include "../openSocket.h"
//---------------------------------------------------------
// OpenSocket
// Open Socket connection
// "xxx.xxx.xxx.xxx" server - IP Address
//---------------------------------------------------------
static PyObject *
ex_OpenSocket (PyObject *self, PyObject *args)
{
PyObject *selfobj;
_daveOSserialType fds;
daveInterface *di;
daveConnection *dconn;
char *server;
char *proto;
int port, mpi, rack, slot, dproto, dspeed;
daveSetDebug (daveDebugPrintErrors);
//Extract Servername from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "Ossiiii", &selfobj, &proto, &server, &port, &mpi, &rack, &slot)))
return NULL;
Py_DECREF (args);
if (strcmp( proto, "ISO_TCP" ) == 0)
{
dproto = daveProtoISOTCP;
}
else if (strcmp( proto, "IBH_Link" ) == 0)
{
dproto = daveProtoMPI_IBH;
}
else
{
printf ("Invalid Proto.\n");
return PyInt_FromLong (3);
}
dspeed = daveSpeed187k;
fds.rfd = openSocket (port, server);
fds.wfd = fds.rfd;
if (fds.rfd > 0)
{
di = daveNewInterface (fds, "IF1", 0, dproto, dspeed);
if (0 == daveInitAdapter (di))
{}
daveSetTimeout (di, 5000000);
dconn = daveNewConnection (di, mpi, rack, slot); // insert your rack and slot here
PyObject_SetAttrString (selfobj, "daveconaddr", PyCObject_FromVoidPtr (dconn, NULL));
PyObject_SetAttrString (selfobj, "davesocket", PyCObject_FromVoidPtr (fds.rfd, NULL));
if (0 == daveConnectPLC (dconn))
{
//printf ("Connected.\n");
Py_INCREF (Py_None);
return Py_None;
}
else
{
printf ("Couldn't connect to PLC.\n");
return PyInt_FromLong (2);
}
}
else
{
printf ("Couldn't open TCP port. \nPlease make sure a CP is connected and the IP address is ok. \n");
return PyInt_FromLong (1);
}
Py_INCREF (Py_None);
return Py_None;
}
/*
* Disconnect PLC
*/
static PyObject *
ex_DisconnectPLC (PyObject *self, PyObject *args)
{
/*
*/
PyObject *selfobj;
daveConnection *dconn;
int sock;
daveSetDebug (daveDebugPrintErrors);
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "O", &selfobj)))
return NULL;
Py_DECREF (args);
dconn = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "daveconaddr"));
sock = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "davesocket"));
daveDisconnectPLC (dconn);
shutdown (sock, 2);
Py_INCREF (Py_None);
return Py_None;
}
/*
* Read Bytes
*/
static PyObject *
ex_ReadBytes (PyObject *self, PyObject *args)
{
/*
* 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.
*
* Area
* daveP 0x80
* daveInputs 0x81
* daveOutputs 0x82
* daveFlags 0x83 Merker
* daveDB 0x84 data blocks
* daveDI 0x85 instance data blocks
* daveLocal 0x86 not tested
* daveV 0x87 don't know what it is
* daveCounter 28 S7 counters
*
* DB
* start
* len
*/
PyObject *selfobj;
PyObject *retval;
int iArea, iDB, iStart, iLen;
int i, res;
daveConnection *dconn;
daveSetDebug (daveDebugPrintErrors);
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "Oiiii", &selfobj, &iArea, &iDB, &iStart, &iLen)))
return NULL;
Py_DECREF (args);
dconn = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "daveconaddr"));
res = daveReadBytes (dconn, iArea, iDB, iStart, iLen, NULL);
if (res == 0)
{
retval = PyList_New (iLen);
for (i = 0; i < iLen; i++)
PyList_SetItem (retval, i, PyInt_FromLong ((long) daveGetU8 (dconn)));
return retval;
}
else
{
printf ("failed! (%d)\n", res);
return PyInt_FromLong (res);
}
Py_INCREF (Py_None);
return Py_None;
}
/*
* Read Bits
*/
static PyObject *
ex_ReadBits (PyObject *self, PyObject *args)
{
PyObject *selfobj;
PyObject *retval;
daveConnection *dconn;
int iArea, iDB, iStart, iLen;
int i, res;
daveSetDebug (daveDebugPrintErrors);
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "Oiiii", &selfobj, &iArea, &iDB, &iStart, &iLen)))
return NULL;
dconn = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "daveconaddr"));
res = daveReadBits (dconn, iArea, iDB, iStart, iLen, NULL);
if (res == 0)
{
retval = PyList_New (iLen);
for (i = 0; i < iLen; i++)
PyList_SetItem (retval, i, PyInt_FromLong ((long) daveGetU8 (dconn)));
return retval;
}
else
{
printf ("failed! (%d)\n", res);
return PyInt_FromLong (res);
}
Py_INCREF (Py_None);
return Py_None;
}
/*
* Prepare Read Request
*/
static PyObject *
ex_PrepareReadRequest (PyObject *self, PyObject *args)
{
PyObject *selfobj;
daveConnection *dconn;
PDU *pdu;
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "O", &selfobj)))
return NULL;
Py_DECREF (args);
dconn = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "daveconaddr"));
pdu = (PDU*) malloc (sizeof (PDU));
PyObject_SetAttrString (selfobj, "pdu", PyCObject_FromVoidPtr (pdu, NULL));
pdu = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "pdu"));
davePrepareReadRequest (dconn, pdu);
Py_INCREF (Py_None);
return Py_None;
}
/*
* Add Bytes to Read Request
*/
static PyObject *
ex_AddVarToReadRequest (PyObject *self, PyObject *args)
{
PyObject *selfobj;
PDU *pdu;
int iArea, iDB, iStart, iLen;
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "Oiiii", &selfobj, &iArea, &iDB, &iStart, &iLen)))
return NULL;
Py_DECREF (args);
pdu = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "pdu"));
daveAddVarToReadRequest (pdu, iArea, iDB, iStart, iLen);
Py_INCREF (Py_None);
return Py_None;
}
/*
* Add Bit to Read Request
*/
static PyObject *
ex_AddBitVarToReadRequest (PyObject *self, PyObject *args)
{
PyObject *selfobj;
int iArea, iDB, iStart, iBytePos;
PDU *pdu;
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "Oiiii", &selfobj, &iArea, &iDB, &iStart, &iBytePos)))
return NULL;
Py_DECREF (args);
pdu = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "pdu"));
daveAddBitVarToReadRequest (pdu, iArea, iDB, iStart, iBytePos);
Py_INCREF (Py_None);
return Py_None;
}
/*
* Execute Read Request
*/
static PyObject *
ex_ExecReadRequest (PyObject *self, PyObject *args)
{
PyObject *selfobj;
PyObject *retval;
daveResultSet rs;
daveConnection *dconn;
PDU *pdu;
int i, rv, res, length;
rs.numResults = 0;
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "O", &selfobj)))
return NULL;
dconn = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "daveconaddr"));
pdu = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "pdu"));
rv = daveExecReadRequest (dconn, pdu, &rs);
if (rv !=0)
{
//printf ("daveExecReadRequest returns (%d)\n", rv);
Py_INCREF (Py_None);
return Py_None;
}
retval = PyList_New (rs.numResults);
if (rs.numResults == 0)
{
printf ("failed! no results\n");
Py_INCREF (Py_None);
return Py_None;
}
i = 0;
while (1)
{
res = daveUseResult (dconn, &rs, i);
if (res == 0)
{
length = rs.results[i].length;
if (length == 1)
PyList_SetItem (retval, i, PyInt_FromLong ((long) daveGetU8 (dconn)));
else if (length == 2)
PyList_SetItem (retval, i, PyInt_FromLong ((long) daveGetU16 (dconn)));
else if (length == 4)
PyList_SetItem (retval, i, PyInt_FromLong ((long) daveGetU32 (dconn)));
else
printf ("failed! size was (%d)\n", length);
}
else
{
daveFreeResults (&rs);
return retval;
}
i++;
}
}
/*
* Write Bytes
*/
static PyObject *
ex_WriteBytes (PyObject *self, PyObject *args)
{
/*
* Write len bytes vrom Buvver to PLC memory area "area", data block DBnum.
* 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.
*
* TODO Length check
*
* Area
* daveP 0x80
* daveInputs 0x81
* daveOutputs 0x82
* daveFlags 0x83 Merker
* daveDB 0x84 data blocks
* daveDI 0x85 instance data blocks
* daveLocal 0x86 not tested
* daveV 0x87 don't know what it is
* daveCounter 28 S7 counters
*
* DB
* start
* len
*/
PyObject *selfobj;
PyObject *values;
int iArea, iDB, iStart, iLen;
int res;
char * cvalues;
daveConnection *dconn;
daveSetDebug (daveDebugPrintErrors);
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "OiiiiO", &selfobj, &iArea, &iDB, &iStart, &iLen, &values)))
return NULL;
Py_DECREF (args);
dconn = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "daveconaddr"));
cvalues = PyString_AsString (values);
res = daveWriteBytes (dconn, iArea, iDB, iStart, iLen, cvalues);
if (res != 0)
{
printf ("failed! (%d)\n", res);
return PyInt_FromLong (res);
}
Py_INCREF (Py_None);
return Py_None;
}
/*
* U32 to FloatRequest
*/
static PyObject *
ex_U32toREAL (PyObject *self, PyObject *args)
{
PyObject *selfobj;
PyObject *retval;
long value;
uc * vp;
union
{
float a;
uc b[8];
} f;
//Extract value
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "Ol", &selfobj, &value)))
return NULL;
vp = (uc *) &value;
f.b[0] = *(vp);
vp++;
f.b[1] = *(vp);
vp++;
f.b[2] = *(vp);
vp++;
f.b[3] = *(vp);
retval = PyFloat_FromDouble (f.a);
return retval;
}
static PyMethodDef pydaveMethods [] = {
{"OpenSocket", ex_OpenSocket, METH_VARARGS, "Open Socket to PLC"},
{"DisconnectPLC", ex_DisconnectPLC, METH_VARARGS, "Close Connection to PLC"},
{"ReadBytes", ex_ReadBytes, METH_VARARGS, "Read Bytes from PLC"},
//{"ReadManyBytes", ex_ReadManyBytes, METH_VARARGS, "Read Many Bytes from PLC"},
{"ReadBits", ex_ReadBits, METH_VARARGS, "Read Bits from PLC"},
{"WriteBytes", ex_WriteBytes, METH_VARARGS, "Write Bytes to PLC"},
//{"WriteManyBytes", ex_WriteManyBytes, METH_VARARGS, "Write Bytes to PLC"},
//{"WriteBit", ex_WriteBit, METH_VARARGS, "Write Bit to PLC"},
//{"SetBit", ex_SetBit, METH_VARARGS, "Set Bit on PLC"},
//{"ClrBit", ex_ClrBit, METH_VARARGS, "Clear Bit on PLC"},
{"PrepareReadRequest", ex_PrepareReadRequest, METH_VARARGS, "Prepare Read Request"},
{"AddVarToReadRequest", ex_AddVarToReadRequest, METH_VARARGS, "Add Variable to Read Request"},
{"AddBitVarToReadRequest", ex_AddBitVarToReadRequest, METH_VARARGS, "Add Bit Variable to Read Request"},
{"ExecReadRequest", ex_ExecReadRequest, METH_VARARGS, "Execute Read Request"},
{"U32toREAL", ex_U32toREAL, METH_VARARGS, "Return Float"},
{NULL, NULL, 0, NULL}
};
static PyMethodDef Modulemethods [] = {
{NULL, NULL}
};
static PyMethodDef AttrMethods [] = {
{NULL, NULL}
};
void initpydave (void)
{
PyMethodDef *def;
/*create a new module and class*/
PyObject *module = Py_InitModule ("pydave", Modulemethods);
PyObject *moduleDict = PyModule_GetDict (module);
PyObject *classDict = PyDict_New ();
PyObject *className = PyString_FromString ("pydave");
//PyObject *daveconaddr = Py_BuildValue ("i", 0);
PyObject *daveconaddr = Py_None;
PyDict_SetItemString (classDict, "daveconaddr", daveconaddr);
PyObject *pduPy = Py_None;
//PDU *pdu = (PDU*) malloc (sizeof (PDU));
//PyObject *pduPy = PyCObject_FromVoidPtr (pdu, NULL);
PyDict_SetItemString (classDict, "pdu", pduPy);
for (def = AttrMethods; def->ml_name; def++)
{
PyObject *func = PyCFunction_New (def, NULL);
PyDict_SetItemString (classDict, def->ml_name, func);
Py_DECREF (func);
}
PyObject *PyDaveClass = PyClass_New (NULL, classDict, className);
PyDict_SetItemString (moduleDict, "pydave", PyDaveClass);
Py_DECREF (classDict);
Py_DECREF (className);
Py_DECREF (PyDaveClass);
/* add methods to class */
for (def = pydaveMethods; def->ml_name != NULL; def++)
{
PyObject *func = PyCFunction_New (def, NULL);
PyObject *method = PyMethod_New (func, NULL, PyDaveClass);
PyDict_SetItemString (classDict, def->ml_name, method);
Py_DECREF (func);
Py_DECREF (method);
}
}

11
Python/Makefile Normal file
View File

@@ -0,0 +1,11 @@
all: pydave.o pydave.so
pydave.o: pydave.c
gcc -g -DNDEBUG -O2 -falign-loops=16 -save-temps -funroll-loops -funroll-all-loops -fno-strict-aliasing -Wall -Wstrict-prototypes -fPIC -c pydave.c -o pydave.o
pydave.so: pydave.o
#ld --shared pydave.o --shared ../nodave.o --shared ../openSocket.o -o pydave.so
ld -lpython2.7 --shared pydave.o --shared /usr/lib/libnodave.so -o pydave.so
clean:
rm -f *.so *.o *.i *.s *~

28
Python/davedefs.py Normal file
View File

@@ -0,0 +1,28 @@
__author__ = "Michael Rest"
__date__ = "1 June 2000"
__email__ = "michi@rosstein.de"
__version__ = "$Revision: 1.0 $"[11:-2]
daveAreas = {
"P" : 0x80, #daveP
"E" : 0x81, #daveInputs
"A" : 0x82, #daveFlags
"M" : 0x83, #daveDB Data Blocks
"DB": 0x84, #daveDI Instance Data Blocs
"DI": 0x85, #daveLocal !!! Untestet
"C" : 0x1c, #daveCounter
"T" : 0x1d #daveTimer
}
daveTypes = {
"BOOL" : 1,
"BYTE" : 1,
"WORD" : 2,
"INT" : 2,
"DWORD" : 4,
"REAL" : 4,
"DINT" : 4
}

3
Python/nodave.cfg Normal file
View File

@@ -0,0 +1,3 @@
[SPS1]
ip = 192.168.100.72

BIN
Python/nodave.db3 Normal file

Binary file not shown.

552
Python/pydave.c Normal file
View File

@@ -0,0 +1,552 @@
/*
* Python Wrapper for llibnodave
*
* NOTE: !!!! Always use Prepare after a Byte Read
*/
#define LINUX
#include <python2.7/Python.h>
#include <stdlib.h>
#include <stdio.h>
#include "../nodave.h"
#include "../nodavesimple.h"
#include "../openSocket.h"
//---------------------------------------------------------
// OpenSocket
// Open Socket connection
// "xxx.xxx.xxx.xxx" server - IP Address
//---------------------------------------------------------
static PyObject *
ex_OpenSocket (PyObject *self, PyObject *args)
{
PyObject *selfobj;
_daveOSserialType fds;
daveInterface *di;
daveConnection *dconn;
char *server;
char *proto;
int port, mpi, rack, slot, dproto, dspeed;
int routingSubnetFirst, routingSubnetSecond, routingRack, routingSlot;
int routingDestination;
daveSetDebug (daveDebugPrintErrors);
//Extract Servername from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "Ossiiiiiiiii", &selfobj, &proto, &server, &port, &mpi, &rack, &slot, &routingSubnetFirst, &routingSubnetSecond, &routingRack, &routingSlot, &routingDestination)))
return NULL;
Py_DECREF (args);
if (strcmp( proto, "ISO_TCP" ) == 0){
dproto = daveProtoISOTCP;
}
else if (strcmp( proto, "IBH_Link" ) == 0){
dproto = daveProtoMPI_IBH;
}
else{
printf ("Invalid Proto.\n");
return PyInt_FromLong (3);
}
dspeed = daveSpeed187k;
fds.rfd = openSocket (port, server);
fds.wfd = fds.rfd;
if (fds.rfd > 0) {
di = daveNewInterface (fds, "IF1", 0, dproto, dspeed);
if (0 == daveInitAdapter (di))
{}
daveSetTimeout (di, 5000000);
if (routingSubnetFirst == 0) {
dconn = daveNewConnection (di, mpi, rack, slot); // insert your rack and slot here
}
else {
dconn = daveNewExtendedConnection (di, &mpi, 0, rack, slot, 1, routingSubnetFirst, routingSubnetSecond, routingRack, routingSlot, &routingDestination, 0, 1, 1);
//daveNewExtendedConnection(daveInterface * di, void * Destination, int DestinationIsIP, int rack, int slot, int routing, int routingSubnetFirst, int routingSubnetSecond, int routingRack, int routingSlot, void * routingDestination, int routingDestinationIsIP, int ConnectionType, int routingConnectionType) {
}
PyObject_SetAttrString (selfobj, "daveconaddr", PyCObject_FromVoidPtr (dconn, NULL));
PyObject_SetAttrString (selfobj, "davesocket", PyCObject_FromVoidPtr (fds.rfd, NULL));
if (0 == daveConnectPLC (dconn))
{
//printf ("Connected.\n");
Py_INCREF (Py_None);
return Py_None;
}
else
{
printf ("Couldn't connect to PLC.\n");
return PyInt_FromLong (2);
}
}
else
{
printf ("Couldn't open TCP port. \nPlease make sure a CP is connected and the IP address is ok. \n");
return PyInt_FromLong (1);
}
Py_INCREF (Py_None);
return Py_None;
}
/*
* Disconnect PLC
*/
static PyObject *
ex_DisconnectPLC (PyObject *self, PyObject *args)
{
/*
*/
PyObject *selfobj;
daveConnection *dconn;
int sock;
daveSetDebug (daveDebugPrintErrors);
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "O", &selfobj)))
return NULL;
Py_DECREF (args);
dconn = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "daveconaddr"));
sock = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "davesocket"));
daveDisconnectPLC (dconn);
shutdown (sock, 2);
Py_INCREF (Py_None);
return Py_None;
}
/*
* Read Bytes
*/
static PyObject *
ex_ReadBytes (PyObject *self, PyObject *args)
{
/*
* 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.
*
* Area
* daveP 0x80
* daveInputs 0x81
* daveOutputs 0x82
* daveFlags 0x83 Merker
* daveDB 0x84 data blocks
* daveDI 0x85 instance data blocks
* daveLocal 0x86 not tested
* daveV 0x87 don't know what it is
* daveCounter 28 S7 counters
*
* DB
* start
* len
*/
PyObject *selfobj;
PyObject *retval;
int iArea, iDB, iStart, iLen;
int i, res;
daveConnection *dconn;
daveSetDebug (daveDebugPrintErrors);
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "Oiiii", &selfobj, &iArea, &iDB, &iStart, &iLen)))
return NULL;
Py_DECREF (args);
dconn = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "daveconaddr"));
if (iLen > 222)
{
printf ("Readbytes Len exceeded PDU!\n");
return Py_None;
}
res = daveReadBytes (dconn, iArea, iDB, iStart, iLen, NULL);
if (res == 0)
{
retval = PyList_New (iLen);
for (i = 0; i < iLen; i++)
PyList_SetItem (retval, i, PyInt_FromLong ((long) daveGetU8 (dconn)));
return retval;
}
else
{
printf ("Readbytes failed! (%d)\n", res);
return PyInt_FromLong (res);
}
Py_INCREF (Py_None);
return Py_None;
}
/*
* Read Bits
*/
static PyObject *
ex_ReadBits (PyObject *self, PyObject *args)
{
PyObject *selfobj;
PyObject *retval;
daveConnection *dconn;
int iArea, iDB, iStart, iLen;
int i, res;
daveSetDebug (daveDebugPrintErrors);
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "Oiiii", &selfobj, &iArea, &iDB, &iStart, &iLen)))
return NULL;
dconn = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "daveconaddr"));
res = daveReadBits (dconn, iArea, iDB, iStart, iLen, NULL);
if (res == 0)
{
retval = PyList_New (iLen);
for (i = 0; i < iLen; i++)
PyList_SetItem (retval, i, PyInt_FromLong ((long) daveGetU8 (dconn)));
return retval;
}
else
{
printf ("ReadBits failed! (%d)\n", res);
return PyInt_FromLong (res);
}
Py_INCREF (Py_None);
return Py_None;
}
/*
* Prepare Read Request
*/
static PyObject *
ex_PrepareReadRequest (PyObject *self, PyObject *args)
{
PyObject *selfobj;
daveConnection *dconn;
PDU *pdu;
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "O", &selfobj)))
return NULL;
Py_DECREF (args);
dconn = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "daveconaddr"));
pdu = (PDU*) malloc (sizeof (PDU));
PyObject_SetAttrString (selfobj, "pdu", PyCObject_FromVoidPtr (pdu, NULL));
pdu = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "pdu"));
davePrepareReadRequest (dconn, pdu);
Py_INCREF (Py_None);
return Py_None;
}
/*
* Add Bytes to Read Request
*/
static PyObject *
ex_AddVarToReadRequest (PyObject *self, PyObject *args)
{
PyObject *selfobj;
PDU *pdu;
int iArea, iDB, iStart, iLen;
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "Oiiii", &selfobj, &iArea, &iDB, &iStart, &iLen)))
return NULL;
Py_DECREF (args);
pdu = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "pdu"));
daveAddVarToReadRequest (pdu, iArea, iDB, iStart, iLen);
Py_INCREF (Py_None);
return Py_None;
}
/*
* Add Bit to Read Request
*/
static PyObject *
ex_AddBitVarToReadRequest (PyObject *self, PyObject *args)
{
PyObject *selfobj;
int iArea, iDB, iStart, iBytePos;
PDU *pdu;
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "Oiiii", &selfobj, &iArea, &iDB, &iStart, &iBytePos)))
return NULL;
Py_DECREF (args);
pdu = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "pdu"));
daveAddBitVarToReadRequest (pdu, iArea, iDB, iStart, iBytePos);
Py_INCREF (Py_None);
return Py_None;
}
/*
* Execute Read Request
*/
static PyObject *
ex_ExecReadRequest (PyObject *self, PyObject *args)
{
PyObject *selfobj;
PyObject *retval;
daveResultSet rs;
daveConnection *dconn;
PDU *pdu;
int i, rv, res, length;
rs.numResults = 0;
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "O", &selfobj)))
return NULL;
dconn = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "daveconaddr"));
pdu = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "pdu"));
rv = daveExecReadRequest (dconn, pdu, &rs);
if (rv !=0)
{
//printf ("daveExecReadRequest returns (%d)\n", rv);
Py_INCREF (Py_None);
return Py_None;
}
retval = PyList_New (rs.numResults);
if (rs.numResults == 0)
{
printf ("Exec ReadReqest failed! no results\n");
Py_INCREF (Py_None);
return Py_None;
}
i = 0;
while (1)
{
res = daveUseResult (dconn, &rs, i, NULL);
if (res == 0)
{
length = rs.results[i].length;
if (length == 1)
PyList_SetItem (retval, i, PyInt_FromLong ((long) daveGetU8 (dconn)));
else if (length == 2)
PyList_SetItem (retval, i, PyInt_FromLong ((long) daveGetU16 (dconn)));
else if (length == 4)
PyList_SetItem (retval, i, PyInt_FromLong ((long) daveGetU32 (dconn)));
else
printf ("failed! size was (%d)\n", length);
}
else
{
daveFreeResults (&rs);
return retval;
}
i++;
}
}
/*
* Write Bytes
*/
static PyObject *
ex_WriteBytes (PyObject *self, PyObject *args)
{
/*
* Write len bytes vrom Buvver to PLC memory area "area", data block DBnum.
* 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.
*
* TODO Length check
*
* Area
* daveP 0x80
* daveInputs 0x81
* daveOutputs 0x82
* daveFlags 0x83 Merker
* daveDB 0x84 data blocks
* daveDI 0x85 instance data blocks
* daveLocal 0x86 not tested
* daveV 0x87 don't know what it is
* daveCounter 28 S7 counters
*
* DB
* start
* len
*/
PyObject *selfobj;
PyObject *values;
int iArea, iDB, iStart, iLen;
int res;
char * cvalues;
daveConnection *dconn;
daveSetDebug (daveDebugPrintErrors);
//Extract area, db, start, len from Arguments
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "OiiiiO", &selfobj, &iArea, &iDB, &iStart, &iLen, &values)))
return NULL;
Py_DECREF (args);
dconn = PyCObject_AsVoidPtr (PyObject_GetAttrString (selfobj, "daveconaddr"));
cvalues = PyString_AsString (values);
res = daveWriteBytes (dconn, iArea, iDB, iStart, iLen, cvalues);
if (res != 0)
{
printf ("WriteBytes failed! (%d)\n", res);
return PyInt_FromLong (res);
}
Py_INCREF (Py_None);
return Py_None;
}
/*
* U32 to FloatRequest
*/
static PyObject *
ex_U32toREAL (PyObject *self, PyObject *args)
{
PyObject *selfobj;
PyObject *retval;
long value;
uc * vp;
union
{
float a;
uc b[8];
} f;
//Extract value
Py_INCREF (args);
if (!(PyArg_ParseTuple (args, "Ol", &selfobj, &value)))
return NULL;
vp = (uc *) &value;
f.b[0] = *(vp);
vp++;
f.b[1] = *(vp);
vp++;
f.b[2] = *(vp);
vp++;
f.b[3] = *(vp);
retval = PyFloat_FromDouble (f.a);
return retval;
}
static PyMethodDef pydaveMethods [] = {
{"OpenSocket", ex_OpenSocket, METH_VARARGS, "Open Socket to PLC"},
{"DisconnectPLC", ex_DisconnectPLC, METH_VARARGS, "Close Connection to PLC"},
{"ReadBytes", ex_ReadBytes, METH_VARARGS, "Read Bytes from PLC"},
//{"ReadManyBytes", ex_ReadManyBytes, METH_VARARGS, "Read Many Bytes from PLC"},
{"ReadBits", ex_ReadBits, METH_VARARGS, "Read Bits from PLC"},
{"WriteBytes", ex_WriteBytes, METH_VARARGS, "Write Bytes to PLC"},
//{"WriteManyBytes", ex_WriteManyBytes, METH_VARARGS, "Write Bytes to PLC"},
//{"WriteBit", ex_WriteBit, METH_VARARGS, "Write Bit to PLC"},
//{"SetBit", ex_SetBit, METH_VARARGS, "Set Bit on PLC"},
//{"ClrBit", ex_ClrBit, METH_VARARGS, "Clear Bit on PLC"},
{"PrepareReadRequest", ex_PrepareReadRequest, METH_VARARGS, "Prepare Read Request"},
{"AddVarToReadRequest", ex_AddVarToReadRequest, METH_VARARGS, "Add Variable to Read Request"},
{"AddBitVarToReadRequest", ex_AddBitVarToReadRequest, METH_VARARGS, "Add Bit Variable to Read Request"},
{"ExecReadRequest", ex_ExecReadRequest, METH_VARARGS, "Execute Read Request"},
{"U32toREAL", ex_U32toREAL, METH_VARARGS, "Return Float"},
{NULL, NULL, 0, NULL}
};
static PyMethodDef Modulemethods [] = {
{NULL, NULL}
};
static PyMethodDef AttrMethods [] = {
{NULL, NULL}
};
void initpydave (void)
{
PyMethodDef *def;
/*create a new module and class*/
PyObject *module = Py_InitModule ("pydave", Modulemethods);
PyObject *moduleDict = PyModule_GetDict (module);
PyObject *classDict = PyDict_New ();
PyObject *className = PyString_FromString ("pydave");
//PyObject *daveconaddr = Py_BuildValue ("i", 0);
PyObject *daveconaddr = Py_None;
PyDict_SetItemString (classDict, "daveconaddr", daveconaddr);
PyObject *pduPy = Py_None;
//PDU *pdu = (PDU*) malloc (sizeof (PDU));
//PyObject *pduPy = PyCObject_FromVoidPtr (pdu, NULL);
PyDict_SetItemString (classDict, "pdu", pduPy);
for (def = AttrMethods; def->ml_name; def++)
{
PyObject *func = PyCFunction_New (def, NULL);
PyDict_SetItemString (classDict, def->ml_name, func);
Py_DECREF (func);
}
PyObject *PyDaveClass = PyClass_New (NULL, classDict, className);
PyDict_SetItemString (moduleDict, "pydave", PyDaveClass);
Py_DECREF (classDict);
Py_DECREF (className);
Py_DECREF (PyDaveClass);
/* add methods to class */
for (def = pydaveMethods; def->ml_name != NULL; def++)
{
PyObject *func = PyCFunction_New (def, NULL);
PyObject *method = PyMethod_New (func, NULL, PyDaveClass);
PyDict_SetItemString (classDict, def->ml_name, method);
Py_DECREF (func);
Py_DECREF (method);
}
}

93
Python/testIBH.py Executable file
View File

@@ -0,0 +1,93 @@
#!/usr/bin/python
__author__ = "Michael Rest"
__date__ = "1 June 2009"
__email__ = "michi@rosstein.de"
__version__ = "$Revision: 1.0 $"[11:-2]
from pydave import pydave
from davedefs import *
from ConfigParser import *
from datetime import datetime
from time import sleep
from select import select
import sys
import os
import string
import signal
import redis
#Enable Debug prints
DEBUG = 0
def on_term (signum, frame):
sys.exit (1);
def on_usr (signum, frame):
pass;
if __name__ == '__main__':
signal.signal (signal.SIGTERM, on_term)
signal.signal (signal.SIGUSR2, on_usr)
signal.signal( signal.SIGUSR1, on_usr)
INSTANZ= sys.argv[0];
if len (sys.argv) > 2 and sys.argv[2] == '-init':
sys.exit (0)
if len (sys.argv) > 2 and sys.argv[2] == '-deinit' :
if len(sys.argv) > 3 and sys.argv[3] == '-forced' :
if len(sys.argv) > 4 :
ListOfTerminatedInstances=string.split (sys.argv [4], ',')
for inst in ListOfTerminatedInstances:
# Cleanup
pass;
sys.exit (0)
r_server = redis.Redis ('176.9.41.147')
#for _id, _name, _proto, _addr, _port, _mpi, _rack, _slot in [[5,'H6L2', 'ISO_TCP', '192.168.100.73', 102, 2, 0, 4]]:
#for _id, _name, _proto, _addr, _port, _mpi, _rack, _slot in [[5,'H6L1', 'ISO_TCP', '192.168.100.73', 102, 3, 0, 4]]:
clients = [
[1,'Centrotherm', 'IBH_Link', '192.168.125.201', 1099, 2, 0, 4, 0, 0, 0, 0, 0],
]
conns = {}
for client in clients:
_id, _name, _proto, _addr, _port, _mpi, _rack, _slot, _rsid1, _rsid2, _rrack, _rslot, _rmpi = client
print (_name)
print ("========================================")
print ("open connection")
#print ("Found Client id %d '%s' with proto %s on address %s Port %d with MPI %d on Rack %d Slot %d" %(_id, _name, _proto, _addr, _port, _mpi, _rack, _slot))
if _proto in ["ISO_TCP","IBH_Link"]:
conns [_id] = pydave ()
conns [_id].OpenSocket (_proto, _addr, _port, _mpi, _rack, _slot, _rsid1, _rsid2, _rrack, _rslot, _rmpi)
#routingSubnetFirst, routingSubnetSecond, routingRack, routingSlot
else:
print ("Invalid Proto for Client")
sys.exit (0)
for client in clients:
_id, _name, _proto, _addr, _port, _mpi, _rack, _slot, _rsid1, _rsid2, _rrack, _rslot, _rmpi = client
print (_name)
print ("========================================")
nr = 1
offset = 0
blocksize = 220
r_pipe = r_server.pipeline ()
while nr < 300:
val = conns [_id].ReadBytes (daveAreas["DB"], 2, offset, blocksize)
for i in range (len (val) / 4):
counter = (val [i * 4 + 0] << 8) + val [i * 4 + 1]
print "%s Counter = %d" % (nr, counter)
print "%s State = %d" % (nr, val [i * 4 + 2])
print "%s Action = %d" % (nr, val [i * 4 + 3])
r_pipe.set('Counter%03d' % nr, counter)
r_pipe.set('State%03d' % nr, val [i * 4 + 2])
r_pipe.set('Action%03d' % nr, val [i * 4 + 3])
nr += 1
offset += blocksize
r_pipe.execute ()
conns [_id].DisconnectPLC ()

53
Python/testRedis.py Executable file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/python
__author__ = "Michael Rest"
__date__ = "1 June 2009"
__email__ = "michi@rosstein.de"
__version__ = "$Revision: 1.0 $"[11:-2]
from ConfigParser import *
from datetime import datetime
from time import sleep
import sys
import os
import string
import signal
import redis
#Enable Debug prints
DEBUG = 0
def on_term (signum, frame):
sys.exit (1);
def on_usr (signum, frame):
pass;
if __name__ == '__main__':
signal.signal (signal.SIGTERM, on_term)
signal.signal (signal.SIGUSR2, on_usr)
signal.signal( signal.SIGUSR1, on_usr)
INSTANZ= sys.argv[0];
if len (sys.argv) > 2 and sys.argv[2] == '-init':
sys.exit (0)
if len (sys.argv) > 2 and sys.argv[2] == '-deinit' :
if len(sys.argv) > 3 and sys.argv[3] == '-forced' :
if len(sys.argv) > 4 :
ListOfTerminatedInstances=string.split (sys.argv [4], ',')
for inst in ListOfTerminatedInstances:
# Cleanup
pass;
sys.exit (0)
r_server = redis.Redis ('176.9.41.147')
r_pipe = r_server.pipeline ()
for nr in range (1,500):
r_pipe.get('Counter%03d' % nr)
print "Counters", r_pipe.execute ()
for nr in range (1,250):
r_pipe.get('State%03d' % nr)
print "States", r_pipe.execute ()
for nr in range (1,250):
r_pipe.get('Action%03d' % nr)
print "States", r_pipe.execute ()

77
Python/testdave-routed.py Executable file
View File

@@ -0,0 +1,77 @@
#!/usr/bin/python
__author__ = "Michael Rest"
__date__ = "1 June 2009"
__email__ = "michi@rosstein.de"
__version__ = "$Revision: 1.0 $"[11:-2]
from pydave import pydave
from davedefs import *
from ConfigParser import *
from datetime import datetime
from time import sleep
from select import select
import sys
import os
import string
import signal
#Enable Debug prints
DEBUG = 0
def on_term (signum, frame):
sys.exit (1);
def on_usr (signum, frame):
pass;
if __name__ == '__main__':
signal.signal (signal.SIGTERM, on_term)
signal.signal (signal.SIGUSR2, on_usr)
signal.signal( signal.SIGUSR1, on_usr)
INSTANZ= sys.argv[0];
if len (sys.argv) > 2 and sys.argv[2] == '-init':
sys.exit (0)
if len (sys.argv) > 2 and sys.argv[2] == '-deinit' :
if len(sys.argv) > 3 and sys.argv[3] == '-forced' :
if len(sys.argv) > 4 :
ListOfTerminatedInstances=string.split (sys.argv [4], ',')
for inst in ListOfTerminatedInstances:
# Cleanup
pass;
sys.exit (0)
#for _id, _name, _proto, _addr, _port, _mpi, _rack, _slot in [[5,'H6L2', 'ISO_TCP', '192.168.100.73', 102, 2, 0, 4]]:
#for _id, _name, _proto, _addr, _port, _mpi, _rack, _slot in [[5,'H6L1', 'ISO_TCP', '192.168.100.73', 102, 3, 0, 4]]:
clients = [
[1,'H6L3', 'ISO_TCP', '192.168.100.73', 102, 3, 0, 4, 0, 0, 0, 0, 0],
[2,'H6L1', 'ISO_TCP', '192.168.100.73', 102, 3, 0, 4, 53, 1, 0, 3, 11],
[3,'H6L2', 'ISO_TCP', '192.168.100.73', 102, 3, 0, 4, 53, 1, 0, 3, 2],
]
for client in clients:
_id, _name, _proto, _addr, _port, _mpi, _rack, _slot, _rsid1, _rsid2, _rrack, _rslot, _rmpi = client
print (_name)
print ("========================================")
#print ("Found Client id %d '%s' with proto %s on address %s Port %d with MPI %d on Rack %d Slot %d" %(_id, _name, _proto, _addr, _port, _mpi, _rack, _slot))
if _proto in ["ISO_TCP","IBH_Link"]:
a = pydave ()
a.OpenSocket (_proto, _addr, _port, _mpi, _rack, _slot, _rsid1, _rsid2, _rrack, _rslot, _rmpi)
#routingSubnetFirst, routingSubnetSecond, routingRack, routingSlot
temperature = 0
for i in range (1, 15):
offset = i * 12 + 60
temp = a.ReadBytes (daveAreas["DB"], 157, offset, 2)
temperature = (temp [0] << 8) + temp [1]
print "Temperature%02d=%d" % (i, temperature)
offset = i * 15 + 155
curr = a.ReadBytes (daveAreas["DB"], 150, offset, 2)
current = (curr [0] << 8) + curr [1]
print "Current%02d=%d" % (i, current)
a.DisconnectPLC ()
else:
print ("Invalid Proto for Client")
sys.exit (0)

97
README.compiling Normal file
View File

@@ -0,0 +1,97 @@
How to compile in different enviroments:
1. Linux on i386: Just type make. Type (as root) make install. This will:
copy libnodave.so to /usr/local/lib
copy nodave.h to /usr/local/include
2. Linux on ARM:
Uncomment the line: #CFLAGS+=-DARM_FIX in Makefile
For cross development, replace CC= and LD= with the appropriate paths to your ARM compiler
and linker.
Do make.
Either do make install or copy libnodave.so to the target system.
3. Linux on MIPS:
For cross development, replace CC= and LD= with the appropriate paths to your MIPS compiler
and linker.
Do make.
Either do make install or copy libnodave.so to the target system.
4. Microsoft Visual C++
Adjust the path to nmake in vcwinmake.bat.
Adjust paths in MAKEFILE.VC to your installation.
Run batch file vcwinmake.bat.
5. Borland Free Commandline Tools
Adjust the path to make in winmake.bat.
Adjust paths in MAKEFILE.MAK to your installation.
Run batch file winmake.bat.
6. Microsoft Visual C++ on LINUX under Wine
Adjust paths in MAKEFILE.VC.WINE to your installation.
Run make -f MAKEFILE.VC.WINE. This will use GNU make to build everything.
7. Borland Free Commandline Tools on LINUX under Wine
Adjust the path to make in winemake script.
Adjust paths in MAKEFILE.MAK to your installation.
Run script winemake
Hint: I use
make -f MAKEFILE.VC.WINE
on a Linux box to compile the windows stuff. While I TRY to keep the other makefiles up to date,
this file is where you should look for compiler options and filenames and compare to your setup
if you have difficulties to reproduce the same results.
from the FAQ:
Q: I try to compile my source code which includes nodave.h. Why do I get:
"#error Fill in what you need for your OS or API" ?
A: Libnodave has to use different functions to read byte data from serial ports or TCP/IP streams
depending on the operating system. You have to specify which OS you compile for.
Define BCCWIN for Windows and LINUX for Linux or other Unix style operating systems. I do
that using -DBCCWIN or -DLINUX when I call the compilers out of a Makefile. If you use an IDE
(integrated development environment), refer to the IDE's documentation to learn where to do
this. If you can't find out, you can help yourself putting the #define into your code (which
means your code will not be portable to other OS without modification). example:
...
#define BCCWIN // if you work on windows
#include nodave.h // or nodavesimple.h, but NEVER both
#include setport.h // if your code works with serial connections
#include openSocket.h // if your code works with TCP/IP
...
Q: I try to compile libnodave from source code. There are problems.
A: - You do not need to compile libnodave yourself. You won't do it with other DLLs (windows)
or .so (Linux shared libraries). Normally, you will link such libraries dynamically with
your code.
- Nevertheless, you have the sources and you may chose to recompile it, recompile only the
parts you need, etcetera.
- You should know that S7 uses Motorola (big endian, high byte first) byte order (endianness)
while Intel processors use Intel (little endian, low byte first). On Intel and similar
machines, libnodave has to convert multibyte values. This is done in daveGetS2(), daveGetU2(),
daveGetFloat() and friends. The conversion code is only compiled in if you #define
DAVE_LITTLE_ENDIAN. Otherwise, big endian is assumed.
- Be aware that I do compile releases of libnodave using gcc (for Linux) and MSVC++
(for windows). I use the script buildall to build Linux and Windows release versions and test
programs on a Linux box. Buildall uses Linux make with MAKEFILE.VC.WINE to invoke the MSVC++
compiler for windows versions. There is also a MAKEFILE.VC which should work on a windows
system using nmake, but it may be out of date :-(. If in doubt about compiler options or
source files involved, refer to MAKEFILE.VC.WINE!
Q: I want to compile libnodave for another OS. What adaptions shall I have to make?
A: - I suppose your OS is 32 bit and your compiler treats int as 32 bit. If not, you are in
trouble!
- You need to provide functional replacements for setport.c or setportw.c and openSocket.c or
openSocketw.c
- nodave.h: You need to define a record _daveOSserialType containing members:
rfd: a data type that can refer to a serial port or a TCP socket for read access
wfd: a data type that can refer to a serial port or a TCP socket for write access
- nodave.c: You need to code functions stdread() and stdwrite() to read and write a number of
bytes from/to a serial port to/from a buffer.

67
Readme.win Normal file
View File

@@ -0,0 +1,67 @@
To build for windows:
While former versions where built with Borland's free commandline tools, the resulting .dll and
.lib files are incompatible with MSVC. So, from this version on, MSVC is preferred compiler for
Win32. You can still build the .dll with Borland tools. For instructions see below.
To build libnodave.dll with MSVC:
1. Get and install MSVC. There is a freely available package from Microsoft containing only the
compiler and linker. You need some windows header files also, which are available as part
of the "Platform Software Development Kit (PSDK)". This PSDK contains also a make utility
named nmake.
2. Makefile is for LINUX. For Windows, you need the file MAKEFILE.VC
Either remove the file Makefile or call make with -fMAKEFILE.VC.
3. Running make with no target will build statically used versions of the test
programs and libnodave.dll
4. Running make with target "dynamic" builds dynamically linked versions of the test
programs and libnodave.dll.
5. If you have difficulties to rebuild libnodave.dll, e.g.:
- you get more or less underscores
- you get error messages when compiling
- you get mangled names
- you get error messages when linking
- the recompiled .dll behaves different from the precompiled one
Allways check MAKEFILE.VC.WINE. This is what I use do build the files I distribute. And
besides all efforts to maintain other makefiles, I may forget to update them...
To build libnodave.dll with BorlandC:
1. Get and install the free commandline tools from borland (www.borland.com).
2. The Makefile is for LINUX. For Windows, you need the file MAKEFILE.MAK.
Either remove the file Makefile or call make with -f MAKEFILE.MAK.
3. Running make with no target will build statically linked versions of the test
programs and libnodave.dll
4. Running make with target "dynamic" builds dynamically linked versions of the test
programs and libnodave.dll.
Using the .dll with C:
You can import either nodave.h or nodavesimple.h. While it makes no difference for most
applications, it is recommended to use nodave.h, because:
- it gives you full access to all internal structures and routines in case you want to do
something unforeseen.
- it is most certainly up to date because the library itself is build with it.
nodavesimple.h is merely there for people who want to interface with languages not yet
supported to give them a guideline what they need to define.
In any case you must make sure that the conditionals BCCWIN and DAVE_LITTLE_ENDIAN are defined
at the moment the preprocessor enters into nodave.h. You can do this by one of these means:
- specify -DBCCWIN -DDAVELITTLE_ENDIAN on the compiler's commandline (recommended)
- put the lines #define BCCWIN, #define DAVE_LITTLE_ENDIAN before #include "nodave.h"
Using the .dll with FreePascal or Delphi:
use unit nodave in your program. nodave.pas corresponds to the .dll generated with MSVC. Should
you want to recompile the .dll with BorlandC and then use it from Pascal, also recompile
unit nodave from nodave.pas.borland. The difference is in the use of underscores in the names
imported from the .dll.
Using the .dll with C#:
There are C# versions of testMPI in the directory dot-net. There is also another README in that
directory. The files have been compiled and found to work with MONO 1.0.5 under Win2k. MONO is
an open source implementation of C# and the .NET framework.
Using the .dll with other languages:
Some people found it very difficult to port the structures daveInterface and daveConnection to
other languages, e.g. VB. In fact, this is not necessary for 99% of all possible applications.
Member fields of these structure either never need to be accessed outside libnodave itself or
they can be accessed using some newly added helper functions. So, regardless how complicate
you think daveInterface is, all you need is a pointer to it.

87
accepter.c Normal file
View File

@@ -0,0 +1,87 @@
/*
Part of multithreaded network servers. The servers MAIN thread is started by
the main program. It starts in turn the ACCEPTER thread, which accepts a
connection and passes the file descriptor to the MAIN thread. The server MAIN
thread starts a PORT SERVER thread to handle the connection. Thus, the
PORT SERVER thread is NOT a child of ACCEPTER, but a brother and SIGCHILD goes
to the server MAIN. If it would go to ACCEPTER, which is blocked in the
accept() system call, we would get "process interrupted in system call" and
that's it. Maybe its different with pthread library..?
Part of Libnodave, a free communication libray for Siemens S7 300/400.
(C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2001..2005.
LIBNDAVE is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with this; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
typedef struct {
int fd;
int port;
} accepter_info;
void *accepter(void *arg)
{
int s;
int newfd, pipefd;
socklen_t addrlen;
struct sockaddr_in addr;
int i;
int opt;
usleep(10000);
pipefd = ((accepter_info *) arg)->fd;
LOG2(ThisModule "Accepter : My pipe is:%d\n", pipefd);
FLUSH;
s = socket(AF_INET, SOCK_STREAM, 0);
if (errno != 0) {
LOG2(ThisModule "Accepter : socket %s\n", strerror(errno));
FLUSH;
}
LOG2(ThisModule "Accepter : port %d\n",
((accepter_info *) arg)->port);
addr.sin_family = AF_INET;
addr.sin_port =htons(((accepter_info *) arg)->port);
// (((((accepter_info *) arg)->port) & 0xff) << 8) |
// (((((accepter_info *) arg)->port) & 0xff00) >> 8);
// LOG2(ThisModule"Accepter : port %d\n",addr.sin_port);
inet_aton("0.0.0.0", &addr.sin_addr);
opt = 1;
i = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, 4);
LOG2(ThisModule "Accepter : setsockopt %s\n", strerror(errno));
addrlen = sizeof(addr);
bind(s, (struct sockaddr *) & addr, addrlen);
LOG2(ThisModule "Accepter : bind %s\n", strerror(errno));
listen(s, 1);
LOG2(ThisModule "Accepter : listen: %s\n", strerror(errno));
while (1) {
addrlen = sizeof(addr);
LOG1(ThisModule "Accepter : before accept\n");
newfd = (accept(s, (struct sockaddr *) & addr, &addrlen));
LOG2(ThisModule "Accepter: after accept. New fd:%d\n", newfd);
FLUSH;
LOG3(ThisModule "Accepter: PID(%d) client:%s\n", getpid(),
inet_ntoa(addr.sin_addr));
FLUSH;
write(pipefd, &newfd, sizeof(newfd));
}
shutdown(s, 2);
LOG3(ThisModule "Accepter : %d shutdown done. Socket is: %d\n",
getpid(), s);
}
struct abstr {
int fd;
int mask;
};

181
benchmark.c Normal file
View File

@@ -0,0 +1,181 @@
extern int seconds, thirds;
void rBenchmark(daveConnection * dc, int bmArea) {
int i,res,maxReadLen,areaNumber;
double usec;
PDU p;
#ifdef UNIX_STYLE
struct timeval t1, t2;
#endif
#ifdef BCCWIN
clock_t t1, t2;
#endif
seconds=0;thirds=0;
maxReadLen=dc->maxPDUlength-46;
areaNumber=0;
if(bmArea==daveDB) areaNumber=1;
printf("Now going to do read benchmark with minimum block length of 1.\n");
wait();
#ifdef UNIX_STYLE
gettimeofday(&t1, NULL);
#endif
#ifdef BCCWIN
t1=clock();
#endif
for (i=1;i<101;i++) {
daveReadBytes(dc, bmArea, areaNumber,0, 1, NULL);
if (i%10==0) {
printf("...%d",i);
fflush(stdout);
}
}
#ifdef UNIX_STYLE
gettimeofday(&t2, NULL);
usec = 1e6 * (t2.tv_sec - t1.tv_sec) + t2.tv_usec - t1.tv_usec;
usec/=1e6;
#endif
#ifdef BCCWIN
t2=clock();
usec = 0.001*(t2 - t1);
#endif
printf(" 100 reads took %g secs. \n",usec);
printf(" retries: 2nd %d 3rd %d\n",seconds, thirds);
seconds=0;thirds=0;
printf("Now going to do read benchmark with shurely supported block length %d.\n",maxReadLen);
// daveSetDebug(daveDebugAll);
wait();
#ifdef UNIX_STYLE
gettimeofday(&t1, NULL);
#endif
#ifdef BCCWIN
t1=clock();
#endif
for (i=1;i<101;i++) {
daveReadBytes(dc, bmArea, areaNumber, 0, maxReadLen, NULL);
if (i%10==0) {
printf("...%d",i);
fflush(stdout);
}
}
#ifdef UNIX_STYLE
gettimeofday(&t2, NULL);
usec = 1e6 * (t2.tv_sec - t1.tv_sec) + t2.tv_usec - t1.tv_usec;
usec/=1e6;
#endif
#ifdef BCCWIN
t2=clock();
usec = 0.001*(t2 - t1);
#endif
printf(" 100 reads took %g secs. \n",usec);
printf(" retries: 2nd %d 3rd %d\n",seconds, thirds);
wait();
seconds=0;thirds=0;
printf("Now going to do read benchmark with 5 variables in a single request.\n");
printf("running...\n");
#ifdef UNIX_STYLE
gettimeofday(&t1, NULL);
#endif
#ifdef BCCWIN
t1=clock();
#endif
for (i=1;i<101;i++) {
davePrepareReadRequest(dc, &p);
daveAddVarToReadRequest(&p,daveInputs,0,0,6);
daveAddVarToReadRequest(&p,daveFlags,0,0,6);
daveAddVarToReadRequest(&p,daveFlags,0,6,6);
daveAddVarToReadRequest(&p,bmArea,areaNumber,4,54);
daveAddVarToReadRequest(&p,bmArea,areaNumber,4,4);
res=daveExecReadRequest(dc, &p, NULL);
if (res!=0) printf("\nerror %d=%s\n",res,daveStrerror(res));
if (i%10==0) {
printf("...%d",i);
fflush(stdout);
}
}
#ifdef UNIX_STYLE
gettimeofday(&t2, NULL);
usec = 1e6 * (t2.tv_sec - t1.tv_sec) + t2.tv_usec - t1.tv_usec;
usec/=1e6;
#endif
#ifdef BCCWIN
t2=clock();
usec = 0.001*(t2 - t1);
#endif
printf(" 100 reads took %g secs.\n",usec);
printf(" retries: 2nd %d 3rd %d\n",seconds, thirds);
}
void wBenchmark(daveConnection * dc,int bmArea) {
int i, c, areaNumber, maxWriteLen;
double usec;
#ifdef UNIX_STYLE
struct timeval t1, t2;
#endif
#ifdef BCCWIN
clock_t t1, t2;
#endif
maxWriteLen=dc->maxPDUlength-28;
areaNumber=0;
if(bmArea==daveDB) areaNumber=1;
printf("Now going to do write benchmark with minimum block length of 1.\n");
wait();
#ifdef UNIX_STYLE
gettimeofday(&t1, NULL);
#endif
#ifdef BCCWIN
t1=clock();
#endif
for (i=1;i<101;i++) {
daveWriteBytes(dc, bmArea, areaNumber,0,1,&c);
if (i%10==0) {
printf("...%d",i);
fflush(stdout);
}
}
#ifdef UNIX_STYLE
gettimeofday(&t2, NULL);
usec = 1e6 * (t2.tv_sec - t1.tv_sec) + t2.tv_usec - t1.tv_usec;
usec/=1e6;
#endif
#ifdef BCCWIN
t2=clock();
usec = 0.001*(t2 - t1);
#endif
printf(" 100 writes took %g secs. \n",usec);
printf("Now going to do write benchmark with shurely supported block length %d.\n",maxWriteLen);
wait();
#ifdef UNIX_STYLE
gettimeofday(&t1, NULL);
#endif
#ifdef BCCWIN
t1=clock();
#endif
for (i=1;i<101;i++) {
daveWriteBytes(dc, bmArea, areaNumber, 0, maxWriteLen, &c);
if (i%10==0) {
printf("...%d",i);
fflush(stdout);
}
}
#ifdef UNIX_STYLE
gettimeofday(&t2, NULL);
usec = 1e6 * (t2.tv_sec - t1.tv_sec) + t2.tv_usec - t1.tv_usec;
usec/=1e6;
#endif
#ifdef BCCWIN
t2=clock();
usec = 0.001*(t2 - t1);
#endif
printf(" 100 writes took %g secs. \n",usec);
wait();
}
/*
Changes:
03/18/2005 introduced bmArea parameter. S7-200 doesn't have enough Merkers/Flags for long
block benchmarks. Must use V memory. This is like DB1 of S7-300/400, but a CPU
may have a DB1 or not depending on program. So in case of 300/400 use Merker/Flag
memory.
*/

BIN
contributions/LotTrack-103.zip Executable file

Binary file not shown.

454
contributions/PowerBasic/Dave.bas Executable file
View File

@@ -0,0 +1,454 @@
#PBFORMS CREATED V1.51
'--------------------------------------------------------------------------------
' Dave.bas 4/08/06
' Test of LibNoDave Step7 MPI communication with PBWin 8.01
' credit for all the great code goes to:
' (C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2005 '
'
' PBWin version writen by Bob Clarke RT Engineering Larvik Norway (rtc@rte.no)
' some variable and function names have been changed to avoid conflicts with PBW keywords
' all the errors in the translation are surely mine.
'--------------------------------------------------------------------------------
#COMPILE EXE ' make an exe file
#DIM ALL ' check all variables and parameters
#COMPILER PBWIN 8.01 ' version used for test
' PBForms 1.51
'------------------------------------------------------------------------------
' ** Includes **
'------------------------------------------------------------------------------
#PBFORMS BEGIN INCLUDES
#RESOURCE "Dave.pbr"
#IF NOT %DEF(%WINAPI)
#INCLUDE "WIN32API.INC"
#ENDIF
#PBFORMS END INCLUDES
#IF NOT %DEF(%DAVE_INC)
#INCLUDE "DAVE.INC"
#ENDIF
'------------------------------------------------------------------------------
'------------------------------------------------------------------------------
' ** Constants **
'------------------------------------------------------------------------------
#PBFORMS BEGIN CONSTANTS
%IDC_QUIT = 2
%IDB_READ = 501
%IDB_ADD = 502
%IDB_Write = 503
%IDC_Status = 601
%IDC_TEXTBOX1 = 701
%IDC_TEXTBOX2 = 702
%IDC_TEXTBOX3 = 703
%IDC_TEXTBOX4 = 704
%IDD_DIALOG1 = 1001
#PBFORMS END CONSTANTS
'------------------------------------------------------------------------------
' ** GLOBALS **
'--------------------------------------------------------------------------------
GLOBAL sDato AS STRING ' PC date
GLOBAL sTid AS STRING ' PC time
GLOBAL gMhDlg AS LONG ' window handle
'------------------------------------------------------------------------------
' ** Declarations **
'------------------------------------------------------------------------------
DECLARE CALLBACK FUNCTION MainCallBackProc()
DECLARE FUNCTION ShowMainDialog(BYVAL hParent AS DWORD) AS LONG
DECLARE FUNCTION Init1() AS LONG
DECLARE FUNCTION Init2() AS LONG
DECLARE FUNCTION Initialize(BYREF ph AS LONG, BYREF di AS LONG, BYREF DC AS LONG) AS LONG
DECLARE SUB GameOver(BYREF ph AS LONG, BYREF di AS LONG, BYREF DC AS LONG)
DECLARE SUB ReadScreen()
DECLARE SUB WriteScreen()
DECLARE SUB ReadFromPLC()
DECLARE SUB addvalue()
DECLARE SUB writeToPLC()
#PBFORMS DECLARATIONS
'------------------------------------------------------------------------------
'------------------------------------------------------------------------------
' ** Main Application Entry Point **
'------------------------------------------------------------------------------
FUNCTION PBMAIN()
DIM lRslt AS LOCAL LONG
Init1()
ShowMainDialog %HWND_DESKTOP
Init2()
DIALOG SHOW MODAL gMhDlg, CALL MainCallBackProc TO lRslt
END FUNCTION
'------------------------------------------------------------------------------
'------------------------------------------------------------------------------
FUNCTION Init1() AS LONG
' before show dialog
DIM localMPI AS GLOBAL INTEGER
DIM plcMPI AS GLOBAL INTEGER
DIM Rack AS GLOBAL INTEGER
DIM Slot AS GLOBAL INTEGER
DIM ph AS GLOBAL LONG ' port handle
DIM di AS GLOBAL LONG ' dave interface handle
DIM DC AS GLOBAL LONG ' dave connection handle
DIM sBaud AS GLOBAL STRING
DIM sPort AS GLOBAL STRING
DIM sParity AS GLOBAL STRING
DIM sPeer AS GLOBAL STRING
DIM sAcspnt AS GLOBAL STRING
DIM sT AS GLOBAL STRING
DIM ok AS GLOBAL LONG
DIM v1 AS GLOBAL LONG
DIM v2 AS GLOBAL LONG
DIM v3 AS GLOBAL LONG
DIM v4 AS GLOBAL SINGLE
DIM v5 AS GLOBAL SINGLE
localMPI = 0
plcMPI = 2
sPort = "COM1"
sBaud = "38400"
sParity = "O"
sPeer = "192.168.1.1"
sAcspnt = "/S7ONLINE"
FUNCTION = 0
END FUNCTION 'Init1
'-----------------------------------------------------------------------------------------------
'------------------------------------------------------------------------------
' ** CallBacks **
'------------------------------------------------------------------------------
CALLBACK FUNCTION MainCallBackProc()
SELECT CASE AS LONG CBMSG
CASE %WM_INITDIALOG
' Initialization handler
CASE %WM_NCACTIVATE
STATIC hWndSaveFocus AS DWORD
IF ISFALSE CBWPARAM THEN
' Save control focus
hWndSaveFocus = GetFocus()
ELSEIF hWndSaveFocus THEN
' Restore control focus
SetFocus(hWndSaveFocus)
hWndSaveFocus = 0
END IF
CASE %WM_COMMAND
' Process control notifications
SELECT CASE AS LONG CBCTL
CASE %IDC_TEXTBOX1 ' MD0 field
IF CBCTLMSG = %EN_KILLFOCUS THEN
SELECT CASE CBCTL
CASE %IDC_TEXTBOX1
' read display
END SELECT 'CBCTL
END IF 'CBCTLMSG = %EN_KILLFOCUS
CASE %IDC_TEXTBOX2 ' MD4 field
IF CBCTLMSG = %EN_KILLFOCUS THEN
SELECT CASE CBCTL
CASE %IDC_TEXTBOX2
' read display
END SELECT 'CBCTL
END IF 'CBCTLMSG = %EN_KILLFOCUS
CASE %IDC_TEXTBOX3 ' MD8 field
IF CBCTLMSG = %EN_KILLFOCUS THEN
SELECT CASE CBCTL
CASE %IDC_TEXTBOX3
' read display
END SELECT 'CBCTL
END IF 'CBCTLMSG = %EN_KILLFOCUS
CASE %IDC_TEXTBOX4 ' MD12 field
IF CBCTLMSG = %EN_KILLFOCUS THEN
SELECT CASE CBCTL
CASE %IDC_TEXTBOX4
' read display
END SELECT 'CBCTL
END IF 'CBCTLMSG = %EN_KILLFOCUS
CASE %IDB_READ ' Read button
IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
readFromPLC()
END IF
CASE %IDB_ADD ' Add button
IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
addvalue()
END IF
CASE %IDB_WRITE ' Write button
IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
writeToPLC()
END IF
CASE %IDC_QUIT ' Quit button
IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
CONTROL DISABLE gMhDlg, %IDC_QUIT
CALL GameOver(ph, di, DC)
DIALOG END CBHNDL
END IF
END SELECT
END SELECT
END FUNCTION
'------------------------------------------------------------------------------
'------------------------------------------------------------------------------
' ** Dialogs **
'------------------------------------------------------------------------------
FUNCTION ShowMainDialog(BYVAL hParent AS DWORD) AS LONG
DIM lRslt AS LOCAL LONG
#PBFORMS BEGIN DIALOG %IDD_DIALOG1->->
LOCAL hDlg AS DWORD
DIALOG NEW hParent, "LibNoDave", 70, 70, 261, 130, %WS_POPUP OR _
%WS_BORDER OR %WS_DLGFRAME OR %WS_SYSMENU OR %WS_CLIPSIBLINGS OR _
%WS_VISIBLE OR %DS_MODALFRAME OR %DS_3DLOOK OR %DS_NOFAILCREATE OR _
%DS_SETFONT, %WS_EX_WINDOWEDGE OR %WS_EX_CONTROLPARENT OR _
%WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR, TO hDlg
CONTROL ADD BUTTON, hDlg, %IDC_QUIT, "QUIT", 200, 95, 30, 15
CONTROL ADD BUTTON, hDlg, %IDB_READ, "Read Flags", 55, 70, 40, 15
CONTROL ADD BUTTON, hDlg, %IDB_ADD, "Add 1", 110, 70, 40, 15
CONTROL ADD BUTTON, hDlg, %IDB_Write, "Write Flags", 165, 70, 40, 15
CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX1, "FD 0", 25, 40, 40, 15
CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX2, "FD 4", 80, 40, 40, 15
CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX3, "FD 8", 135, 40, 40, 15
CONTROL ADD TEXTBOX, hDlg, %IDC_TEXTBOX4, "FD 12", 190, 40, 40, 15
CONTROL ADD LABEL, hDlg, %IDC_Status, "Status", 25, 10, 205, 10, _
%WS_CHILD OR %WS_VISIBLE OR %SS_CENTER, %WS_EX_LEFT OR _
%WS_EX_LTRREADING
CONTROL SET COLOR hDlg, %IDC_Status, %BLUE, -1
#PBFORMS END DIALOG
#PBFORMS BEGIN CLEANUP %IDD_DIALOG1
#PBFORMS END CLEANUP
' Save the handle for global access
gMhDlg = hDlg
FUNCTION = lRslt
END FUNCTION
'------------------------------------------------------------------------------
'-----------------------------------------------------------------------------------------------
FUNCTION Init2() AS LONG
' after show dialog
Initialize(ph, di, DC)
IF ok = %False THEN
CONTROL DISABLE gMhDlg, %IDB_READ
CONTROL DISABLE gMhDlg, %IDB_WRITE
END IF 'ok
FUNCTION = ok
END FUNCTION
'-----------------------------------------------------------------------------------------------
'-----------------------------------------------------------------------------------------------
' This initialization is used in all test programs. In a real program, where you would
' want to read again and again, keep the dc and di until your program terminates.
'
FUNCTION Initialize(BYREF ph AS LONG, BYREF di AS LONG, BYREF DC AS LONG) AS LONG
DIM lvar AS LOCAL LONG
DIM lRslt AS LOCAL LONG
ok = %False ' no connection
ph = 0 ' port handle
di = 0 ' dave interface handle
DC = 0 ' dave connection handle
REM uncomment the daveSetDebug... LINE, SAVE your sheet
REM run excel FROM dos BOX WITH: excel yoursheet >debugout.txt
REM SEND me the file debugout.txt IF you have trouble.
REM CALL daveSetDebug(daveDebugAll)
ph = openPort(sPort, sBaud$, ASC(LEFT$(sParity$, 1)))
' Alternatives:
REM ph = openSocket(102, peer$) ' for ISO over TCP
REM ph = openSocket(1099, peer$)' for IBH NetLink
REM ph = openS7online(acspnt$) ' to use Siemes libraries for transport (s7online)
IF (ph > 0) THEN
sT = "port handle ok: " + STR$(ph)
CONTROL SET TEXT gMhDlg, %IDC_STATUS, sT
di = daveNewInterface(ph, ph, "IF1", 0, %daveProtoMPI, %daveSpeed187k)
' Alternatives:
REM di = daveNewInterface(ph, ph, "IF1", 0, daveProtoPPI, daveSpeed187k)
REM di = daveNewInterface(ph, ph, "IF1", 0, daveProtoMPI_IBH, daveSpeed187k)
REM di = daveNewInterface(ph, ph, "IF1", 0, daveProtoISOTCP, daveSpeed187k)
REM di = daveNewInterface(ph, ph, "IF1", 0, daveProtoS7online, daveSpeed187k)
'
'You can set longer timeout here, if you have a slow connection
' Call daveSetTimeout(di, 500000)
lRslt = daveInitAdapter(di)
IF lRslt = 0 THEN
sT = "result from initAdapter ok: " + STR$(lRslt)
CONTROL SET TEXT gMhDlg, %IDC_STATUS, sT
'
' with ISO over TCP, set correct values for rack and slot of the CPU
'
DC = daveNewConnection(di, plcMPI, Rack, Slot)
lRslt = daveConnectPLC(DC)
IF lRslt = 0 THEN
sT = "PLC connection ok: "
CONTROL SET TEXT gMhDlg, %IDC_STATUS, sT
ok = %True
ELSE
ok = %False
sT = "Error No PLC connection: " + STR$(lRslt)
CONTROL SET TEXT gMhDlg, %IDC_STATUS, sT
EXIT FUNCTION
END IF 'lRslt = daveConnectPLC
ELSE
sT = "Error initAdapter: " + STR$(lRslt)
CONTROL SET TEXT gMhDlg, %IDC_STATUS, sT
EXIT FUNCTION
END IF 'lRslt = daveInitAdapter
ELSE
ok = %False
sT = "Error port handle: " + STR$(ph)
CONTROL SET TEXT gMhDlg, %IDC_STATUS, sT
END IF '(ph > 0)
FUNCTION = ok
END FUNCTION 'Initialize
'-----------------------------------------------------------------------------------------------
'-----------------------------------------------------------------------------------------------
' Disconnect from PLC, disconnect from Adapter, close the serial interface or TCP/IP socket
SUB GameOver(BYREF ph AS LONG, BYREF di AS LONG, BYREF DC AS LONG)
DIM lvar AS LOCAL LONG
DIM lRslt AS LOCAL LONG
sT = "disconnecting from PLC"
CONTROL SET TEXT gMhDlg, %IDC_STATUS, sT
IF DC <> 0 THEN
lRslt = daveDisconnectPLC(DC)
CALL daveFree(DC)
DC = 0
END IF
IF di <> 0 THEN
lRslt = daveDisconnectAdapter(di)
CALL daveFree(di)
di = 0
END IF
IF ph <> 0 THEN
lRslt = closePort(ph)
ph = 0
END IF
ok = %False
END SUB 'GameOver
'-----------------------------------------------------------------------------------------------
'-----------------------------------------------------------------------------------------------
' read values from textboxes
SUB ReadScreen
DIM txt AS LOCAL STRING
' read screen values
CONTROL GET TEXT gMhdlg, %IDC_TEXTBOX1 TO txt$
v1 = VAL(txt$)
CONTROL GET TEXT gMhdlg, %IDC_TEXTBOX2 TO txt$
v2 = VAL(txt$)
CONTROL GET TEXT gMhdlg, %IDC_TEXTBOX3 TO txt$
v3 = VAL(txt$)
CONTROL GET TEXT gMhdlg, %IDC_TEXTBOX4 TO txt$
v4 = VAL(txt$)
END SUB 'ReadScreen
'-----------------------------------------------------------------------------------------------
'-----------------------------------------------------------------------------------------------
' write values to textboxes
SUB WriteScreen
DIM txt AS LOCAL STRING
' write values to screen
txt$ = FORMAT$(v1,"#####") 'show MD0
CONTROL SET TEXT gMhDlg, %IDC_TEXTBOX1, txt$
txt$ = FORMAT$(v2,"#####") 'show MD4
CONTROL SET TEXT gMhDlg, %IDC_TEXTBOX2, txt$
txt$ = FORMAT$(v3,"#####") 'show MD8
CONTROL SET TEXT gMhDlg, %IDC_TEXTBOX3, txt$
txt$ = FORMAT$(v4,"#####.##") 'show MD12
CONTROL SET TEXT gMhDlg, %IDC_TEXTBOX4, txt$
END SUB 'WriteScreen
'-----------------------------------------------------------------------------------------------
'-----------------------------------------------------------------------------------------------
' read some values from FD0,FD4,FD8,FD12 (MD0,MD4,MD8,MD12 in german notation)
' to read from data block 12, you would need to write:
' daveReadBytes(dc, daveDB, 12, 0, 16, 0)
SUB ReadFromPLC()
DIM lRslt AS LOCAL LONG
DIM e AS LOCAL STRING
IF ok = %True THEN
sT = "Testing PLC read "
CONTROL SET TEXT gMhDlg, %IDC_STATUS, sT
lRslt = daveReadBytes(DC, %daveFlags, 0, 0, 16, 0)
IF lRslt = 0 THEN
v1 = daveGetS32(DC) 'MD0(DINT)
v2 = daveGetS32(DC) 'MD4(DINT)
v3 = daveGetS32(DC) 'MD8(DINT)
v4 = daveGetFloat(DC) 'MD12(REAL)
v5 = daveGetFloatAt(DC, 12)
WriteScreen()
ELSE
e$ = daveStrError(lRslt)
sT = " error: " + STR$(lRslt) + e$
CONTROL SET TEXT gMhDlg, %IDC_STATUS, sT
END IF
END IF
END SUB 'ReadFromPLC
'-----------------------------------------------------------------------------------------------
'-----------------------------------------------------------------------------------------------
SUB addvalue()
sT = " add 1 "
CONTROL SET TEXT gMhDlg, %IDC_STATUS, sT
ReadScreen()
v1 = v1 +1
v2 = v2 +2
v3 = v3 +3
v4 = v4 +1.1
WriteScreen()
END SUB 'addvalue
'-----------------------------------------------------------------------------------------------
'-----------------------------------------------------------------------------------------------
' read values from textboxes and
' write values to FD0,FD4,FD8,FD12 (MD0,MD4,MD8,MD12 in german notation)
SUB writeToPLC()
DIM buffer(1024) AS BYTE
DIM lRslt AS LOCAL LONG
DIM e AS LOCAL STRING
IF ok = %True THEN
sT = "Testing PLC write "
CONTROL SET TEXT gMhDlg, %IDC_STATUS, sT
ReadScreen()
' Here we put thre DINTs and a REAL into the buffer. davePutXXX does the necessary conversions.
' The resulting byte pattern in the buffer is the same you would get, when you watch the PLC
' memory (FB0 .. FB15) as bytes
'
lRslt = davePut32(buffer(0), v1)
lRslt = davePut32(buffer(4), v2)
lRslt = davePut32(buffer(8), v3)
lRslt = davePutFloat(buffer(12), v4)
lRslt = daveWriteBytes(DC, %daveFlags, 0, 0, 16, buffer(0))
IF lRslt <> 0 THEN
e$ = daveStrError(lRslt)
sT = " error: " + STR$(lRslt) + e$
CONTROL SET TEXT gMhDlg, %IDC_STATUS, sT
END IF
END IF
END SUB 'writeToPLC()
'-----------------------------------------------------------------------------------------------

BIN
contributions/PowerBasic/Dave.exe Executable file

Binary file not shown.

735
contributions/PowerBasic/Dave.inc Executable file
View File

@@ -0,0 +1,735 @@
'--------------------------------------------------------------------------------
' Dave.inc 4/08/06
' Test of LibNoDave Step7 MPI communication with PBWin 8.01
' credit for all the great code goes to:
' (C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2005 '
'
' PBWin version writen by Bob Clarke RT Engineering Larvik Norway (rtc@rte.no)
' some variable and function names have been changed to avoid conflicts with PBW keywords
' all the errors in the translation are surely mine.
'--------------------------------------------------------------------------------
'--------------------------------------------------------------------------------
#IF NOT %DEF(%WINAPI)
#INCLUDE "WIN32API.INC"
#ENDIF
#IF NOT %DEF(%COMMCTRL_INC)
#INCLUDE "COMMCTRL.INC"
#ENDIF
#IF NOT %DEF(%PBForms_INC)
#INCLUDE "PBForms.INC"
#ENDIF
#IF NOT %DEF(%COMDLG32_INC)
#INCLUDE "COMDLG32.INC"
#ENDIF
#IF NOT %DEF(%Dave_INC)
%DAVE_INC = 1
'--------------------------------------------------------------------------------
' Declarations
'--------------------------------------------------------------------------------
' Set and read debug level:
'
DECLARE SUB daveSetDebug LIB "libnodave.dll" (BYVAL level AS LONG)
DECLARE FUNCTION daveGetDebug LIB "libnodave.dll" () AS LONG
'
' You may wonder what sense it might make to set debug level, as you cannot see
' messages when you opened excel or some VB application from Windows GUI.
' You can invoke Excel from the console or from a batch file with:
' <myPathToExcel>\Excel.Exe <MyPathToXLS-File>VBATest.XLS >ExcelOut
' This will start Excel with VBATest.XLS and all debug messages (and a few from Excel itself)
' go into the file ExcelOut.
'
' Error code to message string conversion:
' Call this function to get an explanation for error codes returned by other functions.
'
'
' The folowing doesn't work properly. A VB string is something different from a pointer to char:
'
' Declare Function daveStrerror Lib "libnodave.dll" Alias "daveStrerror" (ByVal en As Long) As String
'
DECLARE FUNCTION daveInternalStrerror LIB "libnodave.dll" ALIAS "daveStrerror" (BYVAL en AS LONG) AS LONG
' So, I added another function to libnodave wich copies the text into a VB String.
' This function is still not useful without some code araound it, so I call it "internal"
DECLARE SUB daveStringCopy LIB "libnodave.dll" ALIAS "daveStringCopy" (BYVAL internalPointer AS LONG, BYVAL s AS STRING)
'
' Setup a new interface structure using a handle to an open port or socket:
'
DECLARE FUNCTION daveNewInterface LIB "libnodave.dll" ALIAS "daveNewInterface"(BYVAL fd1 AS LONG, BYVAL fd2 AS LONG, BYVAL sname AS STRING, BYVAL localMPI AS LONG, BYVAL protocol AS LONG, BYVAL speed AS LONG) AS LONG
'
' Setup a new connection structure using an initialized daveInterface and PLC's MPI address.
' Note: The parameter di must have been obtained from daveNewinterface.
'
DECLARE FUNCTION daveNewConnection LIB "libnodave.dll" ALIAS "daveNewConnection" (BYVAL di AS LONG, BYVAL mpi AS LONG, BYVAL Rack AS LONG, BYVAL Slot AS LONG) AS LONG
'
' PDU handling:
' PDU is the central structure present in S7 communication.
' It is composed of a 10 or 12 byte header,a parameter block and a data block.
' When reading or writing values, the data field is itself composed of a data
' header followed by payload data
'
' retrieve the answer:
' Note: The parameter dc must have been obtained from daveNewConnection.
'
DECLARE FUNCTION daveGetResponse LIB "libnodave.dll" ALIAS "daveGetResponse" (BYVAL DC AS LONG) AS LONG
'
' send PDU to PLC
' Note: The parameter dc must have been obtained from daveNewConnection,
' The parameter pdu must have been obtained from daveNewPDU.
'
DECLARE FUNCTION daveSendMessage LIB "libnodave.dll" ALIAS "daveSendMessage" (BYVAL DC AS LONG, BYVAL pdu AS LONG) AS LONG
'******
'
'Utilities:
'
'****
'*
' Hex dump PDU:
'
DECLARE SUB daveDumpPDU LIB "libnodave.dll" ALIAS "daveDumpPDU" (BYVAL pdu AS LONG)
'
' Hex dump. Write the name followed by len bytes written in hex and a newline:
'
DECLARE SUB daveDump LIB "libnodave.dll" ALIAS "daveDump" (BYVAL sname AS STRING, BYVAL pdu AS LONG, BYVAL length AS LONG)
'
' names for PLC objects. This is again the intenal function. Use the wrapper code below.
'
DECLARE FUNCTION daveInternalAreaName LIB "libnodave.dll" ALIAS "daveAreaName" (BYVAL en AS LONG) AS LONG
DECLARE FUNCTION daveInternalBlockName LIB "libnodave.dll" ALIAS "daveBlockName" (BYVAL en AS LONG) AS LONG
'
' swap functions. They change the byte order, if byte order on the computer differs from
' PLC byte order:
'
DECLARE FUNCTION daveSwapIed_16 LIB "libnodave.dll" ALIAS "daveSwapIed_16" (BYVAL x AS LONG) AS LONG
DECLARE FUNCTION daveSwapIed_32 LIB "libnodave.dll" ALIAS "daveSwapIed_32" (BYVAL x AS LONG) AS LONG
'
' Data conversion convenience functions. The older set has been removed.
' 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:
'
DECLARE FUNCTION toPLCfloat LIB "libnodave.dll" ALIAS "toPLCfloat" (BYVAL f AS SINGLE) AS SINGLE
DECLARE FUNCTION daveToPLCfloat LIB "libnodave.dll" ALIAS "daveToPLCfloat" (BYVAL f AS SINGLE) AS LONG
'
' Copy and convert value of 8,16,or 32 bit, signed or unsigned at position pos
' from internal buffer:
'
DECLARE FUNCTION daveGetS8from LIB "libnodave.dll" ALIAS "daveGetS8from" (BYREF buffer AS BYTE) AS LONG
DECLARE FUNCTION daveGetU8from LIB "libnodave.dll" ALIAS "daveGetU8from" (BYREF buffer AS BYTE) AS LONG
DECLARE FUNCTION daveGetS16from LIB "libnodave.dll" ALIAS "daveGetS16from" (BYREF buffer AS BYTE) AS LONG
DECLARE FUNCTION daveGetU16from LIB "libnodave.dll" ALIAS "daveGetU16from" (BYREF buffer AS BYTE) AS LONG
DECLARE FUNCTION daveGetS32from LIB "libnodave.dll" ALIAS "daveGetS32from" (BYREF buffer AS BYTE) AS LONG
'
' Is there an unsigned long? Or a longer integer than long? This doesn't work.
' Declare Function daveGetU32from Lib "libnodave.dll" (ByRef buffer As Byte) As Long
'
DECLARE FUNCTION daveGetFloatfrom LIB "libnodave.dll" ALIAS "daveGetFloatfrom" (BYREF buffer AS BYTE) AS SINGLE
'
' Copy and convert a value of 8,16,or 32 bit, signed or unsigned from internal buffer. These
' functions increment an internal buffer position. This buffer position is set to zero by
' daveReadBytes, daveReadBits, daveReadSZL.
'
DECLARE FUNCTION daveGetS8 LIB "libnodave.dll" ALIAS "daveGetS8" (BYVAL DC AS LONG) AS LONG
DECLARE FUNCTION daveGetU8 LIB "libnodave.dll" ALIAS "daveGetU8" (BYVAL DC AS LONG) AS LONG
DECLARE FUNCTION daveGetS16 LIB "libnodave.dll" ALIAS "daveGetS16" (BYVAL DC AS LONG) AS LONG
DECLARE FUNCTION daveGetU16 LIB "libnodave.dll" ALIAS "daveGetU16" (BYVAL DC AS LONG) AS LONG
DECLARE FUNCTION daveGetS32 LIB "libnodave.dll" ALIAS "daveGetS32" (BYVAL DC AS LONG) AS LONG
'
' Is there an unsigned long? Or a longer integer than long? This doesn't work.
'Declare Function daveGetU32 Lib "libnodave.dll" (ByVal dc As Long) As Long
DECLARE FUNCTION daveGetFloat LIB "libnodave.dll" ALIAS "daveGetFloat" (BYVAL DC AS LONG) AS SINGLE
'
' Read a value of 8,16,or 32 bit, signed or unsigned at position pos from internal buffer:
'
DECLARE FUNCTION daveGetS8At LIB "libnodave.dll" ALIAS "daveGetS8At" (BYVAL DC AS LONG, BYVAL POS AS LONG) AS LONG
DECLARE FUNCTION daveGetU8At LIB "libnodave.dll" ALIAS "daveGetU8At" (BYVAL DC AS LONG, BYVAL POS AS LONG) AS LONG
DECLARE FUNCTION daveGetS16At LIB "libnodave.dll" ALIAS "daveGetS16At" (BYVAL DC AS LONG, BYVAL POS AS LONG) AS LONG
DECLARE FUNCTION daveGetU16At LIB "libnodave.dll" ALIAS "daveGetU16At" (BYVAL DC AS LONG, BYVAL POS AS LONG) AS LONG
DECLARE FUNCTION daveGetS32At LIB "libnodave.dll" ALIAS "daveGetS32At" (BYVAL DC AS LONG, BYVAL POS AS LONG) AS LONG
'
' Is there an unsigned long? Or a longer integer than long? This doesn't work.
'Declare Function daveGetU32At Lib "libnodave.dll" (ByVal dc As Long, ByVal pos As Long) As Long
DECLARE FUNCTION daveGetFloatAt LIB "libnodave.dll" ALIAS "daveGetFloatAt" (BYVAL DC AS LONG, BYVAL POS AS LONG) AS SINGLE
'
' Copy and convert a value of 8,16,or 32 bit, signed or unsigned into a buffer. The buffer
' is usually used by daveWriteBytes, daveWriteBits later.
'
DECLARE FUNCTION davePut8 LIB "libnodave.dll" ALIAS "davePut8" (BYREF buffer AS BYTE, BYVAL value AS LONG) AS LONG
DECLARE FUNCTION davePut16 LIB "libnodave.dll" ALIAS "davePut16" (BYREF buffer AS BYTE, BYVAL value AS LONG) AS LONG
DECLARE FUNCTION davePut32 LIB "libnodave.dll" ALIAS "davePut32" (BYREF buffer AS BYTE, BYVAL value AS LONG) AS LONG
DECLARE FUNCTION davePutFloat LIB "libnodave.dll" ALIAS "davePutFloat" (BYREF buffer AS BYTE, BYVAL value AS SINGLE) AS LONG
'
' Copy and convert a value of 8,16,or 32 bit, signed or unsigned to position pos of a buffer.
' The buffer is usually used by daveWriteBytes, daveWriteBits later.
'
DECLARE FUNCTION davePut8At LIB "libnodave.dll" ALIAS "davePut8At" (BYREF buffer AS BYTE, BYVAL POS AS LONG, BYVAL value AS LONG) AS LONG
DECLARE FUNCTION davePut16At LIB "libnodave.dll" ALIAS "davePut16At" (BYREF buffer AS BYTE, BYVAL POS AS LONG, BYVAL value AS LONG) AS LONG
DECLARE FUNCTION davePut32At LIB "libnodave.dll" ALIAS "davePut32At" (BYREF buffer AS BYTE, BYVAL POS AS LONG, BYVAL value AS LONG) AS LONG
DECLARE FUNCTION davePutFloatAt LIB "libnodave.dll" ALIAS "davePutFloatAt" (BYREF buffer AS BYTE, BYVAL POS AS LONG, BYVAL value AS SINGLE) AS LONG
'
' Takes a timer value and converts it into seconds:
'
DECLARE FUNCTION daveGetSeconds LIB "libnodave.dll" ALIAS "daveGetSeconds" (BYVAL DC AS LONG) AS SINGLE
DECLARE FUNCTION daveGetSecondsAt LIB "libnodave.dll" ALIAS "daveGetSecondsAt" (BYVAL DC AS LONG, BYVAL POS AS LONG) AS SINGLE
'
' Takes a counter value and converts it to integer:
'
DECLARE FUNCTION daveGetCounterValue LIB "libnodave.dll" ALIAS "daveGetCounterValue" (BYVAL DC AS LONG) AS LONG
DECLARE FUNCTION daveGetCounterValueAt LIB "libnodave.dll" ALIAS "daveGetCounterValueAt" (BYVAL DC AS LONG, BYVAL POS AS LONG) AS LONG
'
' Get the order code (MLFB number) from a PLC. Does NOT work with 200 family.
'
DECLARE FUNCTION daveGetOrderCode LIB "libnodave.dll" ALIAS "daveGetOrderCode" (BYVAL en AS LONG, BYREF buffer AS BYTE) AS LONG
'
' Connect to a PLC.
'
DECLARE FUNCTION daveConnectPLC LIB "libnodave.dll" ALIAS "daveConnectPLC" (BYVAL DC AS LONG) AS LONG
'
'
' Read a value or a block of values from PLC.
'
DECLARE FUNCTION daveReadBytes LIB "libnodave.dll" ALIAS "daveReadBytes" (BYVAL DC AS LONG, BYVAL area AS LONG, BYVAL areaNumber AS LONG, BYVAL start AS LONG, BYVAL numBytes AS LONG, BYVAL buffer AS LONG) AS LONG
'
' Read a long block of values from PLC. Long means too long to transport in a single PDU.
'
DECLARE FUNCTION daveManyReadBytes LIB "libnodave.dll" ALIAS "daveManyReadBytes" (BYVAL DC AS LONG, BYVAL area AS LONG, BYVAL areaNumber AS LONG, BYVAL start AS LONG, BYVAL numBytes AS LONG, BYVAL buffer AS LONG) AS LONG
'
' Write a value or a block of values to PLC.
'
DECLARE FUNCTION daveWriteBytes LIB "libnodave.dll" ALIAS "daveWriteBytes" (BYVAL DC AS LONG, BYVAL area AS LONG, BYVAL areaNumber AS LONG, BYVAL start AS LONG, BYVAL numBytes AS LONG, BYREF buffer AS BYTE) AS LONG
'
' Write a long block of values to PLC. Long means too long to transport in a single PDU.
'
DECLARE FUNCTION daveWriteManyBytes LIB "libnodave.dll" ALIAS "daveWriteManyBytes" (BYVAL DC AS LONG, BYVAL area AS LONG, BYVAL areaNumber AS LONG, BYVAL start AS LONG, BYVAL numBytes AS LONG, BYREF buffer AS BYTE) AS LONG
'
' Read a bit from PLC. numBytes must be exactly one with all PLCs tested.
' Start is calculated as 8*byte number+bit number.
'
DECLARE FUNCTION daveReadBits LIB "libnodave.dll" ALIAS "daveReadBits" (BYVAL DC AS LONG, BYVAL area AS LONG, BYVAL areaNumber AS LONG, BYVAL start AS LONG, BYVAL numBytes AS LONG, BYVAL buffer AS LONG) AS LONG
'
' Write a bit to PLC. numBytes must be exactly one with all PLCs tested.
'
DECLARE FUNCTION daveWriteBits LIB "libnodave.dll" ALIAS "daveWriteBits" (BYVAL DC AS LONG, BYVAL area AS LONG, BYVAL areaNumber AS LONG, BYVAL start AS LONG, BYVAL numBytes AS LONG, BYREF buffer AS BYTE) AS LONG
'
' Set a bit in PLC to 1.
'
DECLARE FUNCTION daveSetBit LIB "libnodave.dll" ALIAS "daveSetBit" (BYVAL DC AS LONG, BYVAL area AS LONG, BYVAL areaNumber AS LONG, BYVAL start AS LONG, BYVAL byteAddress AS LONG, BYVAL bitAddress AS LONG) AS LONG
'
' Set a bit in PLC to 0.
'
DECLARE FUNCTION daveClrBit LIB "libnodave.dll" ALIAS "daveClrBit" (BYVAL DC AS LONG, BYVAL area AS LONG, BYVAL areaNumber AS LONG, BYVAL start AS LONG, BYVAL byteAddress AS LONG, BYVAL bitAddress AS LONG) AS LONG
'
' Read a diagnostic list (SZL) from PLC. Does NOT work with 200 family.
'
DECLARE FUNCTION daveReadSZL LIB "libnodave.dll" ALIAS "daveReadSZL" (BYVAL DC AS LONG, BYVAL ID AS LONG, BYVAL index AS LONG, BYREF buffer AS BYTE) AS LONG
'
DECLARE FUNCTION daveListBlocksOfType LIB "libnodave.dll" ALIAS "daveListBlocksOfType" (BYVAL DC AS LONG, BYVAL typ AS LONG, BYREF buffer AS BYTE) AS LONG
DECLARE FUNCTION daveListBlocks LIB "libnodave.dll" ALIAS "daveListBlocks" (BYVAL DC AS LONG, BYREF buffer AS BYTE) AS LONG
DECLARE FUNCTION internalDaveGetBlockInfo LIB "libnodave.dll" ALIAS "daveGetBlockInfo" (BYVAL DC AS LONG, BYREF buffer AS BYTE, BYVAL ltype AS LONG, BYVAL number AS LONG) AS LONG
'
DECLARE FUNCTION daveGetProgramBlock LIB "libnodave.dll" ALIAS "daveGetProgramBlock" (BYVAL DC AS LONG, BYVAL blockType AS LONG, BYVAL number AS LONG, BYREF buffer AS BYTE, BYREF length AS LONG) AS LONG
'
' Start or Stop a PLC:
'
DECLARE FUNCTION daveStart LIB "libnodave.dll" ALIAS "daveStart" (BYVAL DC AS LONG) AS LONG
DECLARE FUNCTION daveStop LIB "libnodave.dll" ALIAS "daveStop" (BYVAL DC AS LONG) AS LONG
'
' Set outputs (digital or analog ones) of an S7-200 that is in stop mode:
'
DECLARE FUNCTION daveForce200 LIB "libnodave.dll" ALIAS "daveForce200" (BYVAL DC AS LONG, BYVAL area AS LONG, BYVAL start AS LONG, BYVAL value AS LONG) AS LONG
'
' Initialize a multivariable read request.
' The parameter PDU must have been obtained from daveNew PDU:
'
DECLARE SUB davePrepareReadRequest LIB "libnodave.dll" ALIAS "davePrepareReadRequest" (BYVAL DC AS LONG, BYVAL pdu AS LONG)
'
' Add a new variable to a prepared request:
'
DECLARE SUB daveAddVarToReadRequest LIB "libnodave.dll" ALIAS "daveAddVarToReadRequest" (BYVAL pdu AS LONG, BYVAL area AS LONG, BYVAL areaNumber AS LONG, BYVAL start AS LONG, BYVAL numBytes AS LONG)
'
' Executes the entire request:
'
DECLARE FUNCTION daveExecReadRequest LIB "libnodave.dll" ALIAS "daveExecReadRequest" (BYVAL DC AS LONG, BYVAL pdu AS LONG, BYVAL rs AS LONG) AS LONG
'
' Use the n-th result. This lets the functions daveGet<data type> work on that part of the
' internal buffer that contains the n-th result:
'
DECLARE FUNCTION daveUseResult LIB "libnodave.dll" ALIAS "daveUseResult" (BYVAL DC AS LONG, BYVAL rs AS LONG, BYVAL resultNumber AS LONG) AS LONG
'
' Frees the memory occupied by single results in the result structure. After that, you can reuse
' the resultSet in another call to daveExecReadRequest.
'
DECLARE SUB daveFreeResults LIB "libnodave.dll" ALIAS "daveFreeResults" (BYVAL rs AS LONG)
'
' Adds a new bit variable to a prepared request. As with daveReadBits, numBytes must be one for
' all tested PLCs.
'
DECLARE SUB daveAddBitVarToReadRequest LIB "libnodave.dll" ALIAS "daveAddBitVarToReadRequest" (BYVAL pdu AS LONG, BYVAL area AS LONG, BYVAL areaNumber AS LONG, BYVAL start AS LONG, BYVAL numBytes AS LONG)
'
' Initialize a multivariable write request.
' The parameter PDU must have been obtained from daveNew PDU:
'
DECLARE SUB davePrepareWriteRequest LIB "libnodave.dll" ALIAS "davePrepareWriteRequest" (BYVAL DC AS LONG, BYVAL pdu AS LONG)
'
' Add a new variable to a prepared write request:
'
DECLARE SUB daveAddVarToWriteRequest LIB "libnodave.dll" ALIAS "daveAddVarToWriteRequest" (BYVAL pdu AS LONG, BYVAL area AS LONG, BYVAL areaNumber AS LONG, BYVAL start AS LONG, BYVAL numBytes AS LONG, BYREF buffer AS BYTE)
'
' Add a new bit variable to a prepared write request:
'
DECLARE SUB daveAddBitVarToWriteRequest LIB "libnodave.dll" ALIAS "daveAddBitVarToWriteRequest" (BYVAL pdu AS LONG, BYVAL area AS LONG, BYVAL areaNumber AS LONG, BYVAL start AS LONG, BYVAL numBytes AS LONG, BYREF buffer AS BYTE)
'
' Execute the entire write request:
'
DECLARE FUNCTION daveExecWriteRequest LIB "libnodave.dll" ALIAS "daveExecWriteRequest" (BYVAL DC AS LONG, BYVAL pdu AS LONG, BYVAL rs AS LONG) AS LONG
'
' Initialize an MPI Adapter or NetLink Ethernet MPI gateway.
' While some protocols do not need this, I recommend to allways use it. It will do nothing if
' the protocol doesn't need it. But you can change protocols without changing your program code.
'
DECLARE FUNCTION daveInitAdapter LIB "libnodave.dll" ALIAS "daveInitAdapter" (BYVAL di AS LONG) AS LONG
'
' Disconnect from a PLC. While some protocols do not need this, I recommend to allways use it.
' It will do nothing if the protocol doesn't need it. But you can change protocols without
' changing your program code.
'
DECLARE FUNCTION daveDisconnectPLC LIB "libnodave.dll" ALIAS "daveDisconnectPLC" (BYVAL DC AS LONG) AS LONG
'
'
' Disconnect from an MPI Adapter or NetLink Ethernet MPI gateway.
' While some protocols do not need this, I recommend to allways use it.
' It will do nothing if the protocol doesn't need it. But you can change protocols without
' changing your program code.
'
DECLARE FUNCTION daveDisconnectAdapter LIB "libnodave.dll" ALIAS "daveDisconnectAdapter" (BYVAL DC AS LONG) AS LONG
'
'
' List nodes on an MPI or Profibus Network:
'
DECLARE FUNCTION daveListReachablePartners LIB "libnodave.dll" ALIAS "daveListReachablePartners" (BYVAL DC AS LONG, BYREF buffer AS BYTE) AS LONG
'
'
' Set/change the timeout for an interface:
'
DECLARE SUB daveSetTimeout LIB "libnodave.dll" ALIAS "daveSetTimeout" (BYVAL di AS LONG, BYVAL maxTime AS LONG)
'
' Read the timeout setting for an interface:
'
DECLARE FUNCTION daveGetTimeout LIB "libnodave.dll" ALIAS "daveGetTimeout" (BYVAL di AS LONG) AS LONG
'
' Get the name of an interface. Do NOT use this, but the wrapper function defined below!
'
DECLARE FUNCTION daveInternalGetName LIB "libnodave.dll" ALIAS "daveGetName" (BYVAL en AS LONG) AS LONG
'
' Get the MPI address of a connection.
'
DECLARE FUNCTION daveGetMPIAdr LIB "libnodave.dll" ALIAS "daveGetMPIAdr" (BYVAL DC AS LONG) AS LONG
'
' Get the length (in bytes) of the last data received on a connection.
'
DECLARE FUNCTION daveGetAnswLen LIB "libnodave.dll" ALIAS "daveGetAnswLen" (BYVAL DC AS LONG) AS LONG
'
' Get the maximum length of a communication packet (PDU).
' This value depends on your CPU and connection type. It is negociated in daveConnectPLC.
' A simple read can read MaxPDULen-18 bytes.
'
DECLARE FUNCTION daveGetMaxPDULen LIB "libnodave.dll" ALIAS "daveGetMaxPDULen" (BYVAL DC AS LONG) AS LONG
'
' Reserve memory for a resultSet and get a handle to it:
'
DECLARE FUNCTION daveNewResultSet LIB "libnodave.dll" ALIAS "daveNewResultSet"() AS LONG
'
' Destroy handles to daveInterface, daveConnections, PDUs and resultSets
' Free the memory reserved for them.
'
DECLARE SUB daveFree LIB "libnodave.dll" ALIAS "daveFree" (BYVAL item AS LONG)
'
' Reserve memory for a PDU and get a handle to it:
'
DECLARE FUNCTION daveNewPDU LIB "libnodave.dll" ALIAS "daveNewPDU" () AS LONG
'
' Get the error code of the n-th single result in a result set:
'
DECLARE FUNCTION daveGetErrorOfResult LIB "libnodave.dll" ALIAS "daveGetErrorOfResult" (BYVAL resultSet AS LONG, BYVAL resultNumber AS LONG) AS LONG
'
DECLARE FUNCTION daveForceDisconnectIBH LIB "libnodave.dll" ALIAS "daveForceDisconnectIBH" (BYVAL di AS LONG, BYVAL src AS LONG, BYVAL dest AS LONG, BYVAL mpi AS LONG) AS LONG
'
' Helper functions to open serial ports and IP connections. You can use others if you want and
' pass their results to daveNewInterface.
'
' Open a serial port using name, baud rate and parity. Everything else is set automatically:
'
DECLARE FUNCTION openPort LIB "libnodave.dll" ALIAS "setPort" (BYVAL sportname AS STRING, BYVAL sbaudrate AS STRING, BYVAL bparity AS BYTE) AS LONG
'
' Open a TCP/IP connection using port number (1099 for NetLink, 102 for ISO over TCP) and
' IP address. You must use an IP address, NOT a hostname!
'
DECLARE FUNCTION openSocket LIB "libnodave.dll" ALIAS "openSocket" (BYVAL lport AS LONG, BYVAL peer AS STRING) AS LONG
'
' Open an access oint. This is a name in you can add in the "set Programmer/PLC interface" dialog.
' To the access point, you can assign an interface like MPI adapter, CP511 etc.
'
DECLARE FUNCTION openS7online LIB "libnodave.dll" ALIAS "openS7online" (BYVAL peer AS STRING) AS LONG
'
' Close connections and serial ports opened with above functions:
'
DECLARE FUNCTION closePort LIB "libnodave.dll" ALIAS "closePort" (BYVAL fh AS LONG) AS LONG
'
' Close handle opende by opens7online:
'
DECLARE FUNCTION closeS7online LIB "libnodave.dll" ALIAS "closeS7online" (BYVAL fh AS LONG) AS LONG
'
' Read Clock time from PLC:
'
DECLARE FUNCTION daveReadPLCTime LIB "libnodave.dll" ALIAS "daveReadPLCTime" (BYVAL DC AS LONG) AS LONG
'
' set clock to a value given by user
'
DECLARE FUNCTION daveSetPLCTime LIB "libnodave.dll" ALIAS "daveSetPLCTime" (BYVAL DC AS LONG, BYREF timestamp AS BYTE) AS LONG
'
' set clock to PC system clock:
'
DECLARE FUNCTION daveSetPLCTimeToSystime LIB "libnodave.dll" ALIAS "daveSetPLCTimeToSystime" (BYVAL DC AS LONG) AS LONG
'
' BCD conversions:
'
DECLARE FUNCTION daveToBCD LIB "libnodave.dll" ALIAS "daveToBCD" (BYVAL DC AS LONG) AS LONG
DECLARE FUNCTION daveFromBCD LIB "libnodave.dll" ALIAS "daveFromBCD" (BYVAL DC AS LONG) AS LONG
'
' Here comes the wrapper code for functions returning strings:
'
FUNCTION daveStrError(BYVAL code AS LONG) AS STRING
DIM x AS LOCAL STRING
DIM ip AS LOCAL LONG
x$ = STRING$(256, 0) ' create a string of sufficient capacity
ip = daveInternalStrerror(code) ' have the text for code copied in
CALL daveStringCopy(ip, x$) ' have the text for code copied in
x$ = LEFT$(x$, INSTR(x$, CHR$(0)) - 1) ' adjust the length
daveStrError = x$ ' and return result
END FUNCTION
FUNCTION daveAreaName(BYVAL code AS LONG) AS STRING
DIM x AS LOCAL STRING
DIM ip AS LOCAL LONG
x$ = STRING$(256, 0) ' create a string of sufficient capacity
ip = daveInternalAreaName(code) ' have the text for code copied in
CALL daveStringCopy(ip, x$) ' have the text for code copied in
x$ = LEFT$(x$, INSTR(x$, CHR$(0)) - 1) ' adjust the length
daveAreaName = x$ ' and return result
END FUNCTION
FUNCTION daveBlockName(BYVAL code AS LONG) AS STRING
DIM x AS LOCAL STRING
DIM ip AS LOCAL LONG
x$ = STRING$(256, 0) ' create a string of sufficient capacity
ip = daveInternalBlockName(code) ' have the text for code copied in
CALL daveStringCopy(ip, x$) ' have the text for code copied in
x$ = LEFT$(x$, INSTR(x$, CHR$(0)) - 1) ' adjust the length
daveBlockName = x$ ' and return result
END FUNCTION
FUNCTION daveGetName(BYVAL di AS LONG) AS STRING
DIM x AS LOCAL STRING
DIM ip AS LOCAL LONG
x$ = STRING$(256, 0) ' create a string of sufficient capacity
ip = daveInternalGetName(di) ' have the text for code copied in
CALL daveStringCopy(ip, x$) ' have the text for code copied in
x$ = LEFT$(x$, INSTR(x$, CHR$(0)) - 1) ' adjust the length
daveGetName = x$ ' and return result
END FUNCTION
FUNCTION daveGetBlockInfo(BYVAL di AS LONG) AS BYTE
DIM x AS LOCAL STRING
DIM ip AS LOCAL LONG
x$ = STRING$(256, 0) ' create a string of sufficient capacity
ip = daveInternalGetName(di) ' have the text for code copied in
CALL daveStringCopy(ip, x$) ' have the text for code copied in
x$ = LEFT$(x$, INSTR(x$, CHR$(0)) - 1) ' adjust the length
' daveGetName = x$ ' and return result
'********** ???????????????? *********
END FUNCTION
'*****************************************************
' End of interface declarations and helper functions.
'*****************************************************
'--------------------------------------------------------------------------------
' Constants
'--------------------------------------------------------------------------------
$ProgVers = "Ver 1.0 28/07/06" ' Program version
%MOD_ALT = &H00000001 ' keyboard hooks
%MOD_CONTROL = &H00000002
%MOD_SHIFT = &H00000004
%DLE = &H10
%ETX = &H03
%STX = &H02
%SYN = &H16
'--------------------------------------------------------------------------------
'--------------------------------------------------------------------------------
' LibNoDave Constants
'--------------------------------------------------------------------------------
' Protocol types to be used with newInterface
%daveProtoMPI = 0 ' MPI FOR S7 300/400
%daveProtoMPI2 = 1 ' MPI FOR S7 300/400, "Andrew's version"
%daveProtoPPI = 10 ' PPI FOR S7 200
%daveProtoISOTCP = 122 ' ISO over TCP
%daveProtoISOTCP243 = 123 ' ISO over TCP WITH CP243
%daveProtoMPI_IBH = 223 ' MPI WITH IBH NetLink MPI TO ethernet gateway
%daveProtoPPI_IBH = 224 ' PPI WITH IBH NetLink PPI TO ethernet gateway
%daveProtoNLpro = 230 ' MPI WITH NetLink Pro MPI TO ethernet gateway
%daveProtoUserTransport = 255 ' USER defined transport protocol
' ProfiBus speed constants
%daveSpeed9k = 0
%daveSpeed19k = 1
%daveSpeed187k = 2
%daveSpeed500k = 3
%daveSpeed1500k = 4
%daveSpeed45k = 5
%daveSpeed93k = 6
' Some MPI function codes (yet unused ones may be incorrect)
%daveFuncOpenS7Connection = &HF0
%daveFuncRead = &H04
%daveFuncWrite = &H05
%daveFuncRequestDownload = &H1A
%daveFuncDownloadBlock = &H1B
%daveFuncDownloadEnded = &H1C
%daveFuncStartUpload = &H1D
%daveFuncUpload = &H1E
%daveFuncEndUpload = &H1F
#IF 0
' S7 specific constants
$daveBlockType_OB = "8"
$daveBlockType_DB = "A"
$daveBlockType_SDB = "B"
$daveBlockType_FC = "C"
$daveBlockType_SFC = "D"
$daveBlockType_FB = "E"
$daveBlockType_SFB = "F"
' Use these constants for parameter "area" in daveReadBytes and daveWriteBytes
$daveSysInfo = "3" ' System info OF 200 family
$daveSysFlags = "5" ' System flags OF 200 family
$daveAnaIn = "6" ' analog inputs OF 200 family
$daveAnaOut = "7" ' analog outputs OF 200 family
$daveP = "80"
$daveInputs = "81" ' Peripheral DATA like PIW, PQW
$daveOutputs = "82"
$daveFlags = "83"
$daveDB = "84" ' DATA blocks
$daveDI = "85" ' NOT tested
$daveLocal = "86" ' NOT tested
$daveV = "87" ' don't know what it is
$daveCounter = "28" ' don't know what it is
$daveTimer = "29" ' don't know what it is
#ENDIF
%daveSysInfo = &H3 ' System info of 200 family
%daveSysFlags = &H5 ' System flags of 200 family
%daveAnaIn = &H6 ' analog inputs of 200 family
%daveAnaOut = &H7 ' analog outputs of 200 family
%daveP = &H80 ' direct access to peripheral adresses
%daveInputs = &H81
%daveOutputs = &H82
%daveFlags = &H83
%daveDB = &H84 ' data blocks
%daveDI = &H85 ' instance data blocks
%daveV = &H87 ' don't know what it is
%daveCounter = 28 ' S7 counters
%daveTimer = 29 ' S7 timers
%daveCounter200 = 30 ' IEC counters (200 family)
%daveTimer200 = 31 ' IEC timers (200 family)
'
%daveOrderCodeSize = 21 ' Length of order code (MLFB number)
' Library specific Result codes
' Generally, 0 means ok,
' >0 are results (also errors) reported by the PLC
' <0 means error reported by library code.
%daveResOK = 0 ' means ALL ok
' CPU tells it does not support to read a bit block with a length other than 1 bit
%daveResMultipleBitsNotSupported = 6
' means a a piece of data is not available in the CPU, e.g.
' when trying TO READ a non existing DB OR BIT block OF length<>1
%daveResItemNotAvailable200 = 3
' This code seems to be specific to 200 family
' means a a piece of data is not available in the CPU, e.g.
' when trying TO READ a non existing DB
%daveResItemNotAvailable = 10
' means the data address is beyond the CPUs address range
%daveAddressOutOfRange = 5
%daveResCannotEvaluatePDU = -123
%daveResCPUNoData = -124
%daveUnknownError = -125
%daveEmptyResultError = -126
%daveEmptyResultSetError = -127
' Max number of bytes in a single message.
' An upper limit for MPI over serial is:
' 8 transport header
' +2 240 max PDU len 2 if every character were a DLE
' +3 DLE,ETX and BCC
' = 491
' Later I saw some programs offering up to 960 bytes in PDU size negotiation
' Max number of bytes in a single message.
' An upper limit for MPI over serial is:
' 8 transport header
' +2 960 max PDU len 2 if every character were a DLE
' +3 DLE,ETX and BCC
' = 1931
' For now, we take the rounded max of all this to determine our buffer size. This is ok
' for PC systems, where one k less or more doesn't matter.
%daveMaxRawLen = 2048
' Some definitions for debugging
$daveDebugRawRead = "01" ' SHOW the SINGLE bytes received
$daveDebugSpecialChars = "02" ' SHOW when special chars are READ
$daveDebugRawWrite = "04" ' SHOW the SINGLE bytes written
$daveDebugListReachables = "08" ' SHOW the steps when determine devices IN MPI net
$daveDebugInitAdapter = "10" ' SHOW the steps when Initilizing the MPI adapter
$daveDebugConnect = "20" ' SHOW the steps when connecting a PLC
$daveDebugPacket = "40"
$daveDebugByte = "80"
$daveDebugCompare = "100"
$daveDebugExchange = "200"
$daveDebugPDU = "400" ' debug PDU handling
$daveDebugUpload = "800" ' debug PDU loading program blocks FROM PLC
$daveDebugMPI = "1000"
$daveDebugPrintErrors = "2000" ' PRINT ERROR messages
$daveDebugPassive = "4000"
$daveDebugErrorReporting = "8000"
$daveDebugOpen = "10000"
$daveDebugAll = "1FFFF"
$LibName = "libnodave.dll" ' WIN32
'--------------------------------------------------------------------------------
' Data structures
'--------------------------------------------------------------------------------
' WIN32
TYPE typDaveOSserial BYTE
rfd AS DWORD
wfd AS DWORD
END TYPE
' Helper struct to manage PDUs. This is NOT the part of the packet I would call PDU, but
' a set of pointers that ease access to the "parts" of a PDU.
' pointer to start of PDU (PDU header)
' pointer to start of parameters inside PDU
' pointer to start of data inside PDU
' pointer to start of data inside PDU
' header length
' parameter length
' data length
' user or result data length
TYPE typPDU BYTE
pheader AS BYTE POINTER
pparam AS BYTE POINTER
pDATA AS BYTE POINTER
udata AS BYTE POINTER
hlen AS LONG
plen AS LONG
dlen AS LONG
udlen AS LONG
END TYPE
' Definitions of prototypes for the protocol specific functions. The library "switches"
' protocol by setting pointers to the protol specific implementations.
' This groups an interface together with some information about it's properties
' in the library's context.
' some handle for the serial interface
' a counter used when multiple PLCs are accessed via
' the same serial interface and adapter.
' the adapter's MPI address
' just a name that can be used in programs dealing with multiple
' daveInterfaces
' Timeout in microseconds used in transort.
' The kind of transport protocol used on this interface.
' The MPI or Profibus speed
' position of some packet number that has to be repeated in ackknowledges
' pointers to the protocol
' specific implementations
' of these functions
TYPE typMPIheader BYTE
src_conn AS BYTE
dst_conn AS BYTE
MPI AS BYTE
localMPI AS BYTE
leng AS BYTE
func AS BYTE
packetNumber AS BYTE
END TYPE
TYPE typDaveInterface BYTE
ttimeout AS LONG
fd AS typDaveOSserial
localMPI AS LONG
users AS LONG
pname AS BYTE POINTER
protocol AS LONG
speed AS LONG
ackPos AS LONG
nextConnection AS DWORD
initAdapter AS DWORD
connectPLC AS DWORD
disconnectPLC AS DWORD
disconnectAdapter AS DWORD
exchange AS DWORD
sendMessage AS DWORD
getResponse AS DWORD
listReachablePartners AS DWORD
END TYPE
' This holds data for a PLC connection
' pointer to used interface
' The PLC's address
' current message number
' message number we need ackknowledge for
' length of last message
' template of MPI Header, setup once, copied in and then modified
' used to retrieve single values from the result byte array
' packetNumber in transport layer
' position of PDU in outgoing messages. This is different for different transport methodes.
' position of PDU in incoming messages. This is different for different transport methodes.
' rack number for ISO over TCP
' slot number for ISO over TCP
TYPE typDaveConnection BYTE
AnswLen AS LONG
resultPointer AS BYTE POINTER
maxPDUlength AS LONG
MPIAdr AS LONG
pIface AS LONG
needAckNumber AS LONG
PDUnumber AS LONG
ibhSrcConn AS LONG
ibhDstConn AS LONG
msgIn (0 TO (%daveMaxRawLen)-1) AS BYTE
msgOut (0 TO (%daveMaxRawLen)-1) AS BYTE
r_resultPointer AS BYTE POINTER
PDUstartO AS LONG
PDUstartI AS LONG
rack AS LONG
slot AS LONG
connectionNumber AS LONG
connectionNumber2 AS LONG
messageNumber AS BYTE
packetNumber AS BYTE
END TYPE
'--------------------------------------------------------------------------------
#ENDIF ' #IF NOT %DEF(%DAVE_INC)

6982
contributions/keith/nodave.c Executable file

File diff suppressed because it is too large Load Diff

51
contributions/keith/setport.h Executable file
View File

@@ -0,0 +1,51 @@
/*
Part of Libnodave, a free communication libray for Siemens S7 300/400.
(C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2001.
Libnodave is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 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.
*/
#ifdef __cplusplus //****** ADD
extern "C" { //****** ADD
#endif //****** ADD
#ifdef BCCWIN
#ifdef DOEXPORT
#define EXPORTSPEC __declspec (dllexport)
#else
#define EXPORTSPEC __declspec (dllimport)
#endif
EXPORTSPEC HANDLE __stdcall setPort(char * name, char* baud,char parity);
EXPORTSPEC int __stdcall closePort(HANDLE port);
#endif
//#ifdef __cplusplus //****** REMOVE moved to top
//extern "C" { //****** REMOVE moved to top
//#endif //****** REMOVE moved to top
#ifdef LINUX
int setPort(char * name, char* baud,char parity);
int closePort(int port);
#endif
#ifdef __cplusplus
}
#endif

146
contributions/keith/setportw.c Executable file
View File

@@ -0,0 +1,146 @@
/*
Part of Libnodave, a free communication libray for Siemens S7 300/400.
(C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2004.
Libnodave is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with this; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define debug 0
#define ThisModule "setPort : "
#ifdef BCCWIN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include "nodave.h"
extern int daveDebug;
/*
You may wonder why a pair of identical file handles is set up and
returned. It's for compatibility with an extended UNIX version of
this code, which can use two separate pipes for reads and writes.
*/
__declspec(dllexport)
HANDLE __stdcall setPort(char * devname, char * baud,char parity /*, HANDLE * wfd*/){
HANDLE hComm;
DCB dcb;
hComm = CreateFile( devname,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_FLAG_WRITE_THROUGH,
0);
if (daveDebug & daveDebugOpen) {
printf("setPort %s\n",devname);
printf("setPort %s\n",baud);
printf("setPort %c\n",parity);
}
// printf("Handle to %s opened! %d\n",devname,hComm);
GetCommState(hComm,&dcb);
// printf("got Comm State. %d\n ",dcb.BaudRate);
dcb.ByteSize = 8;
dcb.fOutxCtsFlow=FALSE;
dcb.fOutxDsrFlow=FALSE;
// dcb.fDtrControl=DTR_CONTROL_DISABLE; // this seems to be the evil. Guess do not understand the meaning of this parameter
dcb.fDtrControl=DTR_CONTROL_ENABLE;
dcb.fDsrSensitivity=FALSE;
dcb.fInX=FALSE;
dcb.fOutX=FALSE;
dcb.fNull=FALSE;
dcb.fAbortOnError=FALSE;
dcb.fBinary=TRUE;
dcb.fParity=TRUE;
dcb.fOutxCtsFlow=FALSE;
dcb.fOutxDsrFlow=FALSE;
// dcb.fRtsControl=FALSE; // this seems to be the evil. Guess do not understand the meaning of this parameter
// dcb.fRtsControl=TRUE; //****** REMOVE
// from winbase.h : //****** ADD
//#define RTS_CONTROL_DISABLE 0 //****** ADD
//#define RTS_CONTROL_ENABLE 1 //****** ADD
//#define RTS_CONTROL_HANDSHAKE 2 //****** ADD
dcb.fRtsControl=RTS_CONTROL_ENABLE; //****** ADD
//#define RTS_CONTROL_TOGGLE 3 //****** ADD
dcb.fTXContinueOnXoff=TRUE;
// dcb.StopBits=2; ///that was 2 !!! //****** REMOVE
// from winbase.h : //****** ADD
//#define ONESTOPBIT 0 //****** ADD
//#define ONE5STOPBITS 1 //****** ADD
//#define TWOSTOPBITS 2 //****** ADD
dcb.StopBits=TWOSTOPBITS; ///that was 2 !!! //****** ADD
if (0==strncmp(baud,"115200",6))
dcb.BaudRate = CBR_115200;
else if (0==strncmp(baud,"57600",5))
dcb.BaudRate = CBR_57600;
else if (0==strncmp(baud,"38400",5))
dcb.BaudRate = CBR_38400;
else if (0==strncmp(baud,"19200",5))
dcb.BaudRate = CBR_19200;
else if (0==strncmp(baud,"9600",4))
dcb.BaudRate = CBR_9600;
else if (0==strncmp(baud,"4800",4))
dcb.BaudRate = CBR_4800;
else if (0==strncmp(baud,"2400",4))
dcb.BaudRate = CBR_2400;
else if (0==strncmp(baud,"1200",4))
dcb.BaudRate = CBR_1200;
else if (0==strncmp(baud,"600",3))
dcb.BaudRate = CBR_600;
else if (0==strncmp(baud,"300",3))
dcb.BaudRate = CBR_300;
else if (daveDebug & daveDebugPrintErrors) {
printf(ThisModule "illegal Baudrate: %s\n", baud);
}
parity=tolower(parity);
if (parity == 'e')
dcb.Parity = 2;
else if (parity == 'o')
dcb.Parity = 1;
else if (parity == 'n')
dcb.Parity = 0;
else if (daveDebug & daveDebugPrintErrors) {
printf(ThisModule "illegal parity mode:%c\n", parity);
}
SetCommState(hComm,&dcb);
// printf("got Comm State. %d\n ",dcb.BaudRate);
//printf("Comm State set.\n");
// *wfd=hComm;
return hComm;
}
__declspec(dllexport)
int __stdcall closePort(HANDLE port){
int res=CloseHandle(port);
return res;
}
#endif
/*
Changes:
12/17/2004 1st Version for Windows.
04/03/2005 Hopefully really fixed COM port setting.
05/08/2005 Removed printfs for quiet operation.

16
doc/ARMprocessors.html Normal file
View File

@@ -0,0 +1,16 @@
<H1>Compiling Libnodave for ARM processors</H1>
Libnodave did not work on ARM processors. It seemed that the processor cannot access a word on a
non word boundary (odd address). The following code did not work:
<pre>
((PDUHeader*)p->header)->plen=daveSwapIed_16(len);
</pre>
Here the pointer p->header has been set two an odd address. The offset of plen in the
structure PDUHeader is even, hence the resulting pointer points at an odd address.
The ARM seems to take the previous lower word boundary instead of the calculated address.
It seems that no compiler option could prevent this, possibly because the final address is the result of a calculation
and not known at comile time. The following work around fixes this.
<pre>
templen=daveSwapIed_16(len);
memcpy(&(((PDUHeader*)p->header)->plen),&templen,sizeof(us));
</pre>
Memcpy seems to use two subsequent byte accesses and works as expected.

30
doc/FAQ.html Normal file
View File

@@ -0,0 +1,30 @@
<html>
<head>
<title></title>
<meta content="">
<style></style>
</head>
<body>
<H1><b>LIBNODAVE</b> FAQ</H1>
<H4>Q: Can you give me a documentation about MPI protocol?</H4>
A: <a href="MPI.html">Here's what I know</a>
<H4>Q: That warning on <b>LIBNODAVE</b> home page, does it mean the software is so dangerous/unreliable?</H4>
A: No, it's just there to prevent anybody from holding me responsible for whatever he might do with it. I trust my software. But I heard, in USA a manufacturer of ladders had to put a label on them saying: "Use on firm ground only" and another one: "Do not use on frozen dung."
<H4>Q: Why doesn't <b>LIBNODAVE</b> work with my MPI adapter?</H4>
A: Haven't heard this for quite while. If you have difficulties, first send me the output from testMPI, once started with and once without the option -2. Also include the exact order code of the adapter. I probably can help if I can provide such an adapter. If not, you may think about giving me access to your computer or sending me the adapter.
<H4>Q: Can <b>LIBNODAVE</b> work with CP 5x1x?</H4>
A: No, it can't. The reason is that there is no documentation for this hardware that would allow to write drivers for it. There are commercial products that use the drivers that are installed with Step7 or other Siemens software. But they are for Windows only.<br>
I guess a speedy USB to MPI adapter could make this hardware obsolete.
<H4>Q: How can I support the development of <b>LIBNODAVE</b>?</H4>
A: Give feedback. I should like to be able to put as many order codes of supported hard ware on the list as possible.<br>
Donations in hardware or money are allways very welcome.
<H4>Q: I want to use <b>LIBNODAVE</b> for my project, but will it support future hardware?</H4>
A: It does what it does and it is absolutely unlikely that it could fail with future members of S7 family or compatibles. Naturally, I cannot speak about an immaginary S9...
<H4>Q: What is the largest block of bytes <b>LIBNODAVE</b> can read/write to a PLC?</H4>
A: This is limited by the maximum length of a <a href="pdu.html">PDU</a>, which in turn depends on your CPU type.For 240 byte PDU length, you can read 222 bytes and write 218 bytes in a single transaction.
<H4>Q: The xy software can write larger blocks, or?</H4>
A: No,whichever software hides that limit from you has to split larger blocks into multiple requests.
<H4>Q: Why don't you do that automatically also in <b>LIBNODAVE</b>?</H4>
A: <b>LIBNODAVE</b> is for programmers. They will be easily available to do it themselves. On the other hand, automatic splitting would hide a limit that costs performance...
</body>
</html>

44
doc/MPI.html Normal file
View File

@@ -0,0 +1,44 @@
<H1>MPI details</H1>
As many users asked for the details of MPI Interface, protocol and whether there is a way to make your own MPI adapter, here is a brief description of what I know:
<H2>Physical Layer</H2>
MPI uses RS485 serial communication with parity even and baud rates from 19.2 kBaud to 187.5 or to 12MBaud in newer systems.<br>
Want to "sniff" MPI? Set your PLC's MPI speed to 19.2, because this is the only baudrate also supported by PC UARTs. Prepare a ribbon cable with one male and to female Sub-D connectors. Plug it into the PLCs MPI connector, an MPI adapter or CP5x1x into one female and an RS485/RS232 converter or PPI cable into the other one. Now your PC can listen!
<H2>Media Arbitration</H2>
MPI is basically a variety of Profibus, using the FDL profile.<br>
Profibus is a token ring. One master at a time is in posession of the "token", which entitles it to send requests to other devices. It may hold the token for a limited time. Either when that time expires or when it has sent all its requests and has got the answers, it passes the token on to the next known master. From time to time, it asks for the presence of masters with numbers between its own number and the number of the next known master. Thus, new masters are "invited" to participate in the ring.<br>
In case of MPI, the PLC in absence of a partner sends the token to itself:
<H2>Example</H2>
<pre>DC 02 02</pre>
After each token passing it sends a request to get the diagnostic state of device number x:
<pre>10 x 02 49 bcc 16</pre>
This is repeated for all free device numbers. If you connect an MPI adapter it will do nothing, not even respond. After you set it up, e.g. with daveInitAdapter(), it has its MPI address, e.g. 0. Now it will respond as soon as the PLC requests diagnostic state from device 0. The PLC then passes the token to it:
<pre>DC 00 02</pre>
Now the adapter, if it has a message to send, sends this message in a telegram of variable length
to the CPU:
<pre>68,11,11,68 //message, 17 bytes long </pre>
<pre> 82,80,6D,00,14, // request telegram to 2 from 0, SRD high DSAP=0,SSAP=0x14</pre>
<pre> E0,04,00,80,00,02,00,02,01,00,01,00,ED,16, //the "payload" message </pre>
<pre> E5, // short ackknowledge from CPU</pre>
<pre>DC,02,00, //adapter passes token to CPU</pre>
<pre>10,05,02,49,50,16, //CPU requests diagnostic state from device 5</pre>
<pre>DC,00,02, //CPU passes token to adapter</pre>
<pre>DC,02,00, //adapter passes token back to CPU</pre>
<pre>10,06,02,49,51,16, //CPU requests diagnostic state from device 6</pre>
<pre> //I'm not sure, but I guess CPU waits for some amount of time for a response.
//This time should be part of Profibus specifications.</pre>
<pre>68,11,11,68, //CPU sends message with length 17</pre>
<pre> 80,82,6C,14,14, // response telegram to 0 from 2, DSAP=14,SSAP=0x14</pre>
<pre> D0,04,00,80,00,02,00,02,01,00,01,00,F0,16,//the "payload" message </pre>
<pre> E5, // short ackknowledge from adapter</pre>
<pre>DC,00,02, //CPU passes token to adapter</pre>
<H2>Payload</H2>
The payload messages are something I want to call <a href=S7communication.html>"S7 communication"</a>. They are common for all sorts of transport (MPI,PPI, ISO over TCP). Payload messages are sent to an MPI adapter either through 3964R (which is used by LIBNODAVE and all 3rd party software I know) or through another protocol used by Step7. The protocol used by Step7 has a more complicated checksum(CRC?). It also sends a kind of "keep alive message" when the connection is idle. This keep alive message changes after packets with payload, so the partner will know if a packet with payload got lost.<br>
The <a href=S7communication.html>S7 communication</a> over MPI introduces additional ackknowledges for each "payload" message generated by PLC or PC and passed over Profibus FDL by the adapter. These are neither present in PPI nor ISO/TCP.
<H2>Homemade MPI Adapters</H2>
<li>Find a uC with 2 UARTs of which one can handle 12MBaud.</li>
<li>Implement Profibus token passing.</li>
<li>Implement Profibus variable length messages.</li>
<li>Implement 3964R for PC side.</li>
<li>Implement the adapter init/disconnect commends.</li>
<li>Implement the "Step7 protocol" for PC side, if you want to use it with Step7.</li>
<li>Be aware that it is not tested for Profibus compliance. So you are at risk to stop dangerous machinery...</li>

26
doc/PDUerrorcodes.html Normal file
View File

@@ -0,0 +1,26 @@
<H1>Error codes</H1>
<H2>Error codes reported in 12 byte headers of type 2 and 3 PDUs</H2>
<table>
<tr><td>0</td><td>ok</td></tr>
<tr><td>0x8000</td><td>function already occupied.</td></tr>
<tr><td>0x8001</td><td>not allowed in current operating status.</td></tr>
<tr><td>0x8101</td><td>hardware fault</td></tr>
<tr><td>0x8103</td><td>object access not allowed.</td></tr>
<tr><td>0x8104</td><td>context is not supported.</td></tr>
<tr><td>0x8105</td><td>invalid address.</td></tr>
<tr><td>0x8106</td><td>data type not supported.</td></tr>
<tr><td>0x8107</td><td>data type not consistent.</td></tr>
<tr><td>0x810A</td><td>object does not exist.</td></tr>
<tr><td>0x8500</td><td>incorrect PDU size.</td></tr>
<tr><td>0x8702</td><td>address invalid."</td></tr>
<tr><td>0xd201</td><td>block name syntax error.</td></tr>
<tr><td>0xd202</td><td>syntax error function parameter.</td></tr>
<tr><td>0xd203</td><td>syntax error block type.</td></tr>
<tr><td>0xd204</td><td>no linked block in storage medium.</td></tr>
<tr><td>0xd205</td><td>object already exists.</td></tr>
<tr><td>0xd206</td><td>object already exists.</td></tr>
<tr><td>0xd207</td><td>block exists in EPROM.</td></tr>
<tr><td>0xd209</td><td>block does not exist.</td></tr>
<tr><td>0xd20e</td><td>no block does not exist.</td></tr>
<tr><td>0xd210</td><td>block number too big.</td></tr>
</table>

4
doc/S7Communication.html Normal file
View File

@@ -0,0 +1,4 @@
<H1>S7 Communication details</H1>
The basic unit in S7 communication ia a PDU (protocol data unit). It is made up of:
<li>A 10 or 12 byte header</li>
<li>A parameter field of variable length</li>

12
doc/SZL.html Normal file
View File

@@ -0,0 +1,12 @@
<H1>Reading SZL lists</H1>
The CPUs of the 300 and 400 family provide lists of their internal states and properties. German Siemens terminology calls them SZL (<b>S</b>ystem-<b>Z</b>ustands<b>L</b>isten). These lists are what your programming Software reads when showing the diagnostic buffer, the state of run/stop, the amount of memory and much more. LIBNODAVE provides the function:
<pre>
daveReadSZL(daveConnection * dc, int ID, int index, void * buf);
</pre>
to read these lists.<br>
ID 0, index 0 retrieves a list of available SZL-IDs on your PLC. I don't know how to find out
the available or meaningful indices. In most cases, index 0 is the whole list, while other
indices retrieve parts of it.<br>
Use testMPI -z for some examples and testMPI -a for the complete contents of your PLCs SZL-lists
(with index 0, so there may exist lists that do not have an index 0).<br>
Refer do Siemens documentation for the meaning of IDs and indices.

18
doc/area.html Normal file
View File

@@ -0,0 +1,18 @@
<H1>PLC memory areas</H1>
<table>
<tr><th>Name</th><th>constant</th><th>Example item(German)</th><th>Example item(English)</th><th>Example read call</th></tr>
<tr><td>Data blocks</td><td>daveDB</td><td>DB3.DBD4</td><td>DB3.DBD4</td><td>daveReadBytes(dc,daveDB,3,4,4,NULL)</td></tr>
<tr><td>Flags/Markers</td><td>daveFlags</td><td>MW4</td><td>FW4</td><td>daveReadBytes(dc,daveFlags,0,4,2,NULL)</td></tr>
<tr><td>Input memory image</td><td>daveInputs</td><td>EB2</td><td>IB2</td><td>daveReadBytes(dc,daveInputs,0,2,1,NULL)</td></tr>
<tr><td>Output memory image</td><td>daveOutputs</td><td>AD8</td><td>QD8</td><td>daveReadBytes(dc,daveOutputs,0,8,4,NULL)</td></tr>
<tr><td>Timers</td><td>daveTimer</td><td>T2</td><td>T2</td><td>daveReadBytes(dc,daveTimer,0,2,2,NULL)</td></tr>
<tr><td>Counters</td><td>daveCounter</td><td>Z2</td><td>C2</td><td>daveReadBytes(dc,daveCounter,0,2,2,NULL)</td></tr>
<tr><td>Direct I/O</td><td>daveP</td><td>PEW4</td><td>PIW4</td><td>daveReadBytes(dc,daveP,0,4,2,NULL)</td></tr>
<tr><td>System information of 200 family</td><td>daveSysInfo</td><td></td><td></td><td>daveReadBytes(dc,daveSysInfo,0,0,20,NULL)</td></tr>
<tr><td>Data (V-memory) in S7-200</td><td>daveDB</td><td>VW1234</td><td>VW1234</td><td>daveReadBytes(dc,daveDB,1,1234,2,NULL)</td></tr>
<tr><td>System flag area of 200 family</td><td>daveSysFlags</td><td>SMB0</td><td>SFB0?</td></tr>
<tr><td>Analog input words of 200 family</td><td>daveAnaIn</td><td>AEW0</td><td>AIW0?</td></tr>
<tr><td>Analog output words of 200 family</td><td>daveAnaOut</td><td>AAW0</td><td>AQW0?</td></tr>
<tr><td>IEC Timers</td><td>daveTimer200</td><td>T2</td><td>T2</td><td>daveReadBytes(dc,daveTimer200,0,2,2,NULL)</td></tr>
<tr><td>IEC Counters</td><td>daveCounter200</td><td>Z2</td><td>C2</td><td>daveReadBytes(dc,daveCounter200,0,2,2,NULL)</td></tr>
</table>

73
doc/conversions.html Normal file
View File

@@ -0,0 +1,73 @@
<H1>Conversion routines</H1>
The buffers used by daveReadBytes() and daveWriteBytes() will contain a copy of the PLC memory area. This means: If you read from the beginning of DB2, the buffer will contain a byte for byte copy of DB2. The values are the same and in the same order, as what you get when you observe byte variables in Step7. Example:<br>
<table>
<tr><td>DB20.DBB0</td><td>26</td></tr>
<tr><td>DB20.DBB1</td><td>37</td></tr>
<tr><td>DB20.DBB2</td><td>48</td></tr>
<tr><td>DB20.DBB3</td><td>15</td></tr>
<tr><td>DB20.DBB4</td><td>16</td></tr>
<table>
You are free to interpret these values as single bytes or multibyte values,the same way as you can do this in Step7 AWL.:<br>
Also it is unusual, you can load a real value from DB20.DBD1:<br>
<pre>
L DB20.DBD1
L 2.0
*R
</pre>
A further complication results from the fact that Siemens PLCs store multibyte values beginning with the most significant byte (big endian) while Intel based PCs store the least significant byte first (little endian).<br>
It is not possible to convert the byte order in the daveReadBytes() or daveWriteBytes() fuctions
because the start position of each multibyte value is not known then.<br>
You are free to place such values at arbitrary byte addresses in your PLC program.
The same adresses must in turn be used to retrieve values from the copy of PLC memory.
If you have a data block DB2 with the following layout:<br>
<table>
<tr><td>DBB 0</td><td>BYTE</td></tr>
<tr><td>DBD 1</td><td>DWORD</td></tr>
<tr><td>DBD 5</td><td>REAL</td></tr>
</table>
You can retrieve the single values in three ways:<br>
1. From the intenal buffer. After a successful call, an internal pointer points to the 1st byte.
Now use daveGetU8(dc) to get the value of the first byte as an unsigned value. The internal buffer pointer
is incremented by 1, now pointing to the copy of DBD1. Use daveGetS32(dc) to get the value of the
of the next 4 bytes as a signed value. The internal pointer is incremented by 4, now pointing to
the copy of DBD5. Use daveGetFloat(dc) to get the value of the next 4 bytes as a single precision
float.<br>
2. From the internal buffer, specifying a position. Use daveGetU8at(dc,0) to get the value of the
first byte as an unsigned value. Next use daveGetS32at(dc,1) to get the value of the 4 bytes
starting at 1 as a signed value. Finally, use daveGetFloatat(dc,5) to get the value of the 4 bytes
starting at 5 as a single precision float. You may perform these operation in any order and also
repeat them.<br>
3. From a user buffer. Use daveGetU8from(buffer) to get the value of the first byte as an
unsigned value. Use daveGetS32from(buffer+1) to get the value of the 4 bytes at buffer+1, i.e.
DBD 1, as a signed value. Use daveGetFloatat(buffer+5) to get the value of the 4 bytes starting
at buffer+5 as a single precision float, i.e. DBD5.<br>
The conversion functions are named after the bit length and signedness they assume:
<table>
<tr><th>int buffer</th><th>int buffer+pos</th><th>buffer pointer</th><th>size</th><th>signed</th><th>C-return type</th><th>Pascal ret type</th></tr>
<tr><td>daveGetU8</td><td>daveGetU8at</td><td>daveGetU8from</td><td>8 bit=1 byte</td><td>no</td><td>int</td><td>longint</td></tr>
<tr><td>daveGetS8</td><td>daveGetS8at</td><td>daveGetS8from</td><td>8 bit=1 byte</td><td>yes</td><td>int</td><td>longint</td></tr>
<tr><td>daveGetU16</td><td>daveGetU16at</td><td>daveGetU16from</td><td>16 bit=2 byte</td><td>no</td><td>int</td><td>longint</td></tr>
<tr><td>daveGetS16</td><td>daveGetS16at</td><td>daveGetS16from</td><td>16 bit=2 byte</td><td>yes</td><td>int</td><td>longint</td></tr>
<tr><td>daveGetU32</td><td>daveGetU32at</td><td>daveGetU32from</td><td>32 bit=4 byte</td><td>no</td><td>unsigned int</td><td>longint</td></tr>
<tr><td>daveGetS32</td><td>daveGetS32at</td><td>daveGetS32from</td><td>32 bit=4 byte</td><td>yes</td><td>int</td><td>longint</td></tr>
<tr><td>daveGetFloat</td><td>daveGetFloatat</td><td>daveGetFloatfrom</td><td>32 bit=4 byte</td><td>yes</td><td>float</td><td>single</td></tr>
</table>
There had been an older set of those functions named after data types, e.g.
daveGetDWORD(). Those functions should not be used any more, as there names might be
misunderstandable between PLC and C or other programming languages. They are still supported
for compatibility with older versions. These functions had been inlined in earlier versions
but are now not inlined by default, because other languages than C cannot make use of inline
definitions in a C header file.
<h4>Notes:</h4>
Most commercial libraries handle the conversion issue differently: They provide functions to read one or a set of words, one or a set of long words, one or a set of reals. On the first glance, this might seem more convenient and it <b>is</b> as long as it can be applied to PLC memory areas containing only elements of the same type and size. But when you have to deal with data of mixed type and size, you would have to use another call to daveReadBytes() each time the type or size differs from the former. And each call contributes the overhead of the protocol and the response time of the PLC. Other libraries provide a way to read multiple items with a single call. So does <b>Libnodave</b>. You could also use it to retrieve data located on different boundaries. But this is limited to 20 items by the PLC. And it introduces some overhead as an address has to be transmitted for each item in the request. Use read multiple items to access data from different DBs or other memory areas when the combined results will fit in a single response.
<table>
<tr><th>When you want to read</td><th>do</th><th>remark</th></tr>
<tr><td>DB20.DBD0..DBD20</td><td>read 24 bytes starting at DBB0</td></tr>
<tr><td>DB20.DBD0..DBD8 and DBD20</td><td>read 24 bytes starting at DBB0</td><td>just do not evaluate bytes 11 to 19 of result.</td></tr>
<tr><td>DB20.DBD0..DBD8 and DBD120</td><td>either read 124 bytes starting at DBB0</td><td>just do not evaluate bytes 11 to 119 of result.</td></tr>
<tr><td></td><td>or use multiple read on: item1:DB20.DBD0..DBD8, item2:DB20.DBD120 </td><td>you have to deal with the result set.</td></tr>
<tr><td>DB20.DBD0..DBD8 and DB21.DBD120</td><td>use multiple read on: item1:DB20.DBD0..DBD8, item2:DB21.DBD120 </td><td>this is the only way to read from different DBs with a single request/reponse.</td></tr>
<tr><td>DB20.DBD0..DBD118 and DB21.DBD4..DBD80</td><td>use two calls to daveReadBytes</td><td>this is the only way if the two combined results will not fit into a single PDU.</td></tr>
<table>

18
doc/daveConnection.html Normal file
View File

@@ -0,0 +1,18 @@
<H1>daveConnection</H1>
A structure representing the physical connection to a single PLC.
daveConnection stores all properties that are unique to a single PLC:
<li>The MPI address of this PLC.</li>
<li>The rack the PLC is in.</li>
<li>The slot the PLC is in.</li>
The structure daveConnection is created and initialized by daveNewConnection:
<pre>
daveConnection * dc;
dc =daveNewConnection(di, MPI, rack, slot);
</pre>
The parameters are:
<li>di: a <a href=daveInterface.html>daveInterface</li>
<li>MPI: the address of the PLC (only meaningful for MPI and PPI).</li>
<li>rack: The rack the CPU is mounted in (normally 0, only meaningful for ISO over TCP).</li>
<li>slot: The slot number the CPU is mounted in (normally 2, only meaningful for ISO over TCP)</li>
<H3>Notes:</H3>
In case of PPI communication, the PPI address of the partner PLC must be put into the MPI address parameter. The local PPI address must be put into the local MPI address parameter when calling <a href=daveInterface.html> daveNewInterface()</a>.

41
doc/daveInterface.html Normal file
View File

@@ -0,0 +1,41 @@
<H1>daveInterface</H1>
A structure representing the physical connection to a PLC or a network of PLCs (e.g. like MPI).
daveInterface stores all those properties that are common to a network of PLCs:
<li>- The local address used by your computer.</li>
<li>- The speed used in this network.</li>
<li>- The protocol type used in this network.</li>
<li>- A name which is used when printing out debug messages.</li>
The structure daveInterface is created and initialized by daveNewInterface:
<pre>
daveInterface * di;
di =daveNewInterface(fds, "IF1", localMPI, daveProtoXXX, daveSpeedYYY);
</pre>
The parameters are:
<li>fds: a <a href=daveOSserialType.html>_daveOSserialType</li>
<li>a Name</li>
<li>localMPI: the address used by your computer/adapter (only meaningful for MPI and PPI)</li>
<li>daveProtoXXX: a constant specifying the protocol to be used on this interface</li>
<li>daveSpeedYYY: a constant specifying the speed to be used on this interface. (only meaningful for MPI and Profibus)</li>
<H4>Protocol types to be used with newInterface:</H4>
<table>
<tr><th>Name</th><th>Meaning</th></tr>
<tr><td>daveProtoMPI</td><td>MPI for S7 300/400</td></tr>
<tr><td>daveProtoMPI2</td><td>MPI for S7 300/400, "Andrew's version"</td></tr>
<tr><td>daveProtoPPI</td><td>PPI for S7 200</td></tr>
<tr><td>daveProtoISOTCP</td><td>ISO over TCP</td></tr>
<tr><td>daveProtoISOTCP243</td><td>ISO over TCP with CP243</td></tr>
<tr><td>daveProtoIBH</td><td>MPI with IBH NetLink MPI to ethernet gateway</td></tr>
</table>
<H4>MPI/ProfiBus speed constants:</H4>
<table>
<tr><td> daveSpeed9k</td></tr>
<tr><td> daveSpeed19k</td></tr>
<tr><td> daveSpeed187k</td></tr>
<tr><td> daveSpeed500k </td></tr>
<tr><td> daveSpeed1500k</td></tr>
<tr><td> daveSpeed45k </td></tr>
<tr><td> daveSpeed93k </td></tr>
</table>
The name field is provided for applications which may communicate with multiple PLCs on multiple
pgysical connections. In such cases, the name helps to tell which interface an error message
comes from.

32
doc/daveOSserialType.html Normal file
View File

@@ -0,0 +1,32 @@
<H1>_daveOSserialType </H1>
A wrapper type that contains variables representing the incoming (rfd) and outgoing (wfd) communication
channels on OS level. For LINUX and UNIX like systems, these are file descriptors, for Windows, they are handles.
For bidirectional channels, rfd and wfd are identical. The reasons for having separate in and out channels are:<br>
1. On Unix-like systems, you can use two half duplex (unidirectional)
pipes. While there is no possibility to establish a pipe directly to the PLC, a helper program
or a PLC simulator might provide such pipes.<br>
2. On not yet supported systems, e.g. microcontrollers or DOS with a special support for interrupt
controlled serial communication, these variables probably will contain addresses of different low
level routines or data structures which provide an interface to low level character I/O.<br>
In case of Unix or Windows, the variables are just identical.<br>
<H4>LINUX definition:</H4>
<pre>
typedef struct {
int rfd;
int wfd;
} _daveOSserialType;
</pre>
<H4>WIN32 definition:</H4>
<pre>
typedef struct {
HANDLE rfd;
HANDLE wfd;
} _daveOSserialType;
</pre>
<H4>Possible definition on a microcontroller:</H4>
<pre>
typedef struct {
char * rfd; // pointer to a receivebuffer
*(void(char)) wfd; // pointer to a send routine similar to putchar()
} _daveOSserialType;
</pre>

45
doc/daveReadBytes.html Normal file
View File

@@ -0,0 +1,45 @@
<H1>daveReadBytes</H1>
Reads a sequence of bytes from PLC memory.
<pre>
int daveReadBytes(daveConnection * dc, int area, int DB, int start, int len, void * buffer);
</pre>
<H3>Parameters:</H3>
<li>dc: A pointer to a daveConnection structure representing an established connection.</li>
<li><a href=area.html>area</a>: A constant that specifies a memory area in the PLC.</li>
<li>DB: The number of a data block. Only meaningful if area is daveDB. Use 0 oterwise.</li>
<li>start: The address of the first byte in the block.</li>
<li>len: The number of bytes to read.</li>
<li>buffer: A pointer to some memory space where you want the result to be copied too.</li>
<H3>Result:</H3>
The function returns 0 on success. Nonzero return codes may be passed to daveStrerror() to
get a textual explanation of what happened. Generally, positive error codes represent errors
reported by the PLC, while negative ones represent errors detected by LIBNODAVE, e.g. no
response from the PLC.
<H3>Hints:</H3>
<H4>len:</H4>
Note that timer, counters and the analog input/output words of the 200 family are
allways words (2 bytes). To read n of them, you have to specify 2xn bytes as len.
<H4>buffer:</H4>
You may call daveReadBytes() without a buffer specifying NULL (C) or nil (Pascal). There is,
however, an internal buffer that is part of the <a href=daveConnection.html>daveConnection</a>
structure. This internal buffer allways holds the result from the last read operation.
<H4>Maximum length:</H4>
The maximum size of a block in S7-Communication is limited by the size of a message structure
called<a href=pdu.html>PDU</a>. Each call to daveReadBytes causes a the exchange of a request
and a response PDU. The result data must fit into the "payload" area of a response PDU. This
means a maximum block length is PDU size -18 bytes for read. A typical PDU size is 240 Byte,
limiting read calls to 222 byte result length. If you need more data, you need to use multiple
calls to daveReadBytes().
<H4>Efficiency:</H4>
Each call to daveReadBytes() causes a the exchange of a request
and a response PDU together with prefixes, ackknowledges and what else the transport layer
requires. Therefore you should try to read as much as possible in a single call. Example:<br>
<pre>
daveReadBytes(dc, daveDB, 5, 68, 14, appBuffer);
</pre>
reads DBD68 and DBD78 and everything in between and fills the range appBuffer+4 to appBuffer+9
with 6 unwanted bytes, but it is much faster than:
<pre>
daveReadBytes(dc, daveDB, 5, 68, 4, appBuffer);
daveReadBytes(dc, daveDB, 5, 78, 4, appBuffer+4);
</pre>

40
doc/daveWriteBytes.html Normal file
View File

@@ -0,0 +1,40 @@
<H1>daveWriteBytes</H1>
Write a sequence of bytes from a buffer to PLC memory.
<pre>
int daveWriteBytes(daveConnection * dc, int area, int DB, int start, int len, void * buffer);
</pre>
<H4>Parameters:</H4>
<li>dc: A pointer to a daveConnection structure representing an established connection.</li>
<li><a href=area.html>area</a>: A constant that specifies a memory area in the PLC.</li>
<li>DB: The number of a data block. Only meaningful if area is daveDB. Use 0 oterwise.</li>
<li>start: The address of the first byte in the block.</li>
<li>len: The number of bytes to read.</li>
<li>buffer: A pointer to some memory space where you want the result to be copied too.</li>
<H3>Hints:</H3>
<H4>len:</H4>
Note that timer, counters and the analog input/output words of the 200 family are
allways words (2 bytes). To read n of them, you have to specify 2xn bytes as len.
<H4>buffer:</H4>
You may call daveReadBytes() without a buffer specifying NULL (C) or nil (Pascal). There is,
however, an internal buffer that is part of the <a href=daveConnection.html>daveConnection</a>
structure. This internal buffer allways holds the result from the last read operation.
<H4>Maximum length:</H4>
The maximum size of a block in S7-Communication is limited by the size of a message structure
called<a href=pdu.html>PDU</a>. Each call to daveReadBytes causes a the exchange of a request
and a response PDU. The result data must fit into the "payload" area of a response PDU. This
means a maximum block length is PDU size -18 bytes for read. A typical PDU size is 240 Byte,
limiting read calls to 222 byte result length. If you need more data, you need to use multiple
calls to daveReadBytes().
<H4>Efficiency:</H4>
Each call to daveReadBytes() causes a the exchange of a request
and a response PDU together with prefixes, ackknowledges and what else the transport layer
requires. Therefore you should try to read as much as possible in a single call. Example:<br>
<pre>
daveReadBytes(dc, daveDB, 5, 68, 14, appBuffer);
</pre>
reads DBD68 and DBD78 and everything in between and fills the range appBuffer+4 to appBuffer+9
with 6 unwanted bytes, but it is much faster than:
<pre>
daveReadBytes(dc, daveDB, 5, 68, 4, appBuffer);
daveReadBytes(dc, daveDB, 5, 78, 4, appBuffer+4);
</pre>

641
doc/functions.html Normal file
View File

@@ -0,0 +1,641 @@
<H1>List of functions, structures and constants<H1>
<H2>Constants</H2>
<Pre>
/*
This is a wrapper for the serial or ethernet interface. This is here to make porting easier.
*/
typedef struct {
int rfd;
int wfd;
} _daveOSserialType;
typedef struct {
HANDLE rfd;
HANDLE wfd;
} _daveOSserialType;
</Pre>
<H3>some frequently used ASCII control codes:</H3>
<Pre>
DLE
ETX
STX
SYN
</Pre>
<H3>Protocol types to be used with <a href=daveInterface.html>newInterface</a>:</H3>
<table>
<tr><th>Name</th><th>Meaning</th></tr>
<tr><td>daveProtoMPI</td><td>MPI for S7 300/400</td></tr>
<tr><td>daveProtoMPI2</td><td>MPI for S7 300/400, "Andrew's version"</td></tr>
<tr><td>daveProtoMPI3</td><td>MPI for S7 300/400, The version Step7 uses. Not yet implemented.</td></tr>
<tr><td>daveProtoPPI</td><td>PPI for S7 200</td></tr>
<tr><td>daveProtoISOTCP</td><td>ISO over TCP</td></tr>
<tr><td>daveProtoISOTCP243</td><td>ISO over TCP with CP243</td></tr>
<tr><td>daveProtoIBH</td><td>MPI with IBH NetLink MPI to ethernet gateway</td></tr>
</table>
<H3>ProfiBus/MPI speed constants to be used with <a href=daveInterface.html>newInterface</a>:</H3>
<table>
<tr><td>daveSpeed9k</td></tr>
<tr><td>daveSpeed19k</td></tr>
<tr><td>daveSpeed187k</td></tr>
<tr><td>daveSpeed500k </td></tr>
<tr><td>daveSpeed1500k</td></tr>
<tr><td>daveSpeed45k </td></tr>
<tr><td>daveSpeed93k </td></tr>
</table>
<H3>Some S7 Communication function codes (yet unused ones may be incorrect).</H3>
These codes are used as the first byte of parameters in a <a href=pdu.html>PDU</a>.
<table>
<tr><th>Name</th><th>Meaning</th></tr>
<tr><td>daveFuncOpenS7Connection</td><td>connect to a PLC, negotiate PDU length</td></tr>
<tr><td>daveFuncRead</td><td>marks a read requeast</td></tr>
<tr><td>daveFuncWrite</td><td>marks a write requeast</td></tr>
<tr><td>daveFuncStartUpload</td><td>initiates the transmission of a code block from PLC to programmer</td></tr>
<tr><td>daveFuncUpload</td><td>continues the transmission of a part of a code block from PLC to programmer</td></tr>
<tr><td>daveFuncEndUpload</td><td<td>ends the transmission of a part of a code block from PLC to programmer</td></tr>
</table>
<H3> S7 specific constants:</H3>
<H4>Block Type codes</H4>
S7 specific constants:
<Pre>
daveBlockType_OB
daveBlockType_DB
daveBlockType_SDB
daveBlockType_FC
daveBlockType_SFC
daveBlockType_FB
daveBlockType_SFB
<H4>Memory Area Codes</H4>
Use these constants for parameter "area" in daveReadBytes and daveWriteBytes.
<table>
<tr><th>Name</th><th>Meaning</th></tr>
<tr><td>daveSysInfo 0x3</td><td>System info of 200 family</td></tr>
<tr><td>daveSysFlags</td><td>System flags of 200 family</td></tr>
<tr><td>daveAnaIn</td><td>analog inputs of 200 family</td></tr>
<tr><td>daveAnaOut</td><td>analog outputs of 200 family</td></tr>
<tr><td>daveInputs</td><td>Input image memory</td></tr>
<tr><td>daveOutputs</td><td>Output image memory</td></tr>
<tr><td>daveFlags</td><td>Flags (Merker) area</td></tr>
<tr><td>daveDB</td><td>Data blocks in 300 and 400, V-Memory in 200</td></tr>
<tr><td>daveCounter</td><td>Counters in 300 and 400</td></tr>
<tr><td>daveTimer</td><td>Timers in 300 and 400</td></tr>
</table>
<H3>Function Result Codes.</H3>
Genarally, 0 means ok, &gt;0 are results (also errors) reported by the PLC, &lt;0 means error reported by library code.
<table>
<tr><th>Name</th><th>Value</th><th>Meaning</th></tr>
<tr><td>daveResOK</td><td>0</td><td>all ok</td></tr>
<tr><td>daveResMultipleBitsNotSupported</td><td>6</td><td>CPU tells it does not support to read a bit block with a length other than 1 bit</td></tr>
<tr><td>daveResItemNotAvailable200</td><td>3</td><td>means a a piece of data is not available in the CPU, e.g. when trying to read a non existing DB or bit bloc of length<>1. This code seems to be specific to 200 family.</td></tr>
<tr><td>daveResItemNotAvailable</td><td>10</td><td> means a a piece of data is not available in the CPU, e.g. when trying to read a non existing DB</td></tr>
<tr><td>daveAddressOutOfRange</td><td>5</td><td>means the data address is beyond the CPUs address range</td></tr>
<tr><td>daveResCannotEvaluatePDU</td><td>-123</td><td></td></tr>
<tr><td>daveResCPUNoData</td><td>-124</td><td></td></tr>
<tr><td>daveUnknownError</td><td>-125</td><td></td></tr>
<tr><td>daveEmptyResultError</td><td>-126</td><td></td></tr>
<tr><td>daveEmptyResultSetError</td><td>-127</td><td> </td></tr>
</table>
<H3>Error code to message string conversion:</H3>
Call this function to get an explanation for error codes returned by other functions.
<pre>
char * daveStrerror(int code);
</pre>
<H3>Max number of bytes in a single message.</H3>
An upper limit for MPI over serial is:<br>
<pre>
8 byte transport header
+2*240 max PDU len *2 if every character were a DLE
+3 DLE,ETX and BCC
= 491
Later I saw some programs offering up to 960 bytes in PDU size negotiation
Max number of bytes in a single message.
An upper limit for MPI over serial is:
8 transport header
+2*960 max PDU len *2 if every character were a DLE
+3 DLE,ETX and BCC
= 1931
For now, we take the rounded max of all this to determine our buffer size. This is ok
for PC systems, where one k less or more doesn't matter.
#define daveMaxRawLen 2048
</pre>
<H3>Some definitions for debugging:</H3>
<table>
<tr><td>daveDebugRawRead</td><td>Show the single bytes received</td></tr>
<tr><td>daveDebugSpecialChars</td><td>Show when special chars are read</td></tr>
<tr><td>daveDebugRawWrite</td><td>Show the single bytes written</td></tr>
<tr><td>daveDebugListReachables</td><td>Show the steps when determine devices in MPI net</td></tr>
<tr><td>daveDebugInitAdapter</td><td>Show the steps when Initilizing the MPI adapter</td></tr>
<tr><td>daveDebugConnect</td><td>Show the steps when connecting a PLC</td></tr>
<tr><td>daveDebugPacket</td><td></td></tr>
<tr><td>daveDebugByte</td><td></td></tr>
<tr><td>daveDebugCompare</td><td></td></tr>
<tr><td>daveDebugExchange</td><td></td></tr>
<tr><td>daveDebugPDU</td><td>debug PDU handling</td></tr>
<tr><td>daveDebugUpload</td><td>debug PDU loading program blocks from PLC</td></tr>
<tr><td>daveDebugMPI</td><td></td></tr>
<tr><td>daveDebugPrintErrors</td><td>Print error messages</td></tr>
<tr><td>daveDebugPassive</td><td></td></tr>
<tr><td>daveDebugAll</td><td>Enables all debug levels</td></tr>
</table>
<H2>Global variables</H2>
<H3>Current debug level:</H3>
<pre>
int daveDebug;
</pre>
<H3>Set the Current debug level:</H3>
<pre>
void setDebug(int nDebug);
</pre>
<H3>Some useful data types:</H3>
<pre>
#define uc unsigned char
#define us unsigned short
#define u32 unsigned int
</pre>
typedef struct _daveConnection daveConnection;
typedef struct _daveInterface daveInterface;
<H3>Helper struct to manage PDUs. This is NOT the part of the packet called PDU, but a set of pointers that ease access to the "private parts" of a PDU.</H3>
<pre>
typedef struct {
uc * header; /* pointer to start of PDU (PDU header) */
uc * param; /* pointer to start of parameters inside PDU */
uc * data; /* pointer to start of data inside PDU */
uc * udata; /* pointer to start of result data inside PDU */
int hlen; /* header length */
int plen; /* parameter length */
int dlen; /* data length */
int udlen; /* user or result data length */
} PDU;
</pre>
<H3>Definitions of prototypes for the protocol specific functions. The library "switches" protocol by setting pointers to the protol specific implementations.</H3>
<pre>
typedef int (*_initAdapterFunc) ();
typedef int (*_connectPLCFunc) ();
typedef int (*_disconnectPLCFunc) ();
typedef int (*_disconnectAdapterFunc) ();
typedef int (*_exchangeFunc) (daveConnection *, PDU *);
typedef int (*_receiveFunc) (daveConnection *, PDU *);
typedef int (*_listReachablePartnersFunc) (daveInterface * di, char * buf);
</pre>
/+
This groups an interface together with some information about it's properties
in the library's context.
*/
<pre>
struct _daveInterface {
_daveOSserialType fd; /* some handle for the serial interface */
int users; /* a counter used when multiple PLCs are accessed via */
/* the same serial interface and adapter. */
int localMPI; /* the adapter's MPI address */
char * name; /* just a name that can be used in programs dealing with multiple */
/* daveInterfaces */
int timeout; /* Timeout in microseconds used in transort. */
int protocol; /* The kind of transport protocol used on this interface. */
int speed; /* The MPI or Profibus speed */
int ackPos; /* position of some packet number that has to be repeated in ackknowledges */
_initAdapterFunc initAdapter; /* pointers to the protocol */
_connectPLCFunc connectPLC; /* specific implementations */
_disconnectPLCFunc disconnectPLC; /* of these functions */
_disconnectAdapterFunc disconnectAdapter;
_exchangeFunc exchange;
_listReachablePartnersFunc listReachablePartners;
};
</pre>
daveInterface * daveNewInterface(_daveOSserialType nfd, char * nname, int localMPI, int protocol, int speed);
/*
A special header for MPI packets:
*/
<pre>
typedef struct {
uc src_conn;
uc dst_conn;
uc MPI;
uc localMPI;
uc len;
uc func;
uc packetNumber;
} MPIheader;
</pre>
/*
This is the packet header used by IBH ethernet NetLink.
*/
<pre>
typedef struct {
uc ch1; // logical connection or channel ?
uc ch2; // logical connection or channel ?
uc len; // number of bytes counted from the ninth one.
uc packetNumber; // a counter, response packets refer to request packets
us sFlags; // my guess
us rFlags; // my interpretation
} IBHpacket;
</pre>
/*
This holds data for a PLC connection;
*/
<pre>
struct _daveConnection {
daveInterface * iface; /* pointer to used interface */
int MPIAdr; /* The PLC's address */
int messageNumber; /* current message number */
int needAckNumber; /* message number we need ackknowledge for */
int AnswLen; /* length of last message */
PDU rcvdPDU;
MPIheader templ; /* template of MPI Header, setup once, copied in and then modified */
uc msgIn[daveMaxRawLen];
uc msgOut[daveMaxRawLen];
uc * resultPointer; /* used to retrieve single values from the result byte array */
uc * _resultPointer;
uc packetNumber; /* packetNumber in transport layer */
int PDUstartO; /* position of PDU in outgoing messages. This is different for different transport methodes. */
int PDUstartI; /* position of PDU in incoming messages. This is different for different transport methodes. */
int rack; /* rack number for ISO over TCP */
int slot; /* slot number for ISO over TCP */
int maxPDUlength;
uc ackByte2;
};
</pre>
/*
Setup a new connection structure using an initialized
daveInterface and PLC's MPI address.
*/
<pre>
daveConnection * daveNewConnection(daveInterface * di, int MPI,int rack, int slot);
</pre>
<pre>
typedef struct {
uc type[2];
unsigned short count;
} daveBlockTypeEntry;
</pre>
<pre>
typedef struct {
unsigned short number;
uc type[2];
} daveBlockEntry;
</pre>
<pre>
typedef struct {
uc type[2];
uc x1[2]; /* 00 4A */
uc w1[2]; /* some word var? */
char pp[2]; /* allways 'pp' */
uc x2[4]; /* 00 4A */
unsigned short number; /* the block's number */
uc x3[26]; /* ? */
unsigned short length; /* the block's length */
uc x4[16];
uc name[8];
uc x5[12];
} daveBlockInfo;
</pre>
<H2>PDU handling functions:</H2>
PDU is the central structure present in S7 communication.
It is composed of a 10 or 12 byte header,a parameter block and a data block.
When reading or writing values, the data field is itself composed of a data
header followed by payload data
<pre>
typedef struct {
uc P; /* allways 0x32 */
uc type; /* Header type, one of 1,2,3 or 7. type 2 and 3 headers are two bytes longer. */
uc a,b; /* currently unknown. Maybe it can beused for long numbers? */
us number; /* A number. This can be used to make sure a received answer */
/* corresponds to the request with the same number. */
us plen; /* length of parameters which follow this header */
us dlen; /* length of data which follow the parameters */
uc result[2]; /* only present in type 2 and 3 headers. This contains error information. */
} PDUHeader;
<pre>
<H3>set up the PDU header.</H3> Needs valid header pointer in the struct p points to.
<pre>
void _daveInitPDUheader(PDU * p, int type);
</pre>
<H3>Add parameters after header</h3>
Adjust pointer to data. needs valid header.
<pre>
void _daveAddParam(PDU * p,uc * param,us len);
</pre>
<H3>add data after parameters</H3>
Set dlen needs valid header,and valid parameters.
<pre>
void _daveAddData(PDU * p,void * data,int len);
</pre>
<H3>Add values after value header in data</H3>
Adjust dlen and data count. Needs valid header,parameters,data,dlen
<pre>
void _daveAddValue(PDU * p,void * data,int len);
</pre>
<H3>Add data in user data.</H3>
Add a user data header, if not yet present.
<pre>
void _daveAddUserData(PDU * p, uc * da, int len);
</pre>
<H3>Build PDU for a read request</H3>
<pre>
void _daveConstructReadRequest(PDU *p, int area, int DBnum, int start, int bytes);
</pre>
<H3>
build PDU for a BIT read request
</H3>
<pre> void _daveConstructBitReadRequest(PDU *p, int area, int DBnum, int start, int bytes);</pre>
<H3>
build the PDU for a write request
</H3>
<pre> void _daveConstructWriteRequest(PDU *p, int area, int DBnum, int start, int bytes,void * values);</pre>
<H3>
build the PDU for a bit write request
</H3>
<pre> void _daveConstructBitWriteRequest(PDU *p, int area, int DBnum, int start, int bytes,void * values);</pre>
<H3>
set up pointers to the fields of a received message
<H3>
<pre> int _daveSetupReceivedPDU(daveConnection * dc,PDU * p);</pre>
<H3>
send PDU to PLC and retrieves the answer
</H3>
<pre> int _daveExchange(daveConnection * dc,PDU *p);</pre>
<H2>Utilities:<H2>
<H3>Hex dump PDU:</H3>
<pre> void _daveDumpPDU(PDU * p);</pre>
<H3>Compare blocks:</H3>
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, if there were no mismatches
up to this point.
<pre> int _daveMemcmp(us * a, uc *b, size_t len);</pre>
<H3>Hex dump:</H3>
Writes the name followed by len bytes written in hex and a newline.
<pre> void _daveDump(char * name,uc*b,int len);</pre>
<H2>names for Objects</H2>
<pre> char * daveBlockName(uc bn);</pre>
<pre> char * daveAreaName(uc n);</pre>
<H2>
Data conversion convenience functions:
</H2>
<pre> int daveGetByte(daveConnection * dc);</pre>
<pre> float daveGetFloat(daveConnection * dc);</pre>
<pre> int daveGetInteger(daveConnection * dc);</pre>
<pre> unsigned int daveGetDWORD(daveConnection * dc);</pre>
<pre> unsigned int daveGetUnsignedInteger(daveConnection * dc);</pre>
<pre> unsigned int daveGetWORD(daveConnection * dc);</pre>
<pre> int daveGetByteat(daveConnection * dc, int pos);</pre>
<pre> unsigned int daveGetWORDat(daveConnection * dc, int pos);</pre>
<pre> unsigned int daveGetDWORDat(daveConnection * dc, int pos);</pre>
<pre> float daveGetFloatat(daveConnection * dc, int pos);</pre>
<pre> float toPLCfloat(float ff);</pre>
<pre> short daveSwapIed_16(short ff);</pre>
<pre> int daveSwapIed_32(int ff);</pre>
<H2>Newer data conversion convenience functions:</H2>
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.
<H3>Get a value from the position b points to</h3>
B is typically a pointer to a buffer that has
been filled with daveReadBytes:
<pre> int daveGetS8from(uc *b);</pre>
<pre> int daveGetU8from(uc *b);</pre>
<pre> int daveGetS16from(uc *b);</pre>
<pre> int daveGetU16from(uc *b);</pre>
<pre> int daveGetS32from(uc *b);</pre>
<pre> unsigned int daveGetU32from(uc *b);</pre>
<pre> float daveGetFloatfrom(uc *b);</pre>
<H3>Get a value from the current position</H3>
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.
<pre> int daveGetS8(daveConnection * dc);</pre>
<pre> int daveGetU8(daveConnection * dc);</pre>
<pre> int daveGetS16(daveConnection * dc);</pre>
<pre> int daveGetU16(daveConnection * dc);</pre>
<pre> int daveGetS32(daveConnection * dc);</pre>
<pre> unsigned int daveGetU32(daveConnection * dc);</pre>
<H2>
Get a value from a given position in the last result read on the connection dc.</h2>
<pre> int daveGetS8at(daveConnection * dc, int pos);</pre>
<pre> int daveGetU8at(daveConnection * dc, int pos);</pre>
<pre> int daveGetS16at(daveConnection * dc, int pos);</pre>
<pre> int daveGetU16at(daveConnection * dc, int pos);</pre>
<pre> int daveGetS32at(daveConnection * dc, int pos);</pre>
<pre> unsigned int daveGetU32at(daveConnection * dc, int pos);</pre>
<H3>put one byte into buffer b:</H3>
<pre> uc * davePut8(uc *b,int v);</pre>
<pre> uc * davePut16(uc *b,int v);</pre>
<pre> uc * davePut32(uc *b,int v);</pre>
<pre> uc * davePutFloat(uc *b,float v);</pre>
<pre> void davePut8at(uc *b, int pos, int v);</pre>
<pre> void davePut16at(uc *b, int pos, int v);</pre>
<pre> void davePut32at(uc *b, int pos, int v);</pre>
<pre> void davePutFloatat(uc *b,int pos, float v);</pre>
/**
Timer and Counter conversion functions:
**/
/*
get time in seconds from current read position:
*/
<pre> float daveGetSeconds(daveConnection * dc);</pre>
/*
get time in seconds from random position:
*/
<pre> float daveGetSecondsAt(daveConnection * dc, int pos);
/*
get counter value from current read position:
*/
<pre> int daveGetCounterValue(daveConnection * dc);
/*
get counter value from random read position:
*/
<pre> int daveGetCounterValueAt(daveConnection * dc,int pos);
/*
Functions to load blocks from PLC:
*/
<pre> void _daveConstructUpload(PDU *p,char blockType, int blockNr);
<pre> void _daveConstructDoUpload(PDU * p, int uploadID);
<pre> void _daveConstructEndUpload(PDU * p, int uploadID);
/*
Get the PLC's order code as ASCIIZ. Buf must provide space for
21 characters at least.
*/
#define daveOrderCodeSize 21
<pre> int daveGetOrderCode(daveConnection * dc,char * buf);
/*
connect to a PLC. returns 0 on success.
*/
<pre> int daveConnectPLC(daveConnection * dc);
/*
Read len bytes from the PLC. Start determines the first byte.
Area denotes whether the data comes from FLAGS, DATA BLOCKS,
INPUTS or OUTPUTS. The reading and writing of other data
like timers and counters is not supported.
DB is the number of the data block to be used. Set it to zero
for other area types.
Buffer is a pointer to a memory block provided by the calling
program. If the pointer is not NULL, the result data will be copied thereto.
Hence it must be big enough to take up the result.
In any case, you can also retrieve the result data using the get<type> macros
on the connection pointer.
FIXME: Existence of DB is not checked.
There is no error message for nonexistent data blocks.
There is no check for max. message len or
automatic splitting into multiple messages.
*/
<pre> int daveReadBytes(daveConnection * dc, int area, int DB, int start, int len, void * buffer);
/*
Write len bytes from buffer to the PLC.
Start determines the first byte.
Area denotes whether the data goes to FLAGS, DATA BLOCKS,
INPUTS or OUTPUTS. The writing of other data
like timers and counters is not supported.
DB is the number of the data block to be used. Set it to zero
for other area types.
FIXME: Existence of DB is not checked.
There is no error message for nonexistent data blocks.
There is no check for max. message len or
automatic splitting into multiple messages.
*/
<pre> int daveWriteBytes(daveConnection * dc,int area, int DB, int start, int len, void * buffer);
/*
Bit manipulation:
*/
<pre> int daveReadBits(daveConnection * dc, int area, int DB, int start, int len, void * buffer);
<pre> int daveWriteBits(daveConnection * dc,int area, int DB, int start, int len, void * buffer);
/*
PLC diagnostic and inventory functions:
*/
<pre> int daveReadSZL(daveConnection * dc, int ID, int index, void * buf);
<pre> int daveListBlocksOfType(daveConnection * dc,uc type,daveBlockEntry * buf);
<pre> int daveListBlocks(daveConnection * dc,daveBlockTypeEntry * buf);
/*
PLC program read functions:
*/
<pre> int initUpload(daveConnection * dc,char blockType, int blockNr, int * uploadID);
<pre> int doUpload(daveConnection*dc, int * more, uc**buffer, int*len, int uploadID);
<pre> int endUpload(daveConnection*dc, int uploadID);
/*
Multiple variable support:
*/
typedef struct {
int error;
int length;
uc * bytes;
} daveResult;
typedef struct {
int numResults;
daveResult * results;
} daveResultSet;
/* use this to initialize a multivariable read: */
<pre> void davePrepareReadRequest(daveConnection * dc, PDU *p);
/* Adds a new variable to a prepared request: */
<pre> void daveAddVarToReadRequest(PDU *p, int area, int DBnum, int start, int bytes);
/* Executes the complete request. */
<pre> int daveExecReadRequest(daveConnection * dc, PDU *p, daveResultSet * rl);
/* Lets the functions daveGet<data type> work on the n-th result: */
<pre> int daveUseResult(daveConnection * dc, daveResultSet rl, int n);
/* Frees the memory occupied by the result structure */
<pre> void daveFreeResults(daveResultSet * rl);
<pre> int daveInitAdapter(daveInterface * di);
<pre> int daveConnectPLC(daveConnection * dc);
<pre> int daveDisconnectPLC(daveConnection * dc);
<pre> int daveDisconnectAdapter(daveInterface * di);
<pre> int daveListReachablePartners(daveInterface * di,char * buf);
<pre> int _daveInitAdapterDummy(daveInterface * di);
<pre> int _daveConnectPLCDummy(daveConnection * dc);
<pre> int _daveDisconnectPLCDummy(daveConnection * dc);
<pre> int _daveDisconnectAdapterDummy(daveInterface * di);
<pre> int _daveExchangeDummy(daveConnection * dc,PDU * p1);
<pre> int _daveListReachablePartnersDummy(daveInterface * di,char * buf);
/* MPI specific functions */
#define daveMPIReachable 0x30
#define daveMPIunused 0x10
#define davePartnerListSize 126
<pre> int _daveListReachablePartnersMPI(daveInterface * di,char * buf);
<pre> int _daveInitAdapterMPI1(daveInterface * di);
<pre> int _daveInitAdapterMPI2(daveInterface * di);
<pre> int _daveConnectPLCMPI1(daveConnection * dc);
<pre> int _daveConnectPLCMPI2(daveConnection * dc);
<pre> int _daveDisconnectPLCMPI(daveConnection * dc);
<pre> int _daveDisconnectAdapterMPI(daveInterface * di);
<pre> int _daveExchangeMPI(daveConnection * dc,PDU * p1);
/* ISO over TCP specific functions */
<pre> int _daveExchangeTCP(daveConnection * dc,PDU * p1);
<pre> int _daveConnectPLCTCP(daveConnection * dc);
/*
make internal PPI functions available for experimental use:
*/
<pre> int _daveExchangePPI(daveConnection * dc,PDU * p1);
<pre> void _daveSendYOURTURN(daveConnection * dc);
<pre> void _daveSendLength(daveInterface * di, int len);
<pre> void _daveSendIt(daveInterface * di, uc * b, int size);
<pre> int _daveReadChars(daveInterface * di, uc *b, int tmo, int max);
/*
make internal MPI functions available for experimental use:
*/
<pre> int _daveReadMPI(daveInterface * di, uc *b);
<pre> void _daveSendSingle(daveInterface * di, uc c);
<pre> int _daveSendAck(daveConnection * dc, int nr);
<pre> int _daveGetAck(daveInterface*di, int nr);
<pre> int _daveSendDialog2(daveConnection * dc, int size);
<pre> int _daveSendWithCRC(daveInterface * di, uc *b, int size);
<pre> int _daveReadSingle(daveInterface * di);
<pre> int _daveReadOne(daveInterface * di, uc *b);
typedef uc * (*userReadFunc) (int , int, int, int, int *);
typedef void (*userWriteFunc) (int , int, int, int, int *,uc *);
extern userReadFunc readCallBack;
extern userWriteFunc writeCallBack;
void _daveConstructReadResponse(PDU * p);
void _daveConstructWriteResponse(PDU * p);
void _daveConstructBadReadResponse(PDU * p);
void _daveHandleRead(PDU * p1,PDU * p2);
void _daveHandleWrite(PDU * p1,PDU * p2);
//void _daveSendMPIAck2(daveConnection *dc);
#endif /* _nodave */
/*
Changes:
07/19/04 added the definition of daveExchange().
09/09/04 applied patch for variable Profibus speed from Andrew Rostovtsew.
09/09/04 applied patch from Bryan D. Payne to make this compile under Cygwin and/or newer gcc.
12/09/04 added daveReadBits(), daveWriteBits()
12/09/04 added some more comments.
12/09/04 changed declaration of _daveMemcmp to use typed pointers.
*/

150
doc/gettingStarted.html Normal file
View File

@@ -0,0 +1,150 @@
<H1>Getting started</H1>
<H2>Using the test programs</H2>
<H3>Intended purpose</H3>
<b>Libnodave</b> comes with a set of test programs. They shall serve the following purposes:
<li>Provide the user with a demostration of what Libnodave can do.</li>
<li>Provide a quick test for compatibility with user's hardware and configuration.</li>
<li>Provide a source code example as a template for your own applications.</li>
<li>The debug output, obtained with debug option (-d), provides valuable informatioon in case Libnodave fails with some hardware..</li>
<li>The benchmark options let you measure the time needed for transfers of short and long data blocks.</li>
<H4>Note on PASCAL source test programs:</H3>
Basic test programs are also available as Pascal sources. They are not as complete as their
C coded counterparts. Their main purpose is to test the interface unit <b>nodave.pas</b>.
<H3>Calling test programs</H3>
Just invoke the test programs without arguments.
They will print a list of possible arguments and options.
<H3>Which test program for which Setup?</H3>
<table border=1>
<tr><th>CPU</th><th>Connection</th><th>Generell Tests</th><th>Load Program into CPU</th></tr>
<tr><th>S7 300 or 400</th><th>Serial with MPI adapter cable</th><th>testMPI</th><th>testMPIload</th></tr>
<tr><th>S7 200</th><th>Serial with PPI adapter cable</th><th>testPPI</th><th>testPPIload</th></tr>
<tr><th>S7 300 or 400</th><th>Ethernet CP343/443</th><th>testISO_TCP</th><th>testISO_TCPload</th></tr>
<tr><th>S7 200</th><th>Ethernet CP243</th><th>testISO_TCP -2</th><th>testISO_TCPload -2</th></tr>
<tr><th>S7 300 or 400</th><th>Ethernet with IBH/MHJ-NetLink Gateway</th><th>testIBH</th><th>testMPI_IBHload</th></tr>
<tr><th>S7 200</th><th>Ethernet with IBH/MHJ-NetLink Gateway</th><th>testPPI_IBH</th><th>testPPI_IBHload</th></tr>
</table>
<H3>Options to make test programs work with certain configurations:</H3>
<H4>MPI transport with MPI adapter:</H4>
<li>--mpi=[number] Uses number as the MPI address of the PLC. Default is 2.</li>
<li>--local=[number] Uses number as the local MPI address of the adapter. Default is 0.</li>
<li>-2 Uses another variant of the MPI protocol. Try if you connect to adapter/PLC.</li>
<li>-[some numbers] Use dfferent MPI/Profibus speeds.</li>
<H4>ISO over TCP transport with CP343/CP443:</H4>
<li>--slot=[number] Uses number as the slot address of the PLC. Default rack 0, slot 2.</li>
<H4>ISO over TCP transport with CP243:</H4>
<li>-2 Fakes a MicroWin connection request. This is currently mandatory for CP243.
It will switch the CFG LED on.</li>
<H3>What can the test programs do?</H3>
If invoked with the connection as the only argument, all test programs will read some data from
the memory area of Flags (also known as Merkers).They try to read FD0,FD4 and FD8 as DWORDS and
FD12 as real.<br>Depending on the contents of this memory, the results may or may no seem
reasonable.The obtained values are just the same as if you would observe these variables in Step7
using the display formats signed,signed,signed and floating point.<br>
If you specify the option -w, the test programs increment the data, write it back to your PLC
<font color=red>(Attention! This changes PLC internal memory!)</font> and read it again, thus demonstrating the effect of
changes.<br>
If you specify the option -c, the test programs write 0 to these memory locations. <font color=red>
(Attention! This changes PLC internal memory!)</font>. This is useful if the current memory contents
doesn't make much sense when displayed in the above mentioned format.<br>
If you specify the option -b, the test programs try to do block read benchmark tests. Combining -b and
-w will also do write benchmark tests.<br>
-r option tries to put your PLC into RUN mode.<font color=red>(Attention! May start actions on machinery!).</font><br>
-s option tries to put your PLC into STOP mode.<font color=red>(Attention! Will interrupt running machinery!).</font><br>
--readout option tries to readout program blocks from the PLC and will store them in files named
like OB1.mc7.<font color=red>(Attention! I got reports, that this produces ill effects on S7-400!).</font><br>
-z reads System State Lists (<b>S</b>ystem-<b>Z</b>ustands<b>l</b>isten from the PLC. These lists exist
only in 300/400 family PLCs and provide diagnostic information. Please refer to Siemens documentation about
the meaning of IDs and indices.<br>
<H3>Test programs to load blocks into CPU</H3>
<li>This is a quite experimental feature.
First you will need correctly formed program blocks stored in files. A possible source are files
previously read out using --readout. You cannot get them from Step7 as Step7 stores program
block in a data base. It has not been tested but may be so that third party programming software
that stores program blocks in files uses the same file format.<br>
Loading of SDBs (<b>S</b>ystem <b>D</b>ata <b>B</b>locks) is highly dependent of the sequence of
block numbers.
<H2>Programming a basic application</H2>
<H4>Preparations</H4>
The main purpose of this library is to read and write data from and to Siemens PLCs.
To do so, you need to establish a connection to the PLC.
First, you need to configure a serial port of your computer or establish a TCP connection.
This connection is represented by the type _daveOSserialType, which contains file descriptors
in case of Unix-like systems, handles in case of windows and what other systems supported in
the future might use for this purpose.
use setport to initialize the members of a <a href=daveOSserialType.html>_daveOSserialType</a> to something representing
a configured serial connection:
<pre>
fds.rfd=<a href=setport.html>setPort</a>(argv[adrPos],"38400",'O');
</pre>
for serial connections or:
<pre>
fds.rfd=<a href=openSocket.html>openSocket</a>(102, IPaddress_of_CP);
</pre>
or
<pre>
fds.rfd=<a href=openSocket.html>openSocket</a>(1099, IPaddress_of_IBH-NetLink);
</pre>
for TCP connections. Then do:
<pre>
fds.wfd=fds.rfd;
</pre>
With the initialized _daveOSserialType, you will initialize a structure of type
<a href=daveInterface.html>daveInterface</a>,
representing the physical connection to a PLC or a network of PLCs (e.g. like MPI).
<pre>
di=<a href=daveInterface.html>daveNewInterface</a>(fds, "IF1", localMPI, daveProtoXXX, daveSpeedYYY);
</pre>
With the resulting daveInterface structure, you can initialize an adapter, if one is used:
<pre>
res =daveInitAdapter(di);
</pre>
While currently only MPI-adapters and IBH-NetLinks really need this initialization procedure, it is save to use
daveInitAdapter() with any protocol type. If it has no meaning for the protocol used, it is
mapped to a dummy procedure that returns allways success.
After successfully initializing your adapter, you can retrieve a list of reachable partners
on an MPI network. The function takes the daveInterface structure and a pointer to a buffer
of sufficient length as arguments. It returns the real length of the list. If the partners cannot
be listed with the protocol used, it just returns a length of 0.
<pre>
listLength = daveListReachablePartners(di,buf1);
</pre>
After successfully initializing your adapter, you can establish a connection to a certain PLC
on the network. To do so, you will first initialize a structure of
type <a href=daveConnection.html>daveConnection</a>,
representing the logical connection to a single PLC.
<pre>
dc =daveNewConnection(di, MPI_address, Rack, Slot);
</pre>
With the resulting daveConnection structure, you need to really connect the PLC:
<pre>
res =daveConnectPLC(dc);
</pre>
<H4>Exchanging values:</H4>
Once you have established a connection to your PLC, you can read and write values:
<pre>
res=<a href=daveReadBytes.html>daveReadBytes</a>(dc, <a href=area.html>AREA</a>, area_Number, start_address, length, buffer);
</pre>
<pre>
res=<a href=daveWriteBytes.html>daveWriteBytes</a>(dc, <a href=area.html>AREA</a>, area_Number, start_address, length, buffer);
</pre>
Usually, you will have to <a href=conversions.html>convert</a> byte sequences from and to the buffer
to use the data in your application.<br>
After you are done with your data exchanges, call:
<pre>
daveDisconnectPLC(dc);
</pre>
To disconnect from the PLC and
<pre>
daveDisconnectAdapter(di);
</pre>
to disconnect from the Adapter.
Now close the serial or TCP/IP connection using the appropriate system calls for your OS.
<H1>Advanced data exchange</H1>
<a href=readmultiple.html>Read multiple items with a single transaction.</a><br>
<a href=bitfunctions.html>Read and set single bits.</a><br>
<H1>Other features</H1>
<a href=SZL.html>Read diagnostic info (300 and 400 only).</a><br>
<a href=upload.html>Load program code from PLC.</a><br>
<a href=upload.html>Load program code into PLC.</a><br>

23
doc/index.html Normal file
View File

@@ -0,0 +1,23 @@
<html>
<head>
<title></title>
<meta content="">
<style></style>
</head>
<body>
<H1>The beginnings of a documentation for <b>LIBNODAVE</b></H1>
<H2>Purpose</H2>
LIBNODAVE provides a way to exchange data with Siemens PLCs of S7-200, 300 and 400 families. It has also been successfully tested with a <a href="http://www.vipa.de">VIPA</a> Speed7 CPU. It should also be useable with S7-compatible PLCs from SAIA.<br>
Data exchange comprises all memory areas and variables you canaccess in your PLC programs, e.g. flags, data blocks,input and output image memory, timers and counters.<br>
Additionally, <b>LIBNODAVE</b>provides access to functions that are in the scope of programming software, e,g. reading diagnostic information, read program blocks from a PLC and write them to a PLC, start and stop a PLC.
<H2>Licensing</H2>
<b>LIBNODAVE</b> is free software under GPL and LGPL.
<H2>Availability</H2>
<b>LIBNODAVE</b> is currently available for UNIX and Win32. It comes with precompiled libraries for LINUX and Windows. You may port it yourself on any system providing a C compiler.<br>
Get the latest version from <a href="http://libnodave.sf.net">Sourceforge</a>.
<H2>Basic usage</H2>
<a href=gettingStarted.html>getting started</a>
<H2>FAQ</H2>
<a href=FAQ.html>frequently asked questions</a>
</body>
</html>

9
doc/initAdapter.html Normal file
View File

@@ -0,0 +1,9 @@
With the resulting daveInterface structure, you need to initialize an adapter, if one is used:
<pre>
res =daveInitAdapter(di);
</re>
While currently only MPI-adapters really need this initialization procedure, it is save to use
daveInitAdapter() with any protocol type. If it has no meaning for the protocol used, it is
mapped to a dummy procedure that returns allways success.
After successfully initializing your adapter, you can retrieve a list of reachable partners

13
doc/multiplePLCs.html Normal file
View File

@@ -0,0 +1,13 @@
If you want to use more than one PLC on an MPI network, you would have to do:
<pre>
dc2 =daveNewConnection(di, 2nd_MPI_address, 0, 0);
res =daveConnectPLC(dc2);
</pre>
If you want to use more than one PLC over TCP connections, you would have to setup a second
daveOSserialType, daveInterface and daveConnection:
<pre>
fds2.rfd=openSocket(102, IPaddress_of_CP);
di2 =daveNewInterface(fds2, "IF2", 0, daveProtoISO_TCP, 0);
dc2 =daveNewConnection(di2, 0, 0, 2);
res =daveConnectPLC(dc2);
</pre>

14
doc/openSocket.html Normal file
View File

@@ -0,0 +1,14 @@
<H1>openSocket</H1>
An OS specific routine to establish a TCP/IP connection to a partner.
There are different implementations for LINUX and WIN32.
While the OS specific implementation of openSocket() is part of LIBNODAVE dynamic library,
you could use your own routine and copy the resulting file descriptor/handle to the
members of your <a href=daveOSserialType.html>_daveOSserialType</a> structure.
<pre>
int openSocket(const int port, const char * peer);
</pre>
<li>port is the port number for the protocol, usually 102 for ISO over TCP or 1099 for the IBH/MHJ NetLink protocol.</li>
<li>peer is the IP address of the PLC/CP as a string, e.g. 192.168.0.3 .</li>
<h3>Note:</h3>
With regard to <b>Libnodave</b>, you are free to use other port numbers. While you cannot change port numbers on S7 devices or IBH NetLinks, this can be useful with port redirecting software like <br>rinetd</b>, which makes a gateway listen on several ports and then redirects connections to other IP adresses.<br>
This is the reason, why default port numbers are not hard coded in <b>Libnodave</b>.

62
doc/pdu.html Normal file
View File

@@ -0,0 +1,62 @@
<H1>PDU (protocol data unit)</H1>
This is the central part of packets exchanged in S7-Communication. A PDU cosists of:
<li>A 10 or 12 byte header</li>
<li>A parameter area</li>
<li>A data area</li>
Header:
<table>
<tr><th>Position</th><th>meaning</th><th>possible values</th></tr>
<tr><td>0</td><td>allways 0x32</td></tr>
<tr><td>1</td><td>type</td><td>1,2,3 or 7</td></tr>
<tr><td>2,3</td><td>unknown</td><td>0</td></tr>
<tr><td>4,5</td><td>sequence number</td><td></td></tr>
<tr><td>6,7</td><td>length of parameters</td><td></td></tr>
<tr><td>8,9</td><td>length of data</td><td></td></tr>
<tr><td>10,11</td><td>error code</td><td></td></tr>
</table>
Parameters:
<table>
<tr><th>Position</th><th>meaning</th><th>possible values</th></tr>
<tr><td>0</td><td>a function number</td></tr>
<tr><td>rest</td><td>depends on function number</td><td></td></tr>
</table>
Data:
<table>
<tr><th>Position</th><th>meaning</th><th>possible values</th></tr>
<tr><td>rest</td><td>depends on function number</td><td></td></tr>
</table>
Parameters for read request:
<table>
<tr><th>Position</th><th>meaning</th><th>possible values</th></tr>
<tr><td>0</td><td>function number for read</td><td>4</td></tr>
<tr><td>1</td><td>number of items to read</td><td>1..20</td></tr>
<tr><td>2..</td><td>item adresses, 12 byte each</td><td></td></tr>
</table>
Forming the item address:
<table>
<tr><th>Position</th><th>meaning</th><th>possible values</th></tr>
<tr><td>0,1,2</td><td>unknown</td><td>allways 0x12, 0x0a, 0x10</td></tr>
<tr><td>3</td><td>transport size or unit size</td><td>1=single bit, 2=byte, 4=word</td></tr>
<tr><td>4,5</td><td>length in byte</td><td></td></tr>
<tr><td>6,7</td><td>number of data block</td><td>0 for ares other than data block</td></tr>
<tr><td>8</td><td>area code</td><td>see area</td></tr>
<tr><td>9,10,11</td><td>Start address in bits.</td><td>multiples of 8, if unit size is not bits</td></tr>
</table>
read response:
<table>
<tr><th>Position</th><th>meaning</th><th>possible values</th></tr>
<tr><td>0</td><td>function number for read</td><td>4</td></tr>
<tr><td>1</td><td>number read items</td><td>1..20</td></tr>
<tr><td>2..</td><td>items, 4 byte "data header" +data each</td><td></td></tr>
</table>
Data header:
<table>
<tr><th>Position</th><th>meaning</th><th>possible values</th></tr>
<tr><td>0</td><td>return code</td><td>0xFF means ok, data follows after this header. Other codes give reasons why no data is returned.</td></tr>
<tr><td>1</td><td>transport size or unit size</td><td>4=single bit, 9=byte</td></tr>
<tr><td>2,3</td><td>length in bits</td><td></td></tr>
</table>

51
doc/readmultiple.html Normal file
View File

@@ -0,0 +1,51 @@
<H1>Read multiple items</H1>
The purpose of this mechanism ist to read multiple blocks of bytes from different start addresses and/or memory
areas with a single request to the PLC.
<H2>Basics:</H2>
First, you have to prepare an "empty" read request, i.e. one that doesn't contain the address of an
item. You need a variable of type PDU to store the request. To this request, you add the desired
items using the same parameters you would specify to <a href=daveReadBytes.html> daveReadBytes</a>.
You may add up to 20 difeerent items (limit introduced by Siemens PLCs) provided that the result
data fits into a single response PDU. When you have added all desired items, call
daveExecReadRequest which performs the actual data exchange with the PLC. Example:
<pre>
PDU p;
daveResultSet rs;
davePrepareReadRequest(dc, &p);
daveAddVarToReadRequest(&p,daveInputs,0,0,1);
daveAddVarToReadRequest(&p,daveFlags,0,0,4);
daveAddVarToReadRequest(&p,daveDB,6,20,2);
daveAddVarToReadRequest(&p,daveFlags,0,12,2);
res=daveExecReadRequest(dc, &p, &rs);
</pre>
Now the daveResultSet should contain a result for each item. Each result contains error information,
length information and the resulting byte array. You can use these results in two ways: Either
access the structure daveResult directly or use:
</pre>
daveUseResult(daveConnection *, daveResultSet * rs, int number);
</pre>
This will set the internal result pointer of the <a href=daveConnection.html>daveConnection</a>
to the result byte array. After doing this, the normal <a href=conversions.html>conversion functions</a> can be used
to transfer single values to C variables. Example:
<pre>
res=daveUseResult(dc, rs, 0); // first result
if (res==0) {
a=daveGetU8(dc);
printf("%d\n",a);
} else
printf("*** Error: %s\n",daveStrerror(res));
</pre>
<pre>
res=daveUseResult(dc, rs, 1); // 2nd result
if (res==0) {
a=daveGetInteger(dc);
printf("%d\n",a);
} else
printf("*** Error: %s\n",daveStrerror(res));
</pre>
If you do not need the results any more, call:
<pre>
daveFreeResults(&rs);
</pre>
This will free the memory occupied by the single results (but not by the resultSet itself),
leaving you with an empty reultSet that can be reused in the next multi item read.

13
doc/setport.html Normal file
View File

@@ -0,0 +1,13 @@
<H1>setport</H1>
An OS specific routine to configure and open a serial port.
There are different implementations for LINUX and WIN32.
While the respective implementation of setport() is part of LIBNODAVE dynamic library,
you could use your own routine and copy the resulting filedescriptor/handle to the
members of your <a href=daveOSserialType.html>_daveOSserialType</a> structure.
<pre>
int setPort(char * name, char* baud,char parity);
</pre>
<H4>Parameters:</H4>
<li>name: The name of the serial port known to the OS, e.g. COM1 or /dev/ttyS0.</li>
<li>baud: A baudrate.</li>
<li>parity: One of the letters E,e,O,o,N or n meaning even, odd or none.</li>

73
doc/speedissues.html Normal file
View File

@@ -0,0 +1,73 @@
<H1>Conversion routines</H1>
The buffers used by daveReadBytes() and daveWriteBytes() will contain a copy of the PLC memory area. This means: If you read from the beginning of DB2, the buffer will contain a byte for byte copy of DB2. The values are the same and in the same order, as what you get when you observe byte variables in Step7. Example:<br>
<table>
<tr><td>DB20.DBB0</td><td>26</td></tr>
<tr><td>DB20.DBB1</td><td>37</td></tr>
<tr><td>DB20.DBB2</td><td>48</td></tr>
<tr><td>DB20.DBB3</td><td>15</td></tr>
<tr><td>DB20.DBB4</td><td>16</td></tr>
<table>
You are free to interpret these values as single bytes or multibyte values,the same way as you can do this in Step7 AWL.:<br>
Also it is unusual, you can load a real value from DB20.DBD1:<br>
<pre>
L DB20.DBD1
L 2.0
*R
</pre>
A further complication results from the fact that Siemens PLCs store multibyte values beginning with the most significant byte (big endian) while Intel based PCs store the least significant byte first (little endian).<br>
It is not possible to convert the byte order in the daveReadBytes() or daveWriteBytes() fuctions
because the start position of each multibyte value is not known then.<br>
You are free to place such values at arbitrary byte addresses in your PLC program.
The same adresses must in turn be used to retrieve values from the copy of PLC memory.
If you have a data block DB2 with the following layout:<br>
<table>
<tr><td>DBB 0</td><td>BYTE</td></tr>
<tr><td>DBD 1</td><td>DWORD</td></tr>
<tr><td>DBD 5</td><td>REAL</td></tr>
</table>
You can retrieve the single values in three ways:<br>
1. From the intenal buffer. After a successful call, an internal pointer points to the 1st byte.
Now use daveGetU8(dc) to get the value of the first byte as an unsigned value. The internal buffer pointer
is incremented by 1, now pointing to the copy of DBD1. Use daveGetS32(dc) to get the value of the
of the next 4 bytes as a signed value. The internal pointer is incremented by 4, now pointing to
the copy of DBD5. Use daveGetFloat(dc) to get the value of the next 4 bytes as a single precision
float.<br>
2. From the internal buffer, specifying a position. Use daveGetU8at(dc,0) to get the value of the
first byte as an unsigned value. Next use daveGetS32at(dc,1) to get the value of the 4 bytes
starting at 1 as a signed value. Finally, use daveGetFloatat(dc,5) to get the value of the 4 bytes
starting at 5 as a single precision float. You may perform these operation in any order and also
repeat them.<br>
3. From a user buffer. Use daveGetU8from(buffer) to get the value of the first byte as an
unsigned value. Use daveGetS32from(buffer+1) to get the value of the 4 bytes at buffer+1, i.e.
DBD 1, as a signed value. Use daveGetFloatat(buffer+5) to get the value of the 4 bytes starting
at buffer+5 as a single precision float, i.e. DBD5.<br>
The conversion functions are named after the bit length and signedness they assume:
<table>
<tr><th>int buffer</th><th>int buffer+pos</th><th>buffer pointer</th><th>size</th><th>signed</th><th>C-return type</th><th>Pascal ret type</th></tr>
<tr><td>daveGetU8</td><td>daveGetU8at</td><td>daveGetU8from</td><td>8 bit=1 byte</td><td>no</td><td>int</td><td>longint</td></tr>
<tr><td>daveGetS8</td><td>daveGetS8at</td><td>daveGetS8from</td><td>8 bit=1 byte</td><td>yes</td><td>int</td><td>longint</td></tr>
<tr><td>daveGetU16</td><td>daveGetU16at</td><td>daveGetU16from</td><td>16 bit=2 byte</td><td>no</td><td>int</td><td>longint</td></tr>
<tr><td>daveGetS16</td><td>daveGetS16at</td><td>daveGetS16from</td><td>16 bit=2 byte</td><td>yes</td><td>int</td><td>longint</td></tr>
<tr><td>daveGetU32</td><td>daveGetU32at</td><td>daveGetU32from</td><td>32 bit=4 byte</td><td>no</td><td>unsigned int</td><td>longint</td></tr>
<tr><td>daveGetS32</td><td>daveGetS32at</td><td>daveGetS32from</td><td>32 bit=4 byte</td><td>yes</td><td>int</td><td>longint</td></tr>
<tr><td>daveGetFloat</td><td>daveGetFloatat</td><td>daveGetFloatfrom</td><td>32 bit=4 byte</td><td>yes</td><td>float</td><td>single</td></tr>
</table>
There had been an older set of those functions named after data types, e.g.
daveGetDWORD(). Those functions should not be used any more, as there names might be
misunderstandable between PLC and C or other programming languages. They are still supported
for compatibility with older versions. These functions had been inlined in earlier versions
but are now not inlined by default, because other languages than C cannot make use of inline
definitions in a C header file.
<h4>Notes:</h4>
Most commercial libraries handle the conversion issue differently: They provide functions to read one or a set of words, one or a set of long words, one or a set of reals. On the first glance, this might seem more convenient and it <b>is</b> as long as it can be applied to PLC memory areas containing only elements of the same type and size. But when you have to deal with data of mixed type and size, you would have to use another call to daveReadBytes() each time the type or size differs from the former. And each call contributes the overhead of the protocol and the response time of the PLC. Other libraries provide a way to read multiple items with a single call. So does <b>Libnodave</b>. You could also use it to retrieve data located on different boundaries. But this is limited to 20 items by the PLC. And it introduces some overhead as an address has to be transmitted for each item in the request. Use read multiple items to access data from different DBs or other memory areas when the combined results will fit in a single response.
<table>
<tr><th>When you want to read</td><th>do</th><th>remark</th></tr>
<tr><td>DB20.DBD0..DBD20</td><td>read 24 bytes starting at DBB0</td></tr>
<tr><td>DB20.DBD0..DBD8 and DBD20</td><td>read 24 bytes starting at DBB0</td><td>just do not evaluate bytes 11 to 19 of result.</td></tr>
<tr><td>DB20.DBD0..DBD8 and DBD120</td><td>either read 124 bytes starting at DBB0</td><td>just do not evaluate bytes 11 to 119 of result.</td></tr>
<tr><td></td><td>or use multiple read on: item1:DB20.DBD0..DBD8, item2:DB20.DBD120 </td><td>you have to deal with the result set.</td></tr>
<tr><td>DB20.DBD0..DBD8 and DB21.DBD120</td><td>use multiple read on: item1:DB20.DBD0..DBD8, item2:DB21.DBD120 </td><td>this is the only way to read from different DBs with a single request/reponse.</td></tr>
<tr><td>DB20.DBD0..DBD118 and DB21.DBD4..DBD80</td><td>use two calls to daveReadBytes</td><td>this is the only way if the two combined results will not fit into a single PDU.</td></tr>
<table>

32
doc/writemultiple.html Normal file
View File

@@ -0,0 +1,32 @@
<H1>Write multiple items</H1>
The purpose of this mechanism ist to write multiple blocks of bytes from different start addresses and/or memory
areas with a single request to the PLC.
<H2>Basics:</H2>
First, you have to prepare an "empty" write request, i.e. one that doesn't contain the address of an
item. You need a variable of type PDU to store the request. To this request, you add the desired
items using the same parameters you would specify to <a href=daveWriteBytes.html> daveWriteBytes</a>.
You may add up to 20 different items (limit introduced by Siemens PLCs) provided that the result
data fits into a single response PDU. When you have added all desired items, call
daveExecWriteRequest which performs the actual data exchange with the PLC. Example:
<pre>
PDU p;
daveResultSet rs;
davePrepareWriteRequest(dc, &p);
daveAddVarToWriteRequest(&p,daveInputs,0,0,1,buffer);
daveAddVarToWriteRequest(&p,daveFlags,0,0,4,buffer);
daveAddVarToWriteRequest(&p,daveDB,6,20,2,buffer);
daveAddVarToWriteRequest(&p,daveFlags,0,12,2,buffer);
daveAddBitVarToWriteRequest(&p,daveFlags,0,12,1,buffer);
res=daveExecWriteRequest(dc, &p, &rs);
</pre>
Now the daveResultSet should contain a result for each item. Each result contains error information only. Different from daveExecReadRequest, the other member fields are 0.
You can use these results accessing the structure daveResult directly.
<pre>
printf("*** Error: %s\n",daveStrerror(rs.results[2].error));
</pre>
If you do not need the results any more, call:
<pre>
daveFreeResults(&rs);
</pre>
This will free the memory occupied by the single results (but not by the resultSet itself),
leaving you with an empty reultSet that can be reused in the next multi item read.

172
faq.txt Normal file
View File

@@ -0,0 +1,172 @@
Questions I have been asked and answers:
Q: My VB,.NET or Delphi application doesn't work. I suppose libnodave doesn't work with my
hardware or doesn't work at all. Can you help me?
A: You can most easily find out whether Libnodave works with your hardware, PLC, adapter
etc: try the precompiled test programs. If they don't work (trying different options when
suggested), it's clearly libnodave's fault.
If they work, it's your application (or there is a little chance, the problem is in the
interface to the language you use).
Q: Can you help me?
A: Before asking me, try out the test programs as described above. When they don't work for
you, send me the output with debug option, e.g. do:
testMPI -d COM1 >debugout.txt
If they work, but your application doesn't, do:
Insert "daveSetDebug(daveDebugAll)" before calling anything else from libnodave.
Then do:
yourappliation >debugout.txt
For those people who grew up with mice and don't know the command line:
This works even with MS-Excel. Create a spreadsheet, import the VBA module,
- uncomment the "call daveSetDebug(daveDebugAll)" at the begin of sub initialize,
- save the sheet.
- then, from commandline (dos box, cmd) do:
excel testsheet >debugout.txt
All debug output (otherwise invisible) goes into debugout.txt.
When time falls short, I shall discard mails not containig debug output!
Q: What mail format do you prefer?
A: Ok, nobody asked that, but I DO prefer PLAIN TEXT.
Q: I want to read out whether my PLC is in RUN or STOP mode. Is there a function for that?
A: No, not a specialized one. For S7-300/400, use daveReadSZL to read the diagnostic lists.
This is what Step7 does. You will find information about IDs and indices in Siemens
documentation. The state of all the CPU LEDs is in ID 25 (19hex) index 0.
With S7-200, the information is somewhere in the system data.
Q: I trying to trace libnodave with a debugger. I need help?
A: There is no reason to use a debugger on libnodave unless you suspect one of the
following things:
- memory leaks
- range overflows, on numbers or array indices
- problems with passing parameters to library functions
In the latter case, better recompile libnodave after uncommenting #define DEBUG_CALLS in
nodave.c.
To find problems with PLCs and adapters, the FAR better way is to use
setDaveDebug(daveDebugAll). This will show you what is sent and received from PLC/adapter
and it will show you the relevant information instead of extracting it from memory
contents or processor registers. Thus, data exchange with the PLC is still carried out with
normal timing. With a debugger, the code might send one part of a message and then pause
longer than PLC side timeouts.
Q: Can you give documentation about S7 communication or MPI protocol?
A: No, I can't. What I know about it is from reverse engineering. This means sniffing lots of
packets, trying to make sense of them and trying to generate them with my own code.
If things are named so or so in libnodave this reflects my current hypothesis. I could
try to write documentation, but that would allways be a step behind the code: The code
is what can be proven against existing hardware, i.e. reaity. The docs cannot. And I think
the code is fairly well documented...
Q: So why do you have these complicated structures?
A: What people might have found complicated are probably the pointers to protocol dependent
functions. In fact, they make the library more simple in two ways:
- First, they separate packet forming from transport. If someone finds out how to do some
function that can be done with a Simatic S7, it is sufficient to implement the packet
forming part and it will most likely work with any transport protocol. Vice versa, if there
would be a new transport mechanism, probaly all functions will work with it as soon as the
transport functions are implemented. Version 0.8 clearly shows that by implementing a
transport vie s7online.dll.
- Commercial libraries I looked at usually have separate sets of functions for the 200
and the 300/400 family. Libnodave hasn't, so you have less to learn about the API while your
code can do more without any modification.
Q: Why do you export these structures if you say they are meaningless for applications?
A: They are not completely meaningless. There are also a lot of "intermediate step functions"
that are exported from the .dll or.so which are normally of no use to end users.
- All this is there - in the spirit of open source - for those who want to do their
own experiments. It may give you possibilities I did not think of.
- At least once, I could help a user implementing a new function (daveForce200). He only
had to add about 20 lines to his application which in turn called intermediate steps
of packet forming and analysis, like _daveAddData.
Q: Why aren't there functions like readOutputs, readInputs, readData ?
A: Other libraries usually have separate functions to read from inputs, outputs, flags,
data blocks and so on. Libnodave hasn't. Again, you have to learn less about the API.
Q: Why aren't there functions like readBytes, readIntegers, readDWords, readFloat ?
A: Other libraries often have separate functions to read integers, dwords or floats.
Libnodave hasn't. The reason is: If you have a DB (or memory area) containg mixed data
types, with those function you can only read parts of such a block. Or you use readBytes
to read the memory as raw bytes and you are left alone wit the conversion.
Libnodave generally reads an entire block of data as bytes and then provides you
with conversion functions to convert it to PLC data types and byte order.
Q: I try to compile my source code which includes nodave.h. Why do I get:
"#error Fill in what you need for your OS or API" ?
A: Libnodave has to use different functions to read byte data from serial ports or TCP/IP streams
depending on the operating system. You have to specify which OS you compile for.
Define BCCWIN for Windows and LINUX for Linux or other Unix style operating systems. I do
that using -DBCCWIN or -DLINUX when I call the compilers out of a Makefile. If you use an IDE
(integrated development environment), refer to the IDE's documentation to learn where to do
this. If you can't find out, you can help yourself putting the #define into your code (which
means your code will not be portable to other OS without modification). example:
...
#define BCCWIN // if you work on windows
#include nodave.h // or nodavesimple.h, but NEVER both
#include setport.h // if your code works with serial connections
#include openSocket.h // if your code works with TCP/IP
...
Q: I try to compile libnodave from source code. There are problems.
A: - You do not need to compile libnodave yourself. You won't do it with other DLLs (windows)
or .so (Linux shared libraries). Normally, you will link such libraries dynamically with
your code.
- Nevertheless, you have the sources and you may chose to recompile it or recompile only the
parts you need, etcetera.
- You should know that S7 uses Motorola (big endian, high byte first) byte order (endianness)
while Intel processors use Intel (little endian, low byte first). On Intel and similar
machines, libnodave has to convert multibyte values. This is done in daveGetS2(), daveGetU2(),
daveGetFloat() and friends. The conversion code is only compiled in if you #define
DAVE_LITTLE_ENDIAN. Otherwise, big endian is assumed.
- Be aware that I do compile releases of libnodave using gcc (for Linux) and MSVC++
(for windows). I use the script buildall to build Linux and Windows release versions and test
programs on a Linux box. Buildall uses Linux make with MAKEFILE.VC.WINE to invoke the MSVC++
compiler for windows versions. There is also a MAKEFILE.VC which should work on a windows
system using nmake, but it may be out of date :-(. If in doubt about compiler options or
source files involved, refer to MAKEFILE.VC.WINE!
Q: I want to compile libnodave for another OS. What adaptions shall I have to make?
A: - I suppose your OS is 32 bit and your compiler treats int as 32 bit. If not, you are in
trouble!
- You need to provide functional replacements for setport.c or setportw.c and openSocket.c or
openSocketw.c
- nodave.h: You need to define a record _daveOSserialType containing members:
rfd: a data type that can refer to a serial port or a TCP socket for read access
wfd: a data type that can refer to a serial port or a TCP socket for write access
- nodave.c: You need to code functions stdread() and stdwrite() to read and write a number of
bytes from/to a serial port to/from a buffer.
Q: Is there a function to read PLC Data structure of a data block? The variables
and their data type ?
A: No. The structure information is only available to Step7 BEFORE compilation. It is NOT stored
in the PLC itself. Same situation if you open a new project and load the code from PLC into
programmer: Your DBs will be nothing more than arrays of bytes.
Q: I have problems writing inputs. Can you help me?
A: Well, there are 2 types of inputs: 1. Peripheral adresses. This is what you read with daveP
or, in a PLC program, with L PIW <address>. Accesses to these addresses are real hardware
read or write accesses and there is no way to write to input hardware.
2. Input/Output image memory. This is how programmers normally access Siemens I/O like in:
L IW <address>
or
A I 2.5
Accesses to these addresses do NO access the hardware but a special memory area. The
inputs are copied to this memory in each cycle before the application program is executed.
If you write to this memory area using daveInputs, the memory will be overwritten before next
cycle. Whether your PLC program or your application of Libnodave will "see" the values written
to this memory is a matter of time race.
Q: Is it possible to write a number of bits (up to 3) simultaneously to the PLC?
I want to avoid using "read-modify-write" techniques and make the write as "atomic"
as possible.
A: daveWriteBits() does provide an "atomic" write function, but only for a single bit.
The length parameter to daveWriteBits() has to be one, other values produce errors with
CPUs that I know of. So why is this parameter there? It corresponds to a field in the message
which contains the number of bits to read or write.
Q: Can this (an atomic read to 3 bits) be done using daveWriteBits() or do I have to use multiple
write "items" within the same PDU?
A: It CANNOT be done with daveWriteBits() with the CPUs I know. Whether writing with a PDU containing
multiple items is "atomic" or not I don't know. If you can modify the PLC program, I think the best
solution would be locating the 3 bits into a byte the rest of which remains unused.

49
filelist Normal file
View File

@@ -0,0 +1,49 @@
accepter.c include file for servers
benchmark.c include file for benchmarks
ibhtest7.c Partly simulates an IBH-NetLink
isotest4.c Partly simulates an Ethernet CP x43
libnodave.so Precompiled dynamic library
libnodave.DEF Defines names to export for the Microsoft linker
loadPLC
log.h
Makefile Makefile for LINUX/UNIX
MAKEFILE.MAK Makefile for WIN32/BCC
nodave.c Main library source code file
nodave.h Library header file
nodavesimple.h Simplified Library header file. This file is not intended to be used as the
header file for your application. It is there to give a hind to people who
want to use libnodave from applications in VB or dot.net what is the minimum
they must make known to these languages for simple applications. I use it in
test applications only to verify that is sufficient indeed.
openSocket.c Opens a TCP/IP connection. Library source code file
openSocket.h Library header file (UNIX only)
openSocketw.c Library header file (Win32 only)
pascal Pascal glue unit and example programs
README
Readme.win
readoutISO_TCP Readout program blocks from PLC, ISO over TCP protocol
readoutMPI Readout program blocks from PLC, MPI protocol, serial interface
readoutMPIIBH Readout program blocks from PLC, MPI protocol, ethernet with IBH-NetLink
readoutPPI Readout program blocks from PLC, PPI protocol, serial interface
runStopISO_TCP Make PLC run or stop, ISO over TCP protocol
runStopMPI Make PLC run or stop, ISO over TCP protocol
runStopPPI Make PLC run or stop, ISO over TCP protocol
sampIBH.c include file for NetLink simulation
setport.c Opens a serial interface. Library source code file
setport.h Library header file (UNIX only)
setportw.c Library header file (Win32 only)
testIBH General test program for MPI over IBH/MHJ-NetLink.
testISO_TCP General test program.
testISO_TCPload Store program blocks into PLC, ISO over TCP protocol
testMPI General test program.
testMPI2 Tests multiple MPI connections to a single PLC.
testMPIload Store program blocks into PLC, MPI protocol
testPPI General test program.
testPPI_IBH General test program for PPI over IBH/MHJ-NetLink.
testPPIload Store program blocks into PLC, PPI protocol
testPPI_IBHload Store program blocks into PLC, PPI over IBH/MHJ-NetLink.
win DLL, .lib file and precompiled test programs for Win32
winmake.bat Batch file to compile under Win32
MAKEFILE.VC.WINE Makefile to build libnodave.dll and test programs with MSVC++ under wine
MAKEFILE.WINE Makefile to build libnodave.dll and test programs with BorlandC under wine

89
ibhsamples6.c Normal file
View File

@@ -0,0 +1,89 @@
us cha0[]={
0x00,0xff,0x03,0x00,0x00,0x00,0x04,0x00, 0x03,0x07,0x02,
};
uc res0[]={
0xff,0x00,0xfe,0x00,0x04,0x00,0x00,0x00, 0x00,0x07,0x02,0x03,0x1f,0x00,0x02,0x00,
0x03,0x00,0x00,0x00,0x02,0x00,0x02,0x3e, 0x9f,0x01,0x01,0x00,0x01,0x00,0x3c,0x00,
0x90,0x01,0x10,0x27,0x00,0x00,0x14,0x01, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x48, 0x69,0x6c,0x73,0x63,0x68,0x65,0x72,0x20,
0x47,0x6d,0x62,0x48,0x20,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x01,0xff,
};
us cha1[]={ /* used in C */
0x07,0xff,0x08,0x01,0x00,0x00,0x96,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x0a,0x01,
};
#define isReachable 0x30
#define isNotReachable 0xff
uc res1[]={ /* used in C */
0xff,0x07,0x87,0x01,0x96,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x7f,0x0a,0x02,
0x30,0xff,0x30,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,
};
us cha2[]={/* used in C */
0x00,0x10,0x01,0x00,0x00,0x00,0x01,0x00, 0x0f,
};
uc res2[]={
0x10,0x00,
0x20,
0x00,
0x01,0x00,0x00,0x00,
0x01,0x06,0x20,0x03,0x17,0x00,0x43,0x00,
0x00,0x00,0x22,0x21,0x00,0x00,0x00,0x00, 0x49,0x42,0x48,0x53,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
us cha3[]={/* used in C */
0x07,0xff,0x06,0x01,0x00,0x00,0x97,0x00, 0x114,0x1ff,0xf0,0xf0,0xf0,0xf0,
};
uc res3[]={
0xff,0x07,0x02,0x01,0x97,0x00,0x00,0x00, 0x14,0x00,
};
us cha8[]={ /* used in C */
0x07,0xff,0x07,0x03,0x00,0x00,0x82,0x00, 0x114,0x114,0x103,0x00,0x02,0x05,0x01,
};
uc res7[]={
0xff,0x07,0x09,0x00,0x00,0x00,0xc2,0x02, 0x14,0x14,0x03,0x00,0x00,0x22,0x02,0x05,
0x01,
};
us cha11[]={ /* used in C */
0x07,0xff,0x19,0x04,0x00,0x00,0x82,0x00, 0x114,0x114,0x103,0x00,0x14,0xf1,0x00,0x32,
0x01,0x00,0x00,0x101,0x00,0x00,0x08,0x00, 0x00,0xf0,0x00,0x00,0x101,0x00,0x102,0x00,
0xf0,
};
uc res10[]={
0xff,0x07,0x1d,0x00,0x00,0x00,0xc2,0x02, 0x14,0x14,0x03,0x00,0x00,0x22,0x16,0xf1,
0x00,0x32,0x03,0x00,0x00,0x02,0x00,0x00, 0x08,0x00,0x00,0x00,0x00,0xf0,0x00,0x00,
0x01,0x00,0x01,0x00,0xf0,
};

928
ibhsim5.c Normal file
View File

@@ -0,0 +1,928 @@
/*
Part of Libnodave, a free communication libray for Siemens S7 300/400.
This program simulates the IBHLink MPI-Ethernet-Adapter from IBH-Softec.
www.ibh-softec.de
(C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2002.
Libnodave is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with Visual; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include "log2.h"
#define ThisModule "IBHtest : "
#define uc unsigned char
#include "nodave.h"
#include "ibhsamples6.c"
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include "accepter.c"
#include "simProperties.c"
#define bSize 1256
#define us unsigned short
#define debug 12
extern int daveDebug;
#define daveDebugAnalyze 0x40000
void analyze(daveConnection * dc);
/*
many bytes. hopefully enough to serve any read request.
*/
uc dummyRes[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,
12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,12,13,14,1,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,
12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,12,13,14,1,
5,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,5,16,1,2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,12,13,14,1,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,
12,13,14,15,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,12,13,14,1,
5,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,5,16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,};
/*
a read callback function
*/
uc * dummyRead (int area, int DBnumber, int start, int len, int * res) {
printf("User callback should deliver pointer to %d bytes from %s %d beginning at %d.\n",
len, daveAreaName(area),DBnumber,start);
*res=0;
return dummyRes;
};
void myWrite (int area, int DBnumber, int start, int len, int * res, uc*bytes) {
printf("User callback1 should write %d bytes to %s %d beginning at %d.\n",
len, daveAreaName(area),DBnumber,start);
printf("User callback 1.\n");
*res=0;
start=start/8;
memcpy(dummyRes+start,bytes,len);
printf("User callback done.\n");
fflush(stdout);
};
int handleTime(PDU *p1, PDU *p2){
if (p1->param[6]==1) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x87,0x01,0x01, 0x00,0x00,0,0,};
printf("Get time from CPU.\n");
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddUserData(p2, CpuTimeStamp, sizeof(CpuTimeStamp));
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else if (p1->param[6]==2) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x87,0x02,0x01, 0x00,0x00,0,0,};
printf("Set CPU time.\n");
memcpy(CpuTimeStamp, p1->data+4,sizeof(CpuTimeStamp));
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddUserData(p2, CpuTimeStamp, sizeof(CpuTimeStamp));
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else {
printf("Cannot handle this\n");
return 0;
}
}
int handleBlocks(PDU *p1, PDU *p2){
printf("Block handling Commands(%d) ???? ????\n",p1->param[6]);
if (p1->param[6]==1) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x83,0x01,0x00, 0x00,0x00,0x00,0x00,};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddUserData(p2, blockList, sizeof(blockList));
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else if (p1->param[6]==2) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x83,0x02,0x00, 0x00,0x00,0x00,0x00,};
uc da[]={0x0a,0x0,0x0,0x0,};
uc SDBs[]={0x00,0x01,0x22,0x11,0x00,0x02,0x22,0x11};
printf("List Blocks of type %s\n",daveBlockName(p1->data[5]));
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
if(p1->data[5]==0x42) {
_daveAddUserData(p2, SDBs, sizeof(SDBs));
} else {
_daveAddData(p2, da, sizeof(da));
}
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else if (p1->param[6]==3) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x83,0x03,0x00, 0x00,0x00,0xd2,0x09,};
uc da[]={0x0a,0x0,0x0,0x0,};
printf("get Block info/header %s\n",daveBlockName(p1->data[5]));
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddData(p2, da, sizeof(da));
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else {
printf("Cannot handle this\n");
return 0;
}
}
int handleReadProgram(PDU *p1, PDU *p2){
uc pa[]={
0x1D,0x00,0x01,0x00,0x00,0x00,0x00,0x07,0x07,0x30,0x30,0x30,0x30,0x30,0x37,0x32,
};
printf("Program block readout.\n");
_daveInitPDUheader(p2,3);
_daveAddParam(p2, pa, sizeof(pa));
// if(daveDebug & daveDebugPDU)
_daveDumpPDU(p2);
return 1;
}
typedef struct __ls {
uc * content;
int gotlen;
int totlen;
int number;
int PDUnumber;
int typ;
} loadStruct;
//#define ploaderror
int handleLoadProgram(daveConnection *dc,PDU *p1, PDU *p2,int PDUnumber, uc*resp, loadStruct * ls){
int n;
uc pa[]={
0x1A
};
uc pa2[]={
0x1B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x5F,0x30,0x38,0x30,0x30,0x30,0x30,0x31,0x50,
};
#ifndef ploaderror
printf("Program block load.\n");
_daveInitPDUheader(p2,3);
_daveAddParam(p2, pa, sizeof(pa));
ls->typ=p1->param[11];
printf("block type %s. ",daveBlockName(ls->typ));
ls->number=atol((char*)p1->param+12);
ls->PDUnumber=1;
printf("number: %d\n",ls->number);
p1->param[26]=0;
ls->totlen=atol((char*)p1->param+20);
ls->gotlen=0;
printf("total size: %d\n",ls->totlen);
ls->content=(uc*)malloc(ls->totlen);
resp[22]=PDUnumber % 256; // test!
resp[21]=PDUnumber / 256; // test!
_daveDumpPDU(p2);
write(dc->iface->fd.rfd,resp,resp[2]+8);
_daveInitPDUheader(p2,1);
pa2[11]=ls->typ;
n=ls->number;
pa2[16]=0x30+(n %10);
n/=10;
pa2[15]=0x30+(n %10);
n/=10;
pa2[14]=0x30+(n %10);
n/=10;
pa2[13]=0x30+(n %10);
_daveAddParam(p2, pa2, sizeof(pa2));
#else
_daveInitPDUheader(p2,2);
p2->header[10]=0xD2;
p2->header[11]=0x09;
#endif
// _daveAddParam(p2, pa, sizeof(pa));
// if(daveDebug & daveDebugPDU)
/*
resp[22]=PDUnumber % 256; // test!
resp[21]=PDUnumber / 256; // test!
*/
_daveDumpPDU(p2);
write(dc->iface->fd.rfd,resp,resp[2]+8);
/*
dc->AnswLen=_daveReadIBHPacket(dc->iface, dc->msgIn);
analyze(dc);
// _daveDump("I sent:",resp,resp[2]+8);
LOG1(ThisModule);
_daveDump("epacket 1", dc->msgIn, dc->AnswLen);
dc->AnswLen=_daveReadIBHPacket(dc->iface, dc->msgIn);
analyze(dc);
// _daveDump("I sent:",resp,resp[2]+8);
LOG1(ThisModule);
_daveDump("epacket 2", dc->msgIn, dc->AnswLen);
dc->AnswLen=_daveReadIBHPacket(dc->iface, dc->msgIn);
analyze(dc);
// _daveDump("I sent:",resp,resp[2]+8);
LOG1(ThisModule);
_daveDump("epacket 3", dc->msgIn, dc->AnswLen);
dc->AnswLen=_daveReadIBHPacket(dc->iface, dc->msgIn);
analyze(dc);
// _daveDump("I sent:",resp,resp[2]+8);
LOG1(ThisModule);
_daveDump("epacket 4", dc->msgIn, dc->AnswLen);
*/
return 0;
}
int handleContLoadProgram(daveConnection *dc,PDU *p1, PDU *p2,int PDUnumber, uc*resp, loadStruct * ls){
int n;
uc pa2[]={
0x1B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x5F,0x30,0x38,0x30,0x30,0x30,0x30,0x34,0x50,
};
uc pa3[]={
// 0x1C,0x00,0x84,0x04,0x00,0x00,0x00,0x00,0x09,0x5F,0x30,0x38,0x30,0x30,0x30,0x30,0x34,0x50,
0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x5F,0x30,0x38,0x30,0x30,0x30,0x30,0x34,0x50,
};
printf("total size: %d have %d\n",ls->totlen,ls->gotlen);
ls->gotlen +=p1->dlen-4;
printf("total size: %d have %d\n",ls->totlen,ls->gotlen);
n=ls->number;
pa2[16]=0x30+(n %10);
n/=10;
pa2[15]=0x30+(n %10);
n/=10;
pa2[14]=0x30+(n %10);
n/=10;
pa2[13]=0x30+(n %10);
pa2[11]=ls->typ;
n=ls->number;
pa3[16]=0x30+(n %10);
n/=10;
pa3[15]=0x30+(n %10);
n/=10;
pa3[14]=0x30+(n %10);
n/=10;
pa3[13]=0x30+(n %10);
pa3[11]=ls->typ;
if (p1->param[1]==1) {
_daveAddParam(p2, pa2, sizeof(pa2));
return 1;
}
else {
_daveAddParam(p2, pa3, sizeof(pa3));
return 1;
}
}
int handleEndLoadProgram(daveConnection *dc,PDU *p1, PDU *p2,int PDUnumber, uc*resp, loadStruct * ls){
uc pa[]={
0x1C
};
printf("Program block readout.\n");
_daveInitPDUheader(p2,3);
_daveAddParam(p2, pa, sizeof(pa));
// if(daveDebug & daveDebugPDU)
_daveDumpPDU(p2);
return 1;
}
int handleContinueReadProgram(PDU *p1, PDU *p2){
uc pa[]={
0x1E,0x00
};
uc da[]={
0x00,0x48,0x00,0xFB, 0x70,0x70,0x00,0x0A, 0x11,0x0B,0x00,0x01, 0x00,0x00,0x00,0x48,
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x0E,0x45,0x00,0x00, 0x00,0x00,0x0E,0x45,
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x24, 0x00,0x00,0x00,0x01, 0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,
0x00,0x01,0x7F,0xFF, 0x00,0x00,0x22,0x00, 0x00,0x01,0x00,0xC0,
};
printf("Program block readout.\n");
_daveInitPDUheader(p2,3);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddData(p2, da, sizeof(da));
// if(daveDebug & daveDebugPDU)
_daveDumpPDU(p2);
return 1;
}
int handleEndReadProgram(PDU *p1, PDU *p2){
uc pa[]={
0x1F
};
printf("Program block readout.\n");
_daveInitPDUheader(p2,3);
_daveAddParam(p2, pa, sizeof(pa));
// if(daveDebug & daveDebugPDU)
_daveDumpPDU(p2);
return 1;
}
int handleSZL(int number, int index, PDU *p2){
if ((number==292) && (index==0)) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddUserData(p2, SZL_292_0, sizeof(SZL_292_0));
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else if ((number==274) && (index==512)) { /* read order code (MLFB) */
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddUserData(p2, SZL_274_512, sizeof(SZL_274_512));
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else if ((number==0) && (index==0)) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddUserData(p2, SZL_0_0, sizeof(SZL_0_0));
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else if ((number==25) && (index==0)) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddUserData(p2, SZL_25_0, sizeof(SZL_25_0));
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else if ((number==273) && (index==1)) { /* read order code (MLFB) */
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddUserData(p2, SZL_273_1, sizeof(SZL_273_1));
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else if (number==305) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
switch (index) {
case 1: _daveAddUserData(p2, SZL_305_1, sizeof(SZL_305_1)); break;
case 2: _daveAddUserData(p2, SZL_305_2, sizeof(SZL_305_1)); break;
case 3: _daveAddUserData(p2, SZL_305_3, sizeof(SZL_305_1)); break;
case 4: _daveAddUserData(p2, SZL_305_4, sizeof(SZL_305_1)); break;
case 5: _daveAddUserData(p2, SZL_305_5, sizeof(SZL_305_1)); break;
case 6: _daveAddUserData(p2, SZL_305_6, sizeof(SZL_305_1)); break;
case 7: _daveAddUserData(p2, SZL_305_7, sizeof(SZL_305_1)); break;
case 8: _daveAddUserData(p2, SZL_305_8, sizeof(SZL_305_1)); break;
case 9: _daveAddUserData(p2, SZL_305_9, sizeof(SZL_305_1)); break;
}
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else if (number==306) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
switch (index) {
case 1: _daveAddUserData(p2, SZL_306_1, sizeof(SZL_306_1)); break;
case 2: _daveAddUserData(p2, SZL_306_2, sizeof(SZL_306_2)); break;
case 4: _daveAddUserData(p2, SZL_306_4, sizeof(SZL_306_4)); break;
}
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else if ((number==0xD91) && (index==0)) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddUserData(p2, SZL_3473_0, sizeof(SZL_3473_0));
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else if ((number==1060) && (index==0)) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
if (runStop == runModeRun)
_daveAddUserData(p2, SZL_1060_0, sizeof(SZL_1060_0));
else if (runStop == runModeStop)
_daveAddUserData(p2, SZL_1060_0S, sizeof(SZL_1060_0S));
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else if ((number==1316) && (index==20480)) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddUserData(p2, SZL_1316_20480, sizeof(SZL_1316_20480));
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
} else {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0xD4,0x01,};
uc da[]={0x0a,0x0,0x0,0x0,};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddData(p2, da, sizeof(da));
if(daveDebug & daveDebugPDU)_daveDumpPDU(p2);
return 1;
}
}
int handleSystemMessage(PDU * p1,PDU * p2) {
int number;
int index;
if (
(p1->param[1]==1) &&
(p1->param[2]==18) &&
(p1->param[3]==4) &&
(p1->param[4]==17) &&
(p1->param[5]=='D')
) {
number=0x100*(p1->data[4])+p1->data[5];
index=0x100*(p1->data[6])+p1->data[7];
printf("SZL read ID: %04X index: %d\n",number,index);
return handleSZL(number,index,p2);
} else if (
(p1->param[1]==1) &&
(p1->param[2]==18) &&
(p1->param[3]==4) &&
(p1->param[4]==17) &&
(p1->param[5]=='C')
) {
printf("Block functions\n");
return handleBlocks(p1,p2);
}
else if (
(p1->param[1]==1) &&
(p1->param[2]==18) &&
(p1->param[3]==4) &&
(p1->param[4]==17) &&
(p1->param[5]=='G')
) {
printf("Time System ????\n");
return handleTime(p1,p2);
} else if (
(p1->param[1]==1) &&
(p1->param[2]==18) &&
(p1->param[3]==8) &&
(p1->param[4]==18) &&
(p1->param[5]=='A')
) {
if(p1->param[6]==16) {
printf("Forces ???? ????\n");
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x81,0x10,0x00, 0x00,0x00,0xD0,0x02,};
uc da[]={0,4,0,4,1,0,0,1,16,1,0,0};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddData(p2, da, sizeof(da));
_daveDumpPDU(p2);
return 1;
} else if(p1->param[6]==12) {
printf("Erase ???? ????\n");
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x81,0x12,0x00, 0x00,0x00,0x0,0x0,};
uc da[]={0x0a,0x0,0x0,0x0,};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddData(p2, da, sizeof(da));
_daveDumpPDU(p2);
return 1;
} else {
printf("Programmer Commands(%d) ???? ????\n",p1->param[6]);
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x81,0x10,0x00, 0x00,0x00,0xD0,0x04,};
uc da[]={0x0a,0x0,0x0,0x0,};
pa[6]=p1->param[6];
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddData(p2, da, sizeof(da));
_daveDumpPDU(p2);
return 1;
}
}
printf("Cannot handle this!\n");
return 0;
};
int handleNegociate(PDU * p1,PDU * p2) {
uc pa[]={0xF0,0x0,0x0,0x1, 0x00,0x01,0x00,0xF0};
davePut16At(pa, 6, simMaxPDUlength);
_daveInitPDUheader(p2,3);
_daveAddParam(p2, pa, sizeof(pa));
return 1;
};
int handleStop(PDU * p1,PDU * p2) {
uc pa[]={0x29};
runStop=runModeStop;
_daveInitPDUheader(p2,3);
_daveAddParam(p2, pa, sizeof(pa));
return 1;
};
int handleRun(PDU * p1,PDU * p2, loadStruct * ls) {
uc pa[]={0x28};
runStop=runModeRun;
_daveInitPDUheader(p2,3);
_daveAddParam(p2, pa, sizeof(pa));
return 1;
};
int gpacketNumber=0;
uc r5[]={
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,
};
void _daveSendIBHNetAck2(daveConnection * dc) {
// 0xff,0x07,0x05,0x02,0x82,0x00,0x00,0x00, 0x14,0x00,0x03,0x01,0x09,
IBHpacket * p;
uc ack[13],c;
us d;
memcpy(ack, dc->msgIn, sizeof(ack));
p= (IBHpacket*) ack;
d=p->sFlags; p->sFlags=p->rFlags; p->rFlags=d;
c=p->ch1; p->ch1=p->ch2; p->ch2=c; // certainly nonsense, but I cannot test it
c=ack[9]; // at the moment, and because it DID work,
ack[9]=ack[10];
ack[10]=c; // I'll leave it as it is.
p->len=sizeof(ack)-sizeof(IBHpacket);
ack[11]=1;
ack[12]=9;
// LOG2("Sending net level ack for number: %d\n",p->packetNumber);
if (daveDebug & daveDebugMPI){
_daveDump("I send ack", ack,sizeof(ack));
}
_daveWriteIBH(dc->iface, ack,sizeof(ack));
}
loadStruct lost;
void analyze(daveConnection * dc) {
IBHpacket * p2;
MPIheader2 * m2;
uc resp[2000];
int PDUnumber;
int haveResp=0;
PDU p1,pr;
IBHpacket * p= (IBHpacket*) dc->msgIn;
dc->needAckNumber=-1; // Assume no ack
/*
printf("Channel: %d\n",p->ch1);
printf("Channel: %d\n",p->ch2);
printf("Length: %d\n",p->len);
printf("Number: %d\n",p->packetNumber);
printf("sFlags: %04x rFlags:%04x\n",p->sFlags,p->rFlags);
*/
if (p->rFlags==0x82) {
MPIheader * pm= (MPIheader*) (dc->msgIn+sizeof(IBHpacket));
if (daveDebug & daveDebugAnalyze){
printf("srcconn: %d\n",pm->src_conn);
printf("dstconn: %d\n",pm->dst_conn);
printf("MPI: %d\n",pm->MPI);
printf("MPI len: %d\n",pm->len);
printf("MPI func:%d\n",pm->func);
}
if (pm->func==0xf1) {
if (daveDebug & daveDebugAnalyze){
printf("0xf1:PDU transport, MPI packet number: %d\n",pm->packetNumber);
}
dc->needAckNumber=pm->packetNumber;
dc->PDUstartI=sizeof(IBHpacket)+sizeof(MPIheader);
_daveSetupReceivedPDU(dc, &p1);
PDUnumber=p1.header[5]+256*p1.header[4];
_daveDumpPDU(&p1);
// construct response:
pr.header=resp+sizeof(IBHpacket)+sizeof(MPIheader2);
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;
m2->packetNumber=gpacketNumber;
gpacketNumber++;
if (p1.param[0]==daveFuncRead) {
_daveHandleRead(&p1,&pr);
haveResp=1;
m2->func=0xf1; //!! guessed
m2->len=pr.hlen+pr.plen+pr.dlen+2;
p2->len=m2->len+7;
} else if (p1.param[0]==daveFuncWrite) {
printf("before _daveHandleWrite() %p\n",m2);
_daveHandleWrite(&p1,&pr);
printf("after _daveHandleWrite() %p\n",m2);
fflush(stdout);
haveResp=1;
// m2->func=0xf1; //!! guessed
printf("after _daveHandleWrite()\n");
fflush(stdout);
m2->len=pr.hlen+pr.plen+pr.dlen+2;
printf("after _daveHandleWrite()\n");
fflush(stdout);
p2->len=m2->len+7;
printf("after _daveHandleWrite()\n");
fflush(stdout);
} else if (p1.param[0]==240) {
printf("PDU function code: %d, negociate PDU len\n",p1.param[0]);
// _daveDump("packet:",dc->msgIn,dc->msgIn[2]+8);
handleNegociate(&p1,&pr);
dc->packetNumber=0;
haveResp=1;
m2->func=0xf1; //!! guessed
m2->len=pr.hlen+pr.plen+pr.dlen+2;
p2->len=m2->len+7;
} else if (p1.param[0]==0x28) {
printf("PDU function code: %d, run CPU\n",p1.param[0]);
// _daveDump("packet:",dc->msgIn,dc->msgIn[2]+8);
handleRun(&p1,&pr,&lost);
// dc->packetNumber=0;
haveResp=1;
m2->func=0xf1; //!! guessed
m2->len=pr.hlen+pr.plen+pr.dlen+2;
p2->len=m2->len+7;
} else if (p1.param[0]==0x29) {
printf("PDU function code: %d, stop CPU\n",p1.param[0]);
// _daveDump("packet:",dc->msgIn,dc->msgIn[2]+8);
handleStop(&p1,&pr);
dc->packetNumber=0;
haveResp=1;
m2->func=0xf1; //!! guessed
m2->len=pr.hlen+pr.plen+pr.dlen+2;
p2->len=m2->len+7;
} else if (p1.param[0]==0x1d) {
handleReadProgram(&p1,&pr);
haveResp=1;
m2->func=0xf1; //!! guessed
m2->len=pr.hlen+pr.plen+pr.dlen+2;
p2->len=m2->len+7;
} else if (p1.param[0]==0x1e) {
handleContinueReadProgram(&p1,&pr);
haveResp=1;
m2->func=0xf1; //!! guessed
m2->len=pr.hlen+pr.plen+pr.dlen+2;
p2->len=m2->len+7;
} else if (p1.param[0]==0x1f) {
handleEndReadProgram(&p1,&pr);
haveResp=1;
m2->func=0xf1; //!! guessed
m2->len=pr.hlen+pr.plen+pr.dlen+2;
p2->len=m2->len+7;
} else if (p1.param[0]==0x1a) {
handleLoadProgram(dc, &p1,&pr,PDUnumber,resp, &lost);
haveResp=1;
m2->func=0xf1; //!! guessed
m2->len=pr.hlen+pr.plen+pr.dlen+2;
p2->len=m2->len+7;
// PDUnumber=lost.PDUnumber;
} else if (p1.param[0]==0x1b) {
haveResp=handleContLoadProgram(dc, &p1,&pr,PDUnumber,resp, &lost);
m2->func=0xf1; //!! guessed
m2->len=pr.hlen+pr.plen+pr.dlen+2;
p2->len=m2->len+7;
} else if (p1.param[0]==0x1c) {
handleEndLoadProgram(dc, &p1,&pr,PDUnumber,resp, &lost);
haveResp=0;
} else if (p1.param[0]==0) {
printf("PDU function code: %d, system Messaga ?\n",p1.param[0]);
_daveSendMPIAck2(dc);
// _daveDump("packet:",b,b[2]+8);
haveResp=handleSystemMessage(&p1,&pr);
// pr.header[4]=p1.header[4]; // give the PDU a number
// pr.header[5]=p1.header[5]; // give the PDU a number
m2->func=0xf1; //!! guessed
m2->len=pr.hlen+pr.plen+pr.dlen+2;
p2->len=m2->len+7;
} else {
printf("Unsupported PDU function code: %d\n",p1.param[0]);
}
}
if (pm->func==0xb0) {
// printf("Ackknowledge for packet number: %d\n",*(dc->msgIn+15));
}
if (pm->func==0xe0) {
printf("Connect to MPI: %d\n",pm->MPI);
memcpy(resp, r5, sizeof(r5));
resp[8]=pm->src_conn;
resp[9]=pm->src_conn;
resp[10]=pm->MPI;
resp[11]=7; //????
haveResp=1;
}
}
if (((p->rFlags==0x82) /*||(p->sFlags==0x82)*/)&&(p->packetNumber)&&(p->len)) {
// printf("before _daveSendIBHNetAck()\n");
fflush(stdout);
_daveSendIBHNetAck2(dc);
}
if (haveResp) {
// printf("have response\n");
resp[22]=PDUnumber % 256; // test!
resp[21]=PDUnumber / 256; // test!
write(dc->iface->fd.rfd,resp,resp[2]+8);
_daveDump("I sent:",resp,resp[2]+8);
fflush(stdout);
}
};
typedef struct _portInfo {
int fd;
}portInfo;
#define mymemcmp _daveMemcmp
void *portServer(void *arg)
{
int waitCount, res, pcount, r2;
_daveOSserialType s;
daveInterface * di;
daveConnection * dc;
portInfo * pi=(portInfo *) arg;
LOG2(ThisModule "portMy fd is:%d\n",
pi->fd);
FLUSH;
waitCount= 0;
// daveDebug=daveDebugAll;
pcount=0;
s.rfd=pi->fd;
s.wfd=pi->fd;
di=daveNewInterface(s,"IF",0,daveProtoMPI_IBH,daveSpeed187k);
di->timeout=900000;
dc=daveNewConnection(di,0,0,0);
di->timeout=1900000;
while (waitCount < 1000) {
dc->AnswLen=_daveReadIBHPacket(dc->iface, dc->msgIn);
if (dc->AnswLen>0) {
res=dc->AnswLen;
if (daveDebug & daveDebugPacket) {
LOG2(ThisModule "%d ", pcount);
_daveDump("packet", dc->msgIn, dc->AnswLen);
}
waitCount = 0;
analyze(dc);
r2=2*res;
if(r2==sizeof(cha0)) {
if (0==mymemcmp(cha0, dc->msgIn, res)) {
LOG1(ThisModule "found challenge 0, write response 0\n");
write(pi->fd, res0, sizeof(res0));
}
}
if(r2==sizeof(cha1)) {
if (0==mymemcmp(cha1, dc->msgIn, res)) {
LOG1(ThisModule "found challenge 1, write response 1\n");
write(pi->fd, res1, sizeof(res1));
}
}
if(r2==sizeof(cha2)) {
if (0==mymemcmp(cha2, dc->msgIn, res)) {
LOG1(ThisModule "found challenge 2, write response 2\n");
write(pi->fd, res2, sizeof(res2));
}
}
if(r2==sizeof(cha3)) {
if (0==mymemcmp(cha3, dc->msgIn, res)) {
LOG1(ThisModule "found challenge 3, write response 3\n");
res3[8]=dc->msgIn[8];
write(pi->fd, res3, sizeof(res3));
}
}
if(r2==sizeof(cha8)) {
if (0==mymemcmp(cha8, dc->msgIn, res)) {
LOG1(ThisModule "found challenge 8, write response 7\n");
res7[8]=dc->msgIn[8];
res7[9]=dc->msgIn[9];
res7[10]=dc->msgIn[10];
write(pi->fd, res7, sizeof(res7));
}
}
if(r2==sizeof(cha11)) {
if (0==mymemcmp(cha11, dc->msgIn, res)) {
LOG1(ThisModule "found challenge 11, response 10\n");
res10[8]=dc->msgIn[8];
res10[9]=dc->msgIn[9];
// res10[10]=dc->msgIn[10];
// res10[32]=dc->msgIn[28];
// res10[34]=dc->msgIn[30];
// sendMPIAck2(dc->msgIn, pi->fd, ackPacketNumber);
//// dc->needAckNumber=0;
//// _daveSendMPIAck2(dc);
//// write(pi->fd, res10, sizeof(res10));
}
}
pcount++;
} else {
waitCount++;
}
}
LOG1(ThisModule "portserver: I closed my fd.\n");
FLUSH;
return NULL;
}
/*
This waits in select for a file descriptor from accepter and starts a new child server
with this file descriptor.
*/
int PID;
int main(int argc, char **argv)
{
portInfo pi;
fd_set FDS;
int filedes[2], res, newfd;
pthread_attr_t attr;
pthread_t ac, ps;
accepter_info ai;
PID=getpid();
if (argc<2) {
printf("Usage: ibhtest port\n");
printf("Example: ibhtest 1099 (used by IBHNetLink)\n");
return -1;
}
readCallBack=dummyRead;
writeCallBack=myWrite;
pipe(filedes);
ai.port = atol(argv[1]);
LOG2(ThisModule "Main serv: %d\n", ai.port);
LOG2(ThisModule "Main serv: Accepter pipe fd: %d\n", ai.fd);
ai.fd = filedes[1];
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
res=pthread_create(&ac, &attr, accepter, &ai /*&filedes[1] */ );
do {
FD_ZERO(&FDS);
FD_SET(filedes[0], &FDS);
LOG2(ThisModule "Main serv: about to select on %d\n",
filedes[0]);
FLUSH;
if (select(filedes[0] + 1, &FDS, NULL, &FDS, NULL) > 0) {
LOG1(ThisModule "Main serv: about to read\n");
res = read(filedes[0], &pi.fd, sizeof(pi.fd));
ps=0;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
res=pthread_create(&ps, &attr, portServer, &pi);
if(res) {
LOG2(ThisModule
"Main serv: create error:%s\n", strerror(res));
close(newfd);
usleep(100000);
}
}
}
while (1);
return 0;
}
/*
Changes:
14/07/2003 give a hint about usage
02/01/2005 fixed argc, it's 2 if there is 1 argument.
03/06/2005 removed byteswap.h, it is not needed.
*/

435
isotest4.c Normal file
View File

@@ -0,0 +1,435 @@
/*
Part of Libnodave, a free communication libray for Siemens S7 300/400.
This program simulates a CPx43.
(C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2002.
Libnodave is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License
along with Visual; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include "log2.h"
#define uc unsigned char
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#define ThisModule __FILE__
#include "accepter.c"
//#include <byteswap.h>
#include "nodave.h"
#define bSize 1256
#define us unsigned short
#define debug 10
/*
many (1024) bytes. hopefully enough to serve any read request.
*/
uc dummyRes[1024];
/*
a read callback function
*/
uc * dummyRead (int area, int DBnumber, int start, int len, int * res) {
dummyRes[3]+=5;
dummyRes[0]+=5;
#if debug>1
printf("User callback should deliver pointer to %d bytes from %s %d beginning at %d.\n",
len, daveAreaName(area),DBnumber,start);
#endif
*res=0;
return dummyRes;
};
void dummyWrite(int area, int DBnumber, int start, int len, int * res, uc*buffer) {
// dummyRes[2]++;
start/=8;
#if debug>1
printf("User write callback should write %d bytes from %s %d to address %d.\n",
len, daveAreaName(area),DBnumber,start);
#endif
*res=0;
memcpy(dummyRes+start,buffer,len);
};
int handleSystemMessage(PDU * p1,PDU * p2) {
int number=0x100*(p1->data[4])+p1->data[5];
int count=0x100*(p1->data[6])+p1->data[7];
printf("SysMessage number: %d\n",number);
printf("count: %d\n",count);
if (number==20) {
/*
Info about CPU resources. I return some strange values in order
to watch which number goes where.
*/
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x03, 0x00,0x00,0x00,0x00,};
uc va[]={
// FF,09,00,50,
00,0x14,00,00,
00,0x08,00,07,
0,1,0,1,0,0x81,00,00, // 129 input bytes
0,2,0,1,0,0x7f,00,00, // 127 output bytes
0,3,0,1,0x0a,00,00,0x80, // 10*32 flag bytes
00,04,00,01,00,0x80,00,00, // 128 Timer
00,05,00,01,00,0x40,00,0x08, // 64 counter
00,06,00,01,05,01,00,00, // 5*256+1=1281 log.Address
00,07,00,01,06,03,00,00, // 6*256+3=1539 local data
00,0x08,00,01,01,00,00,0x10,
00,0x09,00,01,00,01,00,00,
};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
// _daveAddData(p2, p1->data, 8);
// p1->data[3]=4; // 4 bytes have been already copied to result string
// _daveAddValue(p2, va, sizeof(va));
_daveAddUserData(p2, va, sizeof(va));
_daveDumpPDU(p2);
return 1;
} else if (number==292) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x03, 0x00,0x00,0x00,0x00,};
uc va[]={
0x00,0x14,0x00,0x00,0x43,0x02,0xff,0x68, 0xc7,0x00,0x00,0x00,
0x08,0x10,0x77,0x10,0x03,0x07,0x10,0x13, 0x22,0x26,0x58,0x85,
};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddData(p2, p1->data, 8);
p1->data[3]=4; // 4 bytes have been copied to result string
_daveAddValue(p2, va, sizeof(va));
_daveDumpPDU(p2);
return 1;
} else if (number==306) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
uc va306[]={
0,40,0,1,0,4,0, 1, 0,0,0,
1, 0,2,0,0,0,0,86,86,0,0,
0, 0,0,0,0,0,0, 0, 0,0,0,
0, 0,0,0,0,0,0, 0, 0,0,0,
};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddData(p2, p1->data, 8);
p1->data[3]=4; // 4 bytes have been copied to result string
_daveAddValue(p2, va306, sizeof(va306));
_daveDumpPDU(p2);
return 1;
} else if (number==273) { /* read order code (MLFB) */
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x03, 0x00,0x00,0x00,0x00,};
uc va273[]={
0x00,0x1c,0x00,0x01,0x43,0x02,
'6','E','S',
// '7',' ','3','1','5','-','2','A','F','0','3','-','0','A','B','0',
'7',' ','L','I','B','N','O','D','A','V','E','-','T','.','H','.',
' ',0x00,0xc0,0x00,0x02,0x00,0x00,
};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddData(p2, p1->data, 8);
p1->data[3]=4; // 4 bytes have already been copied to result string
_daveAddValue(p2, va273, sizeof(va273));
_daveDumpPDU(p2);
return 1;
} else if (number==305) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
uc va306[]={
// 0,40,0,1,0,4,0, 1, 0,0,0,
// 1, 0,2,0,0,0,0,86,86,0,0,
// 0, 0,0,0,0,0,0, 0, 0,0,0,
// 0, 0,0,0,0,0,0, 0, 0,0,0,
0, 40, 0, 1, 0, 2,190,253, 15, 0,
0, 0, 0, 0, 0, 0, 0, 0, 60, 1,
0, 0, 0, 0,125, 0, 0, 5, 3, 4,
0, 0, 0, 0, 0, 12, 0, 10, 0, 0,
0, 9, 0, 0,
};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddData(p2, p1->data, 8);
p1->data[3]=4; // 4 bytes have been copied to result string
_daveAddValue(p2, va306, sizeof(va306));
_daveDumpPDU(p2);
return 1;
} else if (number==0) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
uc va[]={
// 255,9,0,114,0,0,0,0,
0,2,0,53,0,0,15,0,0,17,1,17,15,17,0,18,
1,18,15,18,0,19,0,20,0,21,1,21,0,23,1,23,15,
23,0,24,1,24,15,24,0,25,15,25,15,26,15,27,0,26,0,
27,0,33,10,33,15,33,2,34,0,35,15,35,0,36,1,36,
4,36,5,36,1,49,1,50,2,50,0,116,1,116,15,116,12,
145,13,145,10,145,0,146,2,146,6,146,15,146,0,177,0,178,0,
179,0,180,0,160,1,160,12,139,126,
};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddData(p2, p1->data, 8);
p1->data[3]=4; // 4 bytes have been copied to result string
_daveAddValue(p2, va, sizeof(va));
_daveDumpPDU(p2);
return 1;
} else if (number==1060) {
uc pa[]={0x00,0x01,0x12,0x08,0x12,0x84,0x01,0x01, 0x00,0x00,0x00,0x00,};
uc va[]={
// 255,9,0,114,0,0,0,0,
0,20,0,1,81,68,255,8,0,0,0,0,0,0,0,0,148,1,2,22,83,88,104,17,
};
_daveInitPDUheader(p2,7);
_daveAddParam(p2, pa, sizeof(pa));
_daveAddData(p2, p1->data, 8);
p1->data[3]=4; // 4 bytes have been copied to result string
_daveAddValue(p2, va, sizeof(va));
_daveDumpPDU(p2);
return 1;
} else
return 0;
};
int gpacketNumber=0;
typedef struct{
uc prot;
uc ch1;
uc ch2;
uc len;
uc xxxx1;
uc func;
uc xxxx2;
} ISOpacket;
void analyze(daveConnection * dc) {
ISOpacket * p,* p2;
uc resp[2000];
uc r5[]={
0x03,0x00,0x00,0x16,
0x11,0xd0,
0,0,0,1,0,
0xc0,1,9,
0x0c1,2,1,2,
0x0c2,2,1,0,
};
int haveResp=0;
PDU p1,pr;
p= (ISOpacket*) dc->msgIn;
dc->needAckNumber=-1; // Assume no ack
#if debug>1
printf("Protocol: %d\n",p->ch1);
printf("Channel: %d\n",p->ch1);
printf("Channel: %d\n",p->ch2);
printf("Length: %d\n",p->len);
printf("(MPI) func:%d\n",p->func);
#endif
if (p->func==0xf0) {
#if debug>1
printf("0xf0:PDU transport\n");
#endif
// p1.header=((uc*)p)+sizeof(ISOpacket);
dc->PDUstartI=sizeof(ISOpacket);
_daveSetupReceivedPDU(dc, &p1);
#if debug>1
_daveDumpPDU(&p1);
#endif
pr.header=resp+sizeof(ISOpacket);
p2= (ISOpacket*) resp;
p2->ch1=p->ch2;
p2->ch2=p->ch1;
if (p1.param[0]==daveFuncRead) {
_daveHandleRead(&p1,&pr);
haveResp=1;
p2->len=pr.hlen+pr.plen+pr.dlen+7;
} else if (p1.param[0]==daveFuncWrite) {
_daveHandleWrite(&p1,&pr);
haveResp=1;
p2->len=pr.hlen+pr.plen+pr.dlen+7;
} else if (p1.param[0]==240) {
printf("PDU function code: %d, negociate PDU len\n",p1.param[0]);
_daveDump("packet:",dc->msgIn,dc->msgIn[3]);
memcpy(resp, dc->msgIn, dc->msgIn[3]);
resp[23]=960 / 0x100;
resp[24]=960 % 0x100;
haveResp=1;
} else if (p1.param[0]==0) {
printf("PDU function code: %d, system Message ?\n",p1.param[0]);
haveResp=handleSystemMessage(&p1,&pr);
pr.header[4]=p1.header[4]; // give the PDU a number
haveResp=1;
p2->len=pr.hlen+pr.plen+pr.dlen+7;
} else {
printf("Unsupported PDU function code: %d\n",p1.param[0]);
}
}
if (p->func==0xe0) {
int rack=*(dc->msgIn+17)-1;
int slot=*(dc->msgIn+18);
printf("Connect to rack:%d slot:%d \n", rack, slot);
*(r5+16)=rack+1;
*(r5+17)=slot;
memcpy(resp, r5, sizeof(r5));
haveResp=1;
}
if (haveResp) {
// simulating CP response delay:
usleep(10000);
//#define sim_broken_transport
#undef sim_broken_transport
#ifdef sim_broken_transport
double pr=random();
pr/=RAND_MAX;
if (pr>=0.90) {
LOG2("faking broken transport: %0.2f >= 0.95 \n",pr);
} else
write(dc->iface->fd.wfd,resp,resp[3]);
#else
write(dc->iface->fd.wfd,resp,resp[3]);
#endif
#if debug >1
_daveDump("I send:",resp,resp[3]);
#endif
}
};
typedef struct _portInfo {
int fd;
}portInfo;
#define mymemcmp _daveMemcmp
void *portServer(void *arg)
{
_daveOSserialType s;
daveInterface * di;
daveConnection * dc;
int waitCount,res,pcount;
portInfo * pi=(portInfo *) arg;
LOG2("portServer: My fd is:%d\n", pi->fd);
FLUSH;
waitCount = 0;
daveSetDebug(0);//daveDebugAll;
pcount=0;
s.rfd=pi->fd;
s.wfd=pi->fd;
di =daveNewInterface(s,"IF1",0,daveProtoISOTCP,daveSpeed187k);
di->timeout=900000;
dc=daveNewConnection(di,2,0,2);
while (waitCount < 100) {
dc->AnswLen=_daveReadISOPacket(dc->iface, dc->msgIn);
if (dc->AnswLen>0) {
res=dc->AnswLen;
#if debug>1
LOG2( "%d ", pcount);
_daveDump("packet", dc->msgIn, dc->AnswLen);
#endif
waitCount = 0;
analyze(dc);
pcount++;
} else {
waitCount++;
}
}
LOG1( "portServer: I closed my fd.\n");
FLUSH;
return NULL;
}
/*
This waits in select for a file descriptor from accepter and starts a new child server
with this file descriptor.
*/
int main(int argc, char **argv)
{
// int PID=getpid();
portInfo pi;
int filedes[2], res, newfd;
fd_set FDS;
char * s2;
pthread_attr_t attr;
pthread_t ac, ps;
accepter_info ai;
printf("CP-Simulation for ISO over TCP\n");
if (argc<2) {
printf("Assuming standard port (102) for ISO over TCP\n");
printf("If you don't want that do: isotest4 xxx, and I will listen on xxx.\n\n");
s2 = "102 ";
} else
s2 = argv[1];
printf("Assuming rack 0, slot 2 for simulated device. There's currently no way to change that.\n\n");
readCallBack=dummyRead;
writeCallBack=dummyWrite;
pipe(filedes);
ai.port = atol(s2);
LOG2( "Main serv: port: %d\n", ai.port);
ai.fd = filedes[1];
LOG2( "Main serv: Accepter pipe fd: %d\n", ai.fd);
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
res=pthread_create(&ac, &attr, accepter, &ai /*&filedes[1] */ );
do {
FD_ZERO(&FDS);
FD_SET(filedes[0], &FDS);
LOG2( "Main serv: about to select on %d\n",
filedes[0]);
FLUSH;
if (select(filedes[0] + 1, &FDS, NULL, &FDS, NULL) > 0) {
LOG1( "Main serv: about to read\n");
res = read(filedes[0], &pi.fd, sizeof(pi.fd));
ps=0;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
res=pthread_create(&ps, &attr, portServer, &pi);
if(res) {
LOG2( "Main serv: create error:%s\n", strerror(res));
close(newfd);
usleep(100000);
}
}
}
while (1);
return 0;
}
/*
Changes:
14/07/2003 give a hint about usage
08/04/2004 Logging for multithreaded things now uses PID, which is set in thread creation
*/

191
libnodave.DEF Normal file
View File

@@ -0,0 +1,191 @@
LIBRARY
EXPORTS
__daveAnalyze
__daveAnalyzePPI
_daveAddData
_daveAddParam
_daveAddUserData
_daveAddValue
_daveConnectPLCMPI1
_daveConnectPLCMPI2
_daveConnectPLCPPI
_daveConnectPLCTCP
_daveConnectPLC_IBH
_daveConstructBadReadResponse
_daveConstructDoUpload
_daveConstructEndUpload
_daveConstructReadResponse
_daveConstructUpload
_daveConstructWriteResponse
_daveDisconnectAdapterMPI
_daveDisconnectPLCMPI
_daveDisconnectPLC_IBH
_daveDump
_daveDumpPDU
_daveExchange
_daveExchangeIBH
_daveExchangeMPI
_daveExchangePPI
_daveExchangePPI_IBH
_daveExchangeTCP
_daveGetAck
_daveGetResponseISO_TCP
_daveGetResponseMPI
_daveGetResponseMPI_IBH
_daveGetResponsePPI
_daveGetResponsePPI_IBH
_daveHandleRead
_daveHandleWrite
_daveIncMessageNumber
_daveInitAdapterMPI1
_daveInitAdapterMPI2
_daveInitPDUheader
_daveInitStepIBH
_daveListReachablePartnersDummy
_daveListReachablePartnersMPI
_daveListReachablePartnersMPI_IBH
_daveMemcmp
_daveNegPDUlengthRequest
_davePackPDU
_davePackPDU_PPI
_daveReadChars2
_daveReadIBHPacket
_daveReadISOPacket
_daveReadMPI2
_daveReadMPINLpro
_daveReadMPI
_daveReadOne
_daveReadSingle
_daveReturnOkDummy2
_daveReturnOkDummy
_daveSendAck
_daveSendDialog2
_daveSendIBHNetAck
_daveSendIBHNetAckPPI
_daveSendIt
_daveSendLength
_daveSendMPIAck2
_daveSendMPIAck_IBH
_daveSendMessageMPI
_daveSendMessageMPI_IBH
_daveSendRequestData
_daveSendSingle
_daveSendWithCRC
_daveSendWithPrefix2
_daveSendWithPrefix
_daveSetupReceivedPDU
_daveTestPGReadResult
_daveTestReadResult
_daveTestResultData
_daveTestWriteResult
_daveWriteIBH
closePort
closeSocket
closeS7online
daveAddBitVarToReadRequest
daveAddBitVarToWriteRequest
daveAddToReadRequest
daveAddVarToReadRequest
daveAddVarToWriteRequest
daveAreaName
daveBlockName
daveBuildAndSendPDU
daveClrBit
daveCopyRAMtoROM
daveConnectPLC
daveDisconnectAdapter
daveDisconnectPLC
daveExecReadRequest
daveExecWriteRequest
daveForce200
daveForceDisconnectIBH
daveFree
daveFreeResults
daveFromBCD
daveGetAnswLen
daveGetBlockInfo
daveGetCounterValue
daveGetCounterValueAt
daveGetDebug
daveGetErrorOfResult
daveGetFloat
daveGetFloatAt
daveGetFloatfrom
daveGetMPIAdr
daveGetMaxPDULen
daveGetName
daveGetOrderCode
daveGetPDUerror
daveGetProgramBlock
daveGetResponse
daveGetS16
daveGetS16At
daveGetS16from
daveGetS32
daveGetS32At
daveGetS32from
daveGetS8
daveGetS8At
daveGetS8from
daveGetSeconds
daveGetSecondsAt
daveGetTimeout
daveGetU16
daveGetU16At
daveGetU16from
daveGetU32
daveGetU32At
daveGetU32from
daveGetU8
daveGetU8At
daveGetU8from
daveInitAdapter
daveListBlocks
daveListBlocksOfType
daveListReachablePartners
daveNewConnection
daveNewInterface
daveNewPDU
daveNewResultSet
davePascalNewInterface
davePrepareReadRequest
davePrepareWriteRequest
davePut16
davePut16At
davePut32
davePut32At
davePut8
davePut8At
davePutFloat
davePutFloatAt
daveReadBits
daveReadBytes
daveReadManyBytes
daveReadPLCTime
daveReadSZL
daveResetIBH
daveSendMessage
daveSetBit
daveSetDebug
daveSetPLCTime
daveSetPLCTimeToSystime
daveSetTimeout
daveStart
daveStop
daveStrerror
daveStringCopy
daveSwapIed_16
daveSwapIed_32
daveToBCD
daveToPLCfloat
daveUseResult
daveWriteBits
daveWriteBytes
daveWriteManyBytes
doUpload
endUpload
initUpload
openS7online
openSocket
setPort
toPLCfloat

18
loadPLC Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/sh
MPI="2";
for a in SDB*.mc7; do
echo $a;
./testMPIload --mpi=$MPI /dev/ttyS0 $a
done;
for a in DB*.mc7; do
echo $a;
./testMPIload --mpi=$MPI /dev/ttyS0 $a
done;
for a in FC*.mc7; do
echo $a;
./testMPIload --mpi=$MPI /dev/ttyS0 $a
done;
for a in OB*.mc7; do
echo $a;
./testMPIload --mpi=$MPI /dev/ttyS0 $a
done;

57
log2.h Normal file
View File

@@ -0,0 +1,57 @@
#ifndef __log
#define __log
#ifdef LINUX
#define HAVE_PRINTF
#define logout stdout
#endif
#ifdef BCCWIN
#define HAVE_PRINTF
#define logout stdout
#endif
#ifdef KEILC51
#endif
#ifdef DOS
#define HAVE_PRINTF
#define logout stdout
#endif
#ifdef AVR
#define NO_PRINT_CODE
#endif
#ifdef HAVE_PRINTF
#define LOG1(x) fprintf(logout,x)
#define LOG2(x,y) fprintf(logout,x,y)
#define LOG3(a,b,c) fprintf(logout,a,b,c)
#define LOG4(a,b,c,d) fprintf(logout,a,b,c,d)
#define LOG5(a,b,c,d,e) fprintf(logout,a,b,c,d,e)
#define LOG6(a,b,c,d,e,f) fprintf(logout,a,b,c,d,e,f)
#define LOG7(a,b,c,d,e,f,g) fprintf(logout,a,b,c,d,e,f,g)
#define FLUSH fflush(logout)
#define LOG_1(a) fprintf(logout,a)
#define LOG_2(a,b) fprintf(logout,a,b)
#endif /* HAVE_PRINTF */
#ifdef NO_PRINT_CODE
#define LOG1(x)
#define LOG2(x,y)
#define LOG3(a,b,c)
#define LOG4(a,b,c,d)
#define LOG5(a,b,c,d,e)
#define LOG6(a,b,c,d,e,f)
#define LOG7(a,b,c,d,e,f,g)
#define FLUSH
#define LOG_1(a)
#define LOG_2(a,b)
#endif /* NO_PRINT_CODE */
#endif /* __log */

77
makefile.mak Normal file
View File

@@ -0,0 +1,77 @@
#
# Makefile to build a Windows Version of LIBNODAVE using the free command line tools
# from Borland.
#
# type 'make' to build libnodave.dll and some statically linked test programs.
# type 'make dynamic' to make libnodave.dll and some dynamically linked test programs.
#
# The directory where the tools are:
#
BCCPATH=../bin
CC=$(BCCPATH)/bcc32
LL=$(BCCPATH)/ilink32
CFLAGS= -I..\include -c -DBCCWIN -DDAVE_LITTLE_ENDIAN
PROGRAMS=testMPI.exe testPPI.exe \
testPPI_IBH.exe testPPI_IBHload.exe testPPIload.exe \
testMPIload.exe testISO_TCP.exe testISO_TCPload.exe testIBH.exe testMPI_IBHload.exe
DYNAMIC_PROGRAMS=testMPId.exe testPPId.exe testISO_TCPd.exe testIBHd.exe testPPI_IBHd.exe
LIBRARIES=libnodave.dll
all: $(PROGRAMS) $(LIBRARIES)
dynamic: $(DYNAMIC_PROGRAMS)
testISO_TCP.exe: nodave.obj openSocketw.obj testISO_TCP.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 testISO_TCP.obj openSocketw.obj nodave.obj, testISO_TCP.exe,,CW32 IMPORT32
testMPI.exe: setportw.obj nodave.obj testMPI.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 setportw.obj nodave.obj testMPI.obj, testMPI.exe,,CW32 IMPORT32 ws2_32
testIBH.exe: openSocketw.obj nodave.obj testIBH.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 openSocketw.obj nodave.obj testIBH.obj, testIBH.exe,,CW32 IMPORT32 ws2_32
testIBHd.exe: libnodave.dll testIBH.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 testIBH.obj, testIBHd.exe,,CW32 IMPORT32 ws2_32 libnodave.lib
testPPI_IBH.exe: openSocketw.obj nodave.obj testPPI_IBH.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 openSocketw.obj nodave.obj testPPI_IBH.obj, testPPI_IBH.exe,,CW32 IMPORT32 ws2_32
testPPI_IBHd.exe: libnodave.dll testPPI_IBH.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 testPPI_IBH.obj, testPPI_IBHd.exe,,CW32 IMPORT32 ws2_32 libnodave.lib
testMPId.exe: libnodave.dll testMPI.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 testMPI.obj , testMPId.exe,,CW32 IMPORT32 ws2_32 libnodave.lib
testPPI.exe: testPPI.obj nodave.obj setportw.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 setportw.obj nodave.obj testPPI.obj, testPPI.exe,,CW32 IMPORT32
testPPId.exe: libnodave.dll testPPI.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 testPPI.obj, testPPId.exe,,libnodave CW32 IMPORT32 ws2_32
testISO_TCPd.exe: libnodave.dll testISO_TCP.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 testISO_TCP.obj, testISO_TCPd.exe,,CW32 IMPORT32 ws2_32 libnodave.lib
testPPIload.exe: nodave.obj setportw.obj testPPIload.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 setportw.obj nodave.obj testPPIload.obj, testPPIload.exe,,CW32 IMPORT32
testMPIload.exe: nodave.obj setportw.obj testMPIload.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 setportw.obj nodave.obj testMPIload.obj, testMPIload.exe,,CW32 IMPORT32
testISO_TCPload.exe: nodave.obj openSocketw.obj testISO_TCPload.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 openSocketw.obj nodave.obj testISO_TCPload.obj, testISO_TCPload.exe,,CW32 IMPORT32
testMPI_IBHload.exe: nodave.obj openSocketw.obj testMPI_IBHload.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 openSocketw.obj nodave.obj testMPI_IBHload.obj, testMPI_IBHload.exe,,CW32 IMPORT32
testPPI_IBHload.exe: nodave.obj openSocketw.obj testPPI_IBHload.obj
$(LL) /r /Tpe /L..\lib ..\lib\c0x32 openSocketw.obj nodave.obj testPPI_IBHload.obj, testPPI_IBHload.exe,,CW32 IMPORT32
libnodave.dll: nodave.obj setportw.obj openSocketw.obj
$(LL) /v /C /Gn /w /Gk /r /Tpd /L..\lib ..\lib\c0d32 nodave setportw openSocketw , libnodave.dll,,IMPORT32 CW32
$(BCCPATH)/implib libnodave.lib libnodave.dll
#
# delete all but the sources:
#
clean:
del /f /q *.tds *.il? *.obj *.map *.lib *.dll *.exe *.exp
#
# delete all but what is needed for distribution:
#
distclean:

93
makefile.vc Normal file
View File

@@ -0,0 +1,93 @@
#
# Makefile to build a Windows Version of LIBNODAVE using MSVC++ from Microsoft.
#
# type 'make' to build libnodave.dll and some statically linked test programs.
# type 'make dynamic' to make libnodave.dll and some dynamically linked test programs.
#
# The directory where the tools are:
#
VCPATH=d:\programme\microsoft visual C++ Toolkit 2003
SDKPATH=d:\programme\microsoft platform sdk
VCPATH=c:\programme\microsoft visual Studio 9.0\vc
SDKPATH=c:\programme\microsoft sdks\windows\v6.0a
CC="$(VCPATH)\bin\cl"
LL="$(VCPATH)\bin\link"
CFLAGS= -I"$(VCPATH)\include" -I"$(SDKPATH)\include" -c -DBCCWIN -DDAVE_LITTLE_ENDIAN -TC
LFLAGS= /LIBPATH:"$(VCPATH)"\lib,"$(SDKPATH)"\lib
LLFLAGS = /LIBPATH:"$(VCPATH)\lib" /DEF:libnodave.DEF
PROGRAMS=testMPI.exe testPPI.exe testAS511.exe\
testPPI_IBH.exe testPPI_IBHload.exe testPPIload.exe \
testMPIload.exe testISO_TCP.exe testISO_TCPload.exe testIBH.exe testMPI_IBHload.exe \
testNLpro.exe testS7online.exe
DYNAMIC_PROGRAMS=testMPId.exe testPPId.exe testISO_TCPd.exe testIBHd.exe testPPI_IBHd.exe
LIBRARIES=libnodave.dll
all: $(PROGRAMS) $(LIBRARIES)
dynamic: $(DYNAMIC_PROGRAMS)
testISO_TCP.exe: nodave.obj openSocketw.obj testISO_TCP.obj
$(LL) $(LFLAGS) testISO_TCP.obj openSocketw.obj nodave.obj "$(SDKPATH)"\lib\ws2_32.lib /OUT:testISO_TCP.exe
testMPI.exe: setportw.obj nodave.obj testMPI.obj
$(LL) $(LFLAGS) setportw.obj nodave.obj testMPI.obj "$(SDKPATH)"\lib\ws2_32.lib /OUT:testMPI.exe
testIBH.exe: openSocketw.obj nodave.obj testIBH.obj
$(LL) $(LFLAGS) openSocketw.obj nodave.obj testIBH.obj "$(SDKPATH)"\lib\ws2_32.lib /OUT:testIBH.exe
testIBHd.exe: libnodave.dll testIBH.obj
$(LL) $(LFLAGS) testIBH.obj libnodave.lib /OUT:testIBHd.exe
testPPI_IBH.exe: openSocketw.obj nodave.obj testPPI_IBH.obj
$(LL) $(LFLAGS) openSocketw.obj nodave.obj testPPI_IBH.obj "$(SDKPATH)"\lib\ws2_32.lib /OUT:testPPI_IBH.exe
testPPI_IBHd.exe: libnodave.dll testPPI_IBH.obj
$(LL) $(LFLAGS) testPPI_IBH.obj libnodave.lib /OUT:testPPI_IBHd.exe
testMPId.exe: libnodave.dll testMPI.obj
$(LL) $(LFLAGS) testMPI.obj libnodave.lib /OUT:testMPId.exe
testPPI.exe: testPPI.obj nodave.obj setportw.obj
$(LL) $(LFLAGS) setportw.obj nodave.obj testPPI.obj "$(SDKPATH)"\lib\ws2_32.lib /OUT:testPPI.exe
testPPId.exe: libnodave.dll testPPI.obj
$(LL) $(LFLAGS) testPPI.obj libnodave.lib /OUT:testPPId.exe
testISO_TCPd.exe: libnodave.dll testISO_TCP.obj
$(LL) $(LFLAGS) testISO_TCP.obj libnodave.lib /OUT:testISO_TCPd.exe
testPPIload.exe: nodave.obj setportw.obj testPPIload.obj
$(LL) $(LFLAGS) setportw.obj nodave.obj testPPIload.obj "$(SDKPATH)"\lib\ws2_32.lib /OUT:testPPIload.exe
testMPIload.exe: nodave.obj setportw.obj testMPIload.obj
$(LL) $(LFLAGS) setportw.obj nodave.obj testMPIload.obj "$(SDKPATH)"\lib\ws2_32.lib /OUT:testMPIload.exe
testISO_TCPload.exe: nodave.obj openSocketw.obj testISO_TCPload.obj
$(LL) $(LFLAGS) openSocketw.obj nodave.obj testISO_TCPload.obj "$(SDKPATH)"\lib\ws2_32.lib /OUT:testISO_TCPload.exe
testMPI_IBHload.exe: nodave.obj openSocketw.obj testMPI_IBHload.obj
$(LL) $(LFLAGS) openSocketw.obj nodave.obj testMPI_IBHload.obj "$(SDKPATH)"\lib\ws2_32.lib /OUT:testMPI_IBHload.exe
testPPI_IBHload.exe: nodave.obj openSocketw.obj testPPI_IBHload.obj
$(LL) $(LFLAGS) openSocketw.obj nodave.obj testPPI_IBHload.obj "$(SDKPATH)"\lib\ws2_32.lib /OUT:testPPI_IBHload.exe
testAS511.exe: testAS511.obj nodave.obj setportw.obj
$(LL) $(LFLAGS) setportw.obj nodave.obj testAS511.obj "$(SDKPATH)"\lib\ws2_32.lib /out:$@
testNLpro.exe: testNLpro.obj nodave.obj openSocketw.obj
$(LL) $(LFLAGS) openSocketw.obj nodave.obj testNLpro.obj "$(SDKPATH)"\lib\ws2_32.lib /out:$@
libnodave.dll: nodave.obj setportw.obj openSocketw.obj openS7online.obj
$(LL) $(LLFLAGS) /DLL nodave.obj setportw.obj openSocketw.obj openS7online.obj "$(SDKPATH)"\lib\ws2_32.lib /OUT:libnodave.dll
testS7online.exe: testS7online.obj nodave.obj openS7online.obj
$(LL) $(LFLAGS) nodave.obj testS7online.obj openS7online.obj "$(SDKPATH)"\lib\ws2_32.lib "$(SDKPATH)"\lib\user32.lib /out:$@
#
# delete all but the sources:
#
clean:
del /f /q *.tds *.il? *.obj *.map *.lib *.dll *.exe *.exp
distclean:
del /f /q *.tds *.il? *.obj *.map
nodave.obj: nodave.c nodave.h
$(CC) $(CFLAGS) -DDOEXPORT nodave.c
setportw.obj: setportw.c
$(CC) $(CFLAGS) -DDOEXPORT setportw.c
openSocketw.obj: openSocketw.c
$(CC) $(CFLAGS) -DDOEXPORT openSocketw.c
openS7online.obj: openS7online.c openS7online.h
$(CC) $(CFLAGS) -DDOEXPORT openS7online.c
s7fake.obj: s7fake.c
$(CC) $(CFLAGS) -DDOEXPORT s7fake.c

7964
nodave.c Normal file

File diff suppressed because it is too large Load Diff

1291
nodave.h Normal file

File diff suppressed because it is too large Load Diff

149
nodaveext.c Normal file
View File

@@ -0,0 +1,149 @@
#include "nodave.h"
char * DECL2 daveStrerrorExt(int code) {
switch (code) {
case 0x8001: return "Der angeforderte Dienst kann im derzeitigen Zustand der Baugruppe nicht ausgefuehrt werden. Weitere Bausteinfunktionen sind deshalb nicht moeglich.";
case 0x8003: return "S7-Protokollfehler. Es ist ein Fehler beim Uebertragen des Bausteins aufgetreten.";
case 0x8101: return "Applikation, allgemeiner Fehler: Dienst ist der remoten Baugruppe unbekannt.";
case 0x8104: return "Der Dienst ist auf der Baugruppe nicht implementiert oder es wird ein Telegrammfehler gemeldet.";
case 0x8204: return "Die Typangabe zum Objekt ist inkonsistent.";
case 0x8205: return "Ein kopierter Baustein ist bereits vorhanden und nicht eingekettet.";
case 0x8301: return "Der Speicherplatz oder der Arbeitsspeicher auf der Baugruppe ist nicht ausreichend bzw. das angegebene Speichermedium ist nicht erreichbar.";
case 0x8302: return "Es liegt ein Ressourcen-Engpass vor bzw. die Prozessor-Ressourcen sind nicht verfuegbar.";
case 0x8304: return "Es ist kein weiterer Hochladevorgang mehr moeglich. Es liegt ein Ressourcen-Engpass vor.";
case 0x8305: return "Die Funktionalitaet ist nicht verfuegbar.";
case 0x8306: return "Arbeitsspeicher nicht ausreichend (bai kopieren, Einketten, AWP laden).";
case 0x8307: return "Remanenter Teil des Arbeitsspeichers nicht ausreichend (bai kopieren, Einketten, AWP laden).";
case 0x8401: return "S7-Protokollfehler: Falsche Reihenfolge der Dienste (z.B. beim Laden oder Hochladen eines Bausteins).";
case 0x8402: return "Dienst kann wegen des Zustands des adressierten Objekts nicht ausgefuehrt werden.";
case 0x8404: return "S7-Protokoll: Die Funktion kann nicht ausgefuehrt werden.";
case 0x8405: return "Der remote Baustein ist im Zustand DISABLE (PBK).Die Funktion kann nicht ausgefuehrt werden.";
case 0x8500: return "S7-Protokollfehler: Falsche Telegramme.";
case 0x8503: return "Meldung von der Baugruppe: Der Dienst wurde vorzeitig abgebrochen.";
case 0x8701: return "Fehler bei der Adressierung des Objekts im Kommunikationspartner(z.B. Bereichslaengenfehler).";
case 0x8702: return "Der angeforderte Dienst wird von der Baugruppe nicht unterstuetzt.";
case 0x8703: return "Der Zugriff auf das Objekt wurde abgelehnt.";
case 0x8704: return "Zugriffsfehler: Das Objekt ist zerstoert.";
case 0xd001: return "Protokollfehler: Die Auftragsnummer ist unzulaessig.";
case 0xd002: return "Parameterfehler: Die Auftragsvariante ist unzulaessig.";
case 0xd003: return "Parameterfehler: Die Testfunktion wird von der Baugruppe nicht unterstuetzt.";
case 0xd004: return "Parameterfehler: Der Auftragsstatus ist unzulaessig.";
case 0xd005: return "Parameterfehler: Die Auftragsbeendung ist unzulaessig.";
case 0xd006: return "Parameterfehler: Die Kennung fuer den Verbindungsabbruch ist unzulaessig.";
case 0xd007: return "Parameterfehler: Die Anzahl der Pufferelemente ist unzulaessig.";
case 0xd008: return "Parameterfehler: Der Untersetzungsfaktor ist unzulaessig.";
case 0xd009: return "Parameterfehler: Die Ausfuehrungsanzahl ist unzulaessig.";
case 0xd00a: return "Parameterfehler: Das Triggerereignis ist unzulaessig.Bitte pruefen Sie, ob der angegebene Trigger auf dieser Baugruppe zulaessig ist.";
case 0xd00b: return "Parameterfehler: Das Triggerbedingung ist unzulaessig.Bitte pruefen Sie, ob der angegebene Trigger auf dieser Baugruppe zulaessig ist.";
case 0xd011: return "Parameterfehler im Pfad der Aufrufumgebung: Der Baustein ist nicht vorhanden.";
case 0xd012: return "Parameterfehler: Falsche Adresse im Baustein.";
case 0xd014: return "Parameterfehler: Der Baustein wird gerade geloescht/ueberladen.";
case 0xd015: return "Parameterfehler: Die Variablenadresse ist unzulaessig.";
case 0xd016: return "Parameterfehler: Testauftraege nicht moeglich, da Anwenderprogramm fehlerhaft.";
case 0xd017: return "Parameterfehler: Die Triggernummer ist unzulaessig.";
case 0xd025: return "Parameterfehler: Der Pfad ist unzulaessig.";
case 0xd026: return "Parameterfehler: Die Zugriffsart ist unzulaessig.";
case 0xd027: return "Parameterfehler: Die Anzahl der DBs ist unzulaessig.";
case 0xd031: return "interner Protokollfehler.";
case 0xd032: return "Parameterfehler: Falsche Laenge des Ergebnispuffers.";
case 0xd033: return "Parameterfehler: Die Auftragslaenge ist unzulaessig.";
case 0xd03f: return "Kodierungsfehler: Fehler im Parameterteil (z.B. Reservebytes ungleich NULL).";
case 0xd041: return "Datenfehler: Die Kennung der Statusliste ist unzulaessig.";
case 0xd042: return "Datenfehler: Die Variablenadresse ist unzulaessig.";
case 0xd044: return "Datenfehler: Der Variablenwert ist unzulaessig.Ueberpruefen Sie die Auftragsdaten.";
case 0xd045: return "Datenfehler: Das Verlassen der ODIS-Steuerung bei HALT ist unzulaessig.";
case 0xd046: return "Datenfehler: Die Messtufe bei Laufzeitmessung ist unzulaessig.";
case 0xd047: return "Datenfehler: Die Hierarchie bei 'Auftragsliste lesen' ist unzulaessig.";
case 0xd048: return "Datenfehler: Die Loeschkennung bei 'Auftrag loeschen' ist unzulaessig.";
case 0xd049: return "Datenfehler: Die Ersetzkennung bei 'Auftrag ersetzen' ist unzulaessig.";
case 0xd04a: return "Fehler bei der Ausfuehrung von 'Programmstatus'.";
case 0xd05f: return "Kodierungsfehler: Fehler im Datenteil (z.B. Reservebytes ungleich NULL,...).";
case 0xd061: return "Ressourcenfehler: Es ist kein Speicherplatz fuer den Auftrag vorhanden.";
case 0xd062: return "Ressourcenfehler: Die Auftragsliste ist voll.";
case 0xd063: return "Ressourcenfehler: Das Triggerereignis ist belegt.";
case 0xd064: return "Ressourcenfehler: Der Speicherplatz fuer ein Element des Ereignispuffers ist zu klein.";
case 0xd065: return "Ressourcenfehler: Der Speicherplatz fuer mehrere Elemente des Ereignispuffers ist zu klein.";
case 0xd066: return "Ressourcenfehler: Der Timer fuer die Laufzeitmessung ist durch einen anderen Auftrag belegt.";
case 0xd067: return "Ressourcenfehler: Es sind zu viele 'Steuern Variable'-Auftraege aktiv (insbesondere Mehrprozessorbetrieb).";
case 0xd081: return "Die Funktion ist im aktuellen Betriebszustand unzulaessig.";
case 0xd082: return "Betriebszustandsfehler: Betriebszustand HALT kann nicht verlassen werden.";
case 0xd0a2: return "Funktion ist momentan nicht moeglich, da eine speicherveraendernde Funktion laeuft.";
case 0xd0a3: return "Es sind zu viele 'Steuern Variable'-Auftraege auf die Peripherie aktiv (insbesondere Mehrprozessorbetrieb).";
case 0xd0a4: return "'Forcen' ist schon eingerichtet.";
case 0xd0a5: return "Der referenzierte Auftrag ist nicht vorhanden.";
case 0xd0a6: return "Der Auftrag kann nicht gesperrt/freigegeben werden.";
case 0xd0a7: return "Der Auftrag kann nicht geloescht werden, da dieser z.B. gerade gelesen wird. Versuchen Sie es spaeter noch einmal.";
case 0xd0a8: return "Der Auftrag kann nicht ersetzt werden, da dieser z.B. gerade gelesen oder geloescht wird. Versuchen Sie es spaeter noch einmal.";
case 0xd0a9: return "Der Auftrag kann nicht gelesen werden, da dieser z.B. gerade geloescht wird. Versuchen Sie es spaeter noch einmal.";
case 0xd0aa: return "Das Zeitlimit im Prozessbetrieb ist ueberschritten.";
case 0xd0ab: return "Die Auftragsparameter sind im Prozessbetrieb unzulaessig.";
case 0xd0ac: return "Die Auftragsdaten sind im Prozessbetrieb unzulaessig.";
case 0xd0ad: return "Der Betriebsmodus ist schon eingestellt.";
case 0xd0ae: return "Der Auftrag wurde ueber eine andere Verbindung eingerichtet und kann nur ueber diese hantiert werden.";
case 0xd0c1: return "Beim Zugriff auf die Variable(n) wurde mindestens ein Fehler erkannt.";
case 0xd0c2: return "Betriebszustandsuebergang in STOP/HALT.";
case 0xd0c3: return "Beim Zugriff auf die Variable(n) wurde mindestens ein Fehler erkannt und Betriebszustandsuebergang in STOP/HALT.";
case 0xd0c4: return "Timerueberlauf bei der die Laufzeitmessung";
case 0xd0c5: return "Die Ausgabe des Bausteinstacks ist inkonsistent, da Bausteine geloescht/nachgeladen wurden.";
case 0xd0c6: return "Der Auftrag wurde automatisch geloescht, weil alle in ihm referenzierten Auftraege geloescht wurden.";
case 0xd0c7: return "Der Auftrag wurde automatisch geloescht, wegen Verlassens des Betriebszustands STOP.";
case 0xd0c8: return "'Status Baustein' wurde abgebrochen wegen Inkonsistenz zwischen Testauftrag und Programm.";
case 0xd0c9: return "Verlassen des Statusbereichs durch Ruecksetzen des OB90.";
case 0xd0ca: return "Verlassen des Statusbereichs durch Ruecksetzen des OB90 und Zugriffsfehler beim Lesen der Variablen vor dem Verlassen.";
case 0xd0cb: return "Die Ausgabesperre der Peripherie ist wieder eingeschaltet.";
case 0xd0cc: return "Der Datenumfang fuer die Testfunktion ist durch das Zeitlimit eingeschraenkt.";
case 0xd231: return "Mindestens ein geladener OB kann nicht kopiert werden, da die zugehoerige Ablaufebene nicht existiert.";
case 0xd232: return "Mindestens eine Bausteinnummer eines geladenen Bausteins ist unzulaessig.";
case 0xd234: return "Der Baustein ist im angegebenen Speichermedium oder im Auftrag doppelt vorhanden.";
case 0xd235: return "Der Baustein enthaelt eine fehlerhafte Pruefsumme.";
case 0xd236: return "Der Baustein enthaelt keine Pruefsumme.";
case 0xd237: return "Der Baustein soll doppelt geladen werde, d.h. es existiert schon derselbe Baustein mit gleichem Zeitstempel auf der CPU.";
case 0xd238: return "Mindestens ein angegebener Baustein ist kein DB.";
case 0xd239: return "Mindestens ein angegebener DB ist nicht als eingekettete Variante im Ladespeicher vorhanden.";
case 0xd23a: return "Mindestens ein angegebener DB hat wesentliche Unterschiede zwischen kopierter und eingeketteter Variante.";
case 0xd240: return "Die Koordinierungsregeln wurden verletzt.";
case 0xd241: return "Die Funktion ist in der aktuellen Schutzstufe nicht erlaubt.";
case 0xd242: return "Schutzverletzung bei der Bearbeitung von F-Bausteinen. F-Bausteine knnen nur nach Eingabe es Passwortes bearbeitet werden. Der F-Baustein SDB99 kann nicht geloescht werden.";
case 0xd250: return "Die Update- und Baugruppenkennung oder der Ausgabestand stimmen nicht ueberein.";
case 0xd251: return "Falsche Reihenfolge der Betriebssystem-Komponenten.";
case 0xd252: return "Pruefsummenfehler.";
case 0xd253: return "Es ist kein ablauffaehiger Lader vorhanden; eine Aktualisierung ist nur ueber die Memory Card moeglich.";
case 0xd254: return "Speicherfehler im Betriebssystem.";
case 0xd280: return "Fehler bei der Uebersetzung eines Bausteins in S7-300 CPU.";
case 0xd401: return "Auskunftsfunktion nicht verfuegbar.";
case 0xd402: return "Auskunftsfunktion nicht verfuegbar.";
case 0xd403: return "Der Dienst ist bereits angemeldet/abgemeldet (Diagnose/PMC).";
case 0xd404: return "Die maximale Teilnehmerzahl wurde erreicht. Keine weitere Anmeldung fuer Diagnose/PMC moeglich.";
case 0xd405: return "Der Dienst wird nicht unterstuetzt oder Syntaxfehler bei den Funktionsparametern.";
case 0xd406: return "Die gewnschte Information ist temporaer nicht vorhanden.";
case 0xd407: return "Es ist ein Diagnose-Fehler aufgetreten.";
case 0xd408: return "Update abgebrochen.";
case 0xd409: return "Am DP-Bus ist ein Fehler aufgetreten.";
default: return daveStrerror(code);
}
}

2
nodaveext.h Normal file
View File

@@ -0,0 +1,2 @@
//#include "nodave.h"
char * DECL2 daveStrerrorExt(int code);

649
nodavesimple.h Normal file
View File

@@ -0,0 +1,649 @@
/*
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
or VIPA Speed7 with builtin ethernet support.
(C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2002..2005
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.
*/
/*
Do NOT use this include file in C programs. It is just here for people who want
to interface Libnodave with other programming languages, so they see that they
do not neccessarily need to implement all the internal structures.
So if you plan to interface with other languages, this file shall show you
show the minimum of structures and funtions you'll need to make known to your compiler.
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __nodave
#define __nodave
#ifdef LINUX
#define DECL2
#define EXPORTSPEC
typedef struct {
int rfd;
int wfd;
} _daveOSserialType;
#include <stdlib.h>
#else
#ifdef BCCWIN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define DECL2 __stdcall
//#define DECL2 cdecl
#ifdef DOEXPORT
#define EXPORTSPEC __declspec (dllexport)
#else
#define EXPORTSPEC __declspec (dllimport)
#endif
typedef struct {
HANDLE rfd;
HANDLE wfd;
} _daveOSserialType;
#else
#error Fill in what you need for your OS or API.
#endif /* BCCWIN */
#endif /* LINUX */
/*
Protocol types to be used with newInterface:
*/
#define daveProtoMPI 0 /* MPI for S7 300/400 */
#define daveProtoMPI2 1 /* MPI for S7 300/400, "Andrew's version" */
#define daveProtoMPI3 2 /* MPI for S7 300/400, Step 7 Version, not well tested */
#define daveProtoMPI4 3 /* MPI for S7 300/400, "Andrew's version" with extra STX */
#define daveProtoPPI 10 /* PPI for S7 200 */
#define daveProtoAS511 20 /* S5 programming port protocol */
#define daveProtoS7online 50 /* use s7onlinx.dll for transport */
#define daveProtoISOTCP 122 /* ISO over TCP */
#define daveProtoISOTCP243 123 /* ISO over TCP with CP243 */
#define daveProtoNLpro 230 /* MPI with NetLink Pro MPI to ethernet gateway */
#define daveProtoMPI_IBH 223 /* MPI with IBH NetLink MPI to ethernet gateway */
#define daveProtoPPI_IBH 224 /* PPI with IBH NetLink PPI to ethernet gateway */
#define daveProtoUserTransport 255 /* Libnodave will pass the PDUs of S7 Communication to user */
/* defined call back functions. */
/*
* ProfiBus speed constants:
*/
#define daveSpeed9k 0
#define daveSpeed19k 1
#define daveSpeed187k 2
#define daveSpeed500k 3
#define daveSpeed1500k 4
#define daveSpeed45k 5
#define daveSpeed93k 6
/*
Some MPI function codes (yet unused ones may be incorrect).
*/
#define daveFuncOpenS7Connection 0xF0
#define daveFuncRead 0x04
#define daveFuncWrite 0x05
#define daveFuncRequestDownload 0x1A
#define daveFuncDownloadBlock 0x1B
#define daveFuncDownloadEnded 0x1C
#define daveFuncStartUpload 0x1D
#define daveFuncUpload 0x1E
#define daveFuncEndUpload 0x1F
#define daveFuncInsertBlock 0x28
/*
S7 specific constants:
*/
#define daveBlockType_OB '8'
#define daveBlockType_DB 'A'
#define daveBlockType_SDB 'B'
#define daveBlockType_FC 'C'
#define daveBlockType_SFC 'D'
#define daveBlockType_FB 'E'
#define daveBlockType_SFB 'F'
/*
Use these constants for parameter "area" in daveReadBytes and daveWriteBytes
*/
#define daveSysInfo 0x3 /* System info of 200 family */
#define daveSysFlags 0x5 /* System flags of 200 family */
#define daveAnaIn 0x6 /* analog inputs of 200 family */
#define daveAnaOut 0x7 /* analog outputs of 200 family */
#define daveP 0x80
#define daveInputs 0x81
#define daveOutputs 0x82
#define daveFlags 0x83
#define daveDB 0x84 /* data blocks */
#define daveDI 0x85 /* instance data blocks */
#define daveLocal 0x86 /* not tested */
#define daveV 0x87 /* don't know what it is */
#define daveCounter 28 /* S7 counters */
#define daveTimer 29 /* S7 timers */
#define daveCounter200 30 /* IEC counters (200 family) */
#define daveTimer200 31 /* IEC timers (200 family) */
/**
Library specific:
**/
/*
Result codes. Genarally, 0 means ok,
>0 are results (also errors) reported by the PLC
<0 means error reported by library code.
*/
#define daveResOK 0 /* means all ok */
#define daveResNoPeripheralAtAddress 1 /* CPU tells there is no peripheral at address */
#define daveResMultipleBitsNotSupported 6 /* CPU tells it does not support to read a bit block with a */
/* length other than 1 bit. */
#define daveResItemNotAvailable200 3 /* means a a piece of data is not available in the CPU, e.g. */
/* when trying to read a non existing DB or bit bloc of length<>1 */
/* This code seems to be specific to 200 family. */
#define daveResItemNotAvailable 10 /* means a a piece of data is not available in the CPU, e.g. */
/* when trying to read a non existing DB */
#define daveAddressOutOfRange 5 /* means the data address is beyond the CPUs address range */
#define daveWriteDataSizeMismatch 7 /* means the write data size doesn't fit item size */
#define daveResCannotEvaluatePDU -123
#define daveResCPUNoData -124
#define daveUnknownError -125
#define daveEmptyResultError -126
#define daveEmptyResultSetError -127
#define daveResUnexpectedFunc -128
#define daveResUnknownDataUnitSize -129
#define daveResShortPacket -1024
#define daveResTimeout -1025
/*
error code to message string conversion:
Call this function to get an explanation for error codes returned by other functions.
*/
EXPORTSPEC char * DECL2 daveStrerror(int code);
/*
Copy an internal String into an external string buffer. This is needed to interface
with Visual Basic. Maybe it is helpful elsewhere, too.
*/
EXPORTSPEC void DECL2 daveStringCopy(char * intString, char * extString);
/*
Max number of bytes in a single message.
*/
#define daveMaxRawLen 2048
/*
Some definitions for debugging:
*/
#define daveDebugRawRead 0x01 /* Show the single bytes received */
#define daveDebugSpecialChars 0x02 /* Show when special chars are read */
#define daveDebugRawWrite 0x04 /* Show the single bytes written */
#define daveDebugListReachables 0x08 /* Show the steps when determine devices in MPI net */
#define daveDebugInitAdapter 0x10 /* Show the steps when Initilizing the MPI adapter */
#define daveDebugConnect 0x20 /* Show the steps when connecting a PLC */
#define daveDebugPacket 0x40
#define daveDebugByte 0x80
#define daveDebugCompare 0x100
#define daveDebugExchange 0x200
#define daveDebugPDU 0x400 /* debug PDU handling */
#define daveDebugUpload 0x800 /* debug PDU loading program blocks from PLC */
#define daveDebugMPI 0x1000
#define daveDebugPrintErrors 0x2000 /* Print error messages */
#define daveDebugPassive 0x4000
#define daveDebugErrorReporting 0x8000
#define daveDebugOpen 0x10000 /* print messages in openSocket and setPort */
#define daveDebugAll 0x1ffff
/*
set and read debug level:
*/
EXPORTSPEC void DECL2 daveSetDebug(int nDebug);
EXPORTSPEC int DECL2 daveGetDebug(void);
/*
Some data types:
*/
#define uc unsigned char
#define us unsigned short
#define u32 unsigned int
/*
This is a wrapper for the serial or ethernet interface. This is here to make porting easier.
*/
typedef struct _daveConnection daveConnection;
typedef struct _daveInterface daveInterface;
/*
Helper struct to manage PDUs. This is NOT the part of the packet I would call PDU, but
a set of pointers that ease access to the "private parts" of a PDU.
*/
/* While gcc-3.3 can handle a 0 size struct (at least pointers to it), BCC can't. */
/*
typedef struct {
} PDU;
*/
typedef struct {
uc * header; /* pointer to start of PDU (PDU header) */
uc * param; /* pointer to start of parameters inside PDU */
uc * data; /* pointer to start of data inside PDU */
uc * udata; /* pointer to start of data inside PDU */
int hlen; /* header length */
int plen; /* parameter length */
int dlen; /* data length */
int udlen; /* user or result data length */
} PDU;
/*
This groups an interface together with some information about it's properties
in the library's context.
*/
struct _daveInterface {
int _timeout; /* Timeout in microseconds used in transort. */
};
EXPORTSPEC daveInterface * DECL2 daveNewInterface(_daveOSserialType nfd, char * nname, int localMPI, int protocol, int speed);
/*
This holds data for a PLC connection;
*/
struct _daveConnection {
int AnswLen; /* length of last message */
uc * resultPointer; /* used to retrieve single values from the result byte array */
int maxPDUlength;
// int MPIAdr; /* The PLC's address */
};
/*
Setup a new connection structure using an initialized
daveInterface and PLC's MPI address.
*/
EXPORTSPEC daveConnection * DECL2 daveNewConnection(daveInterface * di, int MPI,int rack, int slot);
typedef struct {
uc type[2];
unsigned short count;
} daveBlockTypeEntry;
typedef struct {
unsigned short number;
uc type[2];
} daveBlockEntry;
/**
PDU handling:
PDU is the central structure present in S7 communication.
It is composed of a 10 or 12 byte header,a parameter block and a data block.
When reading or writing values, the data field is itself composed of a data
header followed by payload data
**/
/*
retrieve the answer
*/
EXPORTSPEC int DECL2 daveGetResponse(daveConnection * dc);
/*
send PDU to PLC
*/
EXPORTSPEC int DECL2 daveSendMessage(daveConnection * dc, PDU * p);
/******
Utilities:
****/
/*
Hex dump PDU:
*/
EXPORTSPEC void DECL2 _daveDumpPDU(PDU * p);
/*
Hex dump. Write the name followed by len bytes written in hex and a newline:
*/
EXPORTSPEC void DECL2 _daveDump(char * name,uc*b,int len);
/*
names for PLC objects:
*/
EXPORTSPEC char * DECL2 daveBlockName(uc bn);
EXPORTSPEC char * DECL2 daveAreaName(uc n);
/*
swap functions:
*/
EXPORTSPEC short DECL2 daveSwapIed_16(short ff);
EXPORTSPEC int DECL2 daveSwapIed_32(int ff);
/**
Data conversion convenience functions. The older set has been removed.
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:
*/
EXPORTSPEC float DECL2 daveGetFloatAt(daveConnection * dc, int pos);
EXPORTSPEC float DECL2 toPLCfloat(float ff);
EXPORTSPEC int DECL2 daveToPLCfloat(float ff);
EXPORTSPEC int DECL2 daveGetS8from(uc *b);
EXPORTSPEC int DECL2 daveGetU8from(uc *b);
EXPORTSPEC int DECL2 daveGetS16from(uc *b);
EXPORTSPEC int DECL2 daveGetU16from(uc *b);
EXPORTSPEC int DECL2 daveGetS32from(uc *b);
EXPORTSPEC unsigned int DECL2 daveGetU32from(uc *b);
EXPORTSPEC float DECL2 daveGetFloatfrom(uc *b);
/*
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.
*/
EXPORTSPEC int DECL2 daveGetS8(daveConnection * dc);
EXPORTSPEC int DECL2 daveGetU8(daveConnection * dc);
EXPORTSPEC int DECL2 daveGetS16(daveConnection * dc);
EXPORTSPEC int DECL2 daveGetU16(daveConnection * dc);
EXPORTSPEC int DECL2 daveGetS32(daveConnection * dc);
EXPORTSPEC unsigned int DECL2 daveGetU32(daveConnection * dc);
EXPORTSPEC float DECL2 daveGetFloat(daveConnection * dc);
/*
Get a value from a given position in the last result read on the connection dc.
*/
EXPORTSPEC int DECL2 daveGetS8At(daveConnection * dc, int pos);
EXPORTSPEC int DECL2 daveGetU8At(daveConnection * dc, int pos);
EXPORTSPEC int DECL2 daveGetS16At(daveConnection * dc, int pos);
EXPORTSPEC int DECL2 daveGetU16At(daveConnection * dc, int pos);
EXPORTSPEC int DECL2 daveGetS32At(daveConnection * dc, int pos);
EXPORTSPEC unsigned int DECL2 daveGetU32At(daveConnection * dc, int pos);
/*
put one byte into buffer b:
*/
EXPORTSPEC uc * DECL2 davePut8(uc *b,int v);
EXPORTSPEC uc * DECL2 davePut16(uc *b,int v);
EXPORTSPEC uc * DECL2 davePut32(uc *b,int v);
EXPORTSPEC uc * DECL2 davePutFloat(uc *b,float v);
EXPORTSPEC void DECL2 davePut8At(uc *b, int pos, int v);
EXPORTSPEC void DECL2 davePut16At(uc *b, int pos, int v);
EXPORTSPEC void DECL2 davePut32At(uc *b, int pos, int v);
EXPORTSPEC void DECL2 davePutFloatAt(uc *b,int pos, float v);
/**
Timer and Counter conversion functions:
**/
/*
get time in seconds from current read position:
*/
EXPORTSPEC float DECL2 daveGetSeconds(daveConnection * dc);
/*
get time in seconds from random position:
*/
EXPORTSPEC float DECL2 daveGetSecondsAt(daveConnection * dc, int pos);
/*
get counter value from current read position:
*/
EXPORTSPEC int DECL2 daveGetCounterValue(daveConnection * dc);
/*
get counter value from random read position:
*/
EXPORTSPEC int DECL2 daveGetCounterValueAt(daveConnection * dc,int pos);
/*
Functions to load blocks from PLC:
*/
EXPORTSPEC void DECL2 _daveConstructUpload(PDU *p,char blockType, int blockNr);
EXPORTSPEC void DECL2 _daveConstructDoUpload(PDU * p, int uploadID);
EXPORTSPEC void DECL2 _daveConstructEndUpload(PDU * p, int uploadID);
/*
Get the PLC's order code as ASCIIZ. Buf must provide space for
21 characters at least.
*/
#define daveOrderCodeSize 21
EXPORTSPEC int DECL2 daveGetOrderCode(daveConnection * dc,char * buf);
/*
connect to a PLC. returns 0 on success.
*/
EXPORTSPEC int DECL2 daveConnectPLC(daveConnection * dc);
/*
Read len bytes from the PLC. Start determines the first byte.
Area denotes whether the data comes from FLAGS, DATA BLOCKS,
INPUTS or OUTPUTS, etc.
DB is the number of the data block to be used. Set it to zero
for other area types.
Buffer is a pointer to a memory block provided by the calling
program. If the pointer is not NULL, the result data will be copied thereto.
Hence it must be big enough to take up the result.
In any case, you can also retrieve the result data using the get<type> macros
on the connection pointer.
RESTRICTION:There is no check for max. message len or automatic splitting into
multiple messages. Use daveReadManyBytes() in case the data you want
to read doesn't fit into a single PDU.
*/
EXPORTSPEC int DECL2 daveReadBytes(daveConnection * dc, int area, int DB, int start, int len, void * buffer);
/*
Read len bytes from the PLC. Start determines the first byte.
In contrast to daveReadBytes(), this function can read blocks
that are too long for a single transaction. To achieve this,
the data is fetched with multiple subsequent read requests to
the CPU.
Area denotes whether the data comes from FLAGS, DATA BLOCKS,
INPUTS or OUTPUTS, etc.
DB is the number of the data block to be used. Set it to zero
for other area types.
Buffer is a pointer to a memory block provided by the calling
program. It may not be NULL, the result data will be copied thereto.
Hence it must be big enough to take up the result.
You CANNOT read result bytes from the internal buffer of the
daveConnection. This buffer is overwritten in each read request.
*/
EXPORTSPEC int DECL2 daveReadManyBytes(daveConnection * dc,int area, int DBnum, int start,int len, void * buffer);
/*
Write len bytes from buffer to the PLC.
Start determines the first byte.
Area denotes whether the data goes to FLAGS, DATA BLOCKS,
INPUTS or OUTPUTS, etc.
DB is the number of the data block to be used. Set it to zero
for other area types.
RESTRICTION: There is no check for max. message len or automatic splitting into
multiple messages. Use daveReadManyBytes() in case the data you want
to read doesn't fit into a single PDU.
*/
EXPORTSPEC int DECL2 daveWriteBytes(daveConnection * dc,int area, int DB, int start, int len, void * buffer);
/*
Write len bytes to the PLC. Start determines the first byte.
In contrast to daveWriteBytes(), this function can write blocks
that are too long for a single transaction. To achieve this, the
the data is transported with multiple subsequent write requests to
the CPU.
Area denotes whether the data comes from FLAGS, DATA BLOCKS,
INPUTS or OUTPUTS, etc.
DB is the number of the data block to be used. Set it to zero
for other area types.
Buffer is a pointer to a memory block provided by the calling
program. It may not be NULL.
*/
EXPORTSPEC int DECL2 daveWriteManyBytes(daveConnection * dc,int area, int DB, int start, int len, void * buffer);
/*
Bit manipulation:
*/
EXPORTSPEC int DECL2 daveReadBits(daveConnection * dc, int area, int DB, int start, int len, void * buffer);
EXPORTSPEC int DECL2 daveWriteBits(daveConnection * dc,int area, int DB, int start, int len, void * buffer);
EXPORTSPEC int DECL2 daveSetBit(daveConnection * dc,int area, int DB, int byteAdr, int bitAdr);
EXPORTSPEC int DECL2 daveClrBit(daveConnection * dc,int area, int DB, int byteAdr, int bitAdr);
/*
PLC diagnostic and inventory functions:
*/
EXPORTSPEC int DECL2 daveReadSZL(daveConnection * dc, int ID, int index, void * buf, int buflen);
EXPORTSPEC int DECL2 daveListBlocksOfType(daveConnection * dc,uc type,daveBlockEntry * buf);
EXPORTSPEC int DECL2 daveListBlocks(daveConnection * dc,daveBlockTypeEntry * buf);
/*
PLC program read functions:
*/
EXPORTSPEC int DECL2 initUpload(daveConnection * dc,char blockType, int blockNr, int * uploadID);
EXPORTSPEC int DECL2 doUpload(daveConnection*dc, int * more, uc**buffer, int*len, int uploadID);
EXPORTSPEC int DECL2 endUpload(daveConnection*dc, int uploadID);
EXPORTSPEC int DECL2 daveGetProgramBlock(daveConnection * dc, int blockType, int number, char* buffer, int * length);
/*
PLC run/stop control functions:
*/
EXPORTSPEC int DECL2 daveStop(daveConnection*dc);
EXPORTSPEC int DECL2 daveStart(daveConnection*dc);
/*
PLC special commands
*/
EXPORTSPEC int DECL2 daveCopyRAMtoROM(daveConnection*dc);
EXPORTSPEC int DECL2 daveForce200(daveConnection * dc, int area, int start, int val);
/*
Multiple variable support:
*/
typedef struct {
int error;
int length;
uc * bytes;
} daveResult;
typedef struct {
int numResults;
daveResult * results;
} daveResultSet;
/* use this to initialize a multivariable read: */
EXPORTSPEC void DECL2 davePrepareReadRequest(daveConnection * dc, PDU *p);
/* Adds a new variable to a prepared request: */
EXPORTSPEC void DECL2 daveAddVarToReadRequest(PDU *p, int area, int DBnum, int start, int bytes);
/* Executes the complete request. */
EXPORTSPEC int DECL2 daveExecReadRequest(daveConnection * dc, PDU *p, daveResultSet * rl);
/* Lets the functions daveGet<data type> work on the n-th result: */
EXPORTSPEC int DECL2 daveUseResult(daveConnection * dc, daveResultSet * rl, int n);
/* Frees the memory occupied by the result structure */
EXPORTSPEC void DECL2 daveFreeResults(daveResultSet * rl);
/* Adds a new bit variable to a prepared request: */
EXPORTSPEC void DECL2 daveAddBitVarToReadRequest(PDU *p, int area, int DBnum, int start, int byteCount);
/* use this to initialize a multivariable write: */
EXPORTSPEC void DECL2 davePrepareWriteRequest(daveConnection * dc, PDU *p);
/* Adds a new variable to a prepared request: */
EXPORTSPEC void DECL2 daveAddVarToWriteRequest(PDU *p, int area, int DBnum, int start, int bytes, void * buffer);
/* Adds a new bit variable to a prepared write request: */
EXPORTSPEC void DECL2 daveAddBitVarToWriteRequest(PDU *p, int area, int DBnum, int start, int byteCount, void * buffer);
/* Executes the complete request. */
EXPORTSPEC int DECL2 daveExecWriteRequest(daveConnection * dc, PDU *p, daveResultSet * rl);
EXPORTSPEC int DECL2 daveInitAdapter(daveInterface * di);
EXPORTSPEC int DECL2 daveConnectPLC(daveConnection * dc);
EXPORTSPEC int DECL2 daveDisconnectPLC(daveConnection * dc);
EXPORTSPEC int DECL2 daveDisconnectAdapter(daveInterface * di);
EXPORTSPEC int DECL2 daveListReachablePartners(daveInterface * di,char * buf);
/* MPI specific functions */
#define daveMPIReachable 0x30
#define daveMPIActive 0x30
#define daveMPIPassive 0x00
#define daveMPIunused 0x10
#define davePartnerListSize 126
/**
C# interoperability:
**/
EXPORTSPEC void DECL2 daveSetTimeout(daveInterface * di, int tmo);
EXPORTSPEC int DECL2 daveGetTimeout(daveInterface * di);
EXPORTSPEC char * DECL2 daveGetName(daveInterface * di);
EXPORTSPEC int DECL2 daveGetMPIAdr(daveConnection * dc);
EXPORTSPEC int DECL2 daveGetAnswLen(daveConnection * dc);
EXPORTSPEC int DECL2 daveGetMaxPDULen(daveConnection * dc);
EXPORTSPEC daveResultSet * DECL2 daveNewResultSet();
EXPORTSPEC void DECL2 daveFree(void * dc);
EXPORTSPEC PDU * DECL2 daveNewPDU();
EXPORTSPEC int DECL2 daveGetErrorOfResult(daveResultSet *,int number);
/*
Special function do disconnect arbitrary connections on IBH-Link:
*/
EXPORTSPEC int DECL2 daveForceDisconnectIBH(daveInterface * di, int src, int dest, int mpi);
/*
Special function do reset an IBH-Link:
*/
EXPORTSPEC int DECL2 daveResetIBH(daveInterface * di);
/**
Program Block from PLC:
*/
EXPORTSPEC int DECL2 daveGetProgramBlock(daveConnection * dc, int blockType, int number, char* buffer, int * length);
/**
PLC realtime clock handling:
*/
/*
read out clock:
*/
EXPORTSPEC int DECL2 daveReadPLCTime(daveConnection * dc);
/*
set clock to a value given by user:
*/
EXPORTSPEC int DECL2 daveSetPLCTime(daveConnection * dc,uc * ts);
/*
set clock to PC system clock:
*/
EXPORTSPEC int DECL2 daveSetPLCTimeToSystime(daveConnection * dc);
EXPORTSPEC uc DECL2 daveToBCD(uc i);
EXPORTSPEC uc DECL2 daveFromBCD(uc i);
#endif /* _nodave */
#ifdef __cplusplus
}
#endif
/*
Changes:
04/10/05 first version.
09/11/05 added read/write functions for long blocks of data.
10/26/07 fixed __cplusplus
*/

70
openS7online.c Normal file
View File

@@ -0,0 +1,70 @@
#include "openS7online.h"
#include "log2.h"
#ifdef BCCWIN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#endif
/*
Access S7onlinx.dll and load pointers to the functions.
We load them using GetProcAddress on their names because:
1. We have no .lib file for them.
2. We don't want to link with a particular version.
3. Libnodave shall remain useable without Siemens .dlls. So it shall not try to access them
unless the user chooses the daveProtoS7online.
*/
extern int daveDebug;
typedef int (DECL2 * _setHWnd) (int, HWND);
EXPORTSPEC HANDLE DECL2 openS7online(const char * accessPoint, HWND handle) {
HMODULE hmod;
int h,en;
_setHWnd SetSinecHWnd;
hmod=LoadLibrary("S7onlinx.dll");
if (daveDebug & daveDebugOpen)
LOG2("LoadLibrary(S7onlinx.dll) returned %p)\n",hmod);
SCP_open=GetProcAddress(hmod,"SCP_open");
if (daveDebug & daveDebugOpen)
LOG2("GetProcAddress returned %p)\n",SCP_open);
SCP_close=GetProcAddress(hmod,"SCP_close");
if (daveDebug & daveDebugOpen)
LOG2("GetProcAddress returned %p)\n",SCP_close);
SCP_get_errno=GetProcAddress(hmod,"SCP_get_errno");
if (daveDebug & daveDebugOpen)
LOG2("GetProcAddress returned %p)\n",SCP_get_errno);
SCP_send=GetProcAddress(hmod,"SCP_send");
if (daveDebug & daveDebugOpen)
LOG2("GetProcAddress returned %p)\n",SCP_send);
SCP_receive=GetProcAddress(hmod,"SCP_receive");
if (daveDebug & daveDebugOpen)
LOG2("GetProcAddress returned %p)\n",SCP_receive);
SetSinecHWnd=GetProcAddress(hmod,"SetSinecHWnd");
if (daveDebug & daveDebugOpen)
LOG2("GetProcAddress returned %p)\n",SetSinecHWnd);
en=SCP_get_errno();
h=SCP_open(accessPoint);
en=SCP_get_errno();
LOG3("handle: %d error:%d\n", h, en);
SetSinecHWnd(h, handle);
return h;
};
EXPORTSPEC HANDLE DECL2 closeS7online(int h) {
SCP_close(h);
}
/*
01/09/07 Used Axel Kinting's version.
*/

Some files were not shown because too many files have changed in this diff Show More