|
|
 |
|
|
 |
The following example tinyslave.cpp shows how to implement a small Modbus RTU slave:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MbusRtuSlaveProtocol.hpp"
#if defined(__LINUX__)
char *portName = "/dev/ttyS0";
#elif defined(__WIN32__) || defined(__CYGWIN__)
char *portName = "COM1";
#elif defined(__FREEBSD__) || defined(__NETBSD__) || defined(__OPENBSD__)
char *portName = "/dev/ttyd0";
#elif defined(__QNX__)
char *portName = "/dev/ser1";
#elif defined(__VXWORKS__)
char *portName = "/tyCo/0";
#elif defined(__IRIX__)
char *portName = "/dev/ttyf1";
#elif defined(__SOLARIS__)
char *portName = "/dev/ttya";
#elif defined(__OSF__)
char *portName = "/dev/tty00";
#else
# error Unknown platform, please add an entry for portName
#endif
typedef struct
{
short actTemp;
short minTemp;
long scanCounter;
float setPoint;
short statusReg;
short configType;
} MyDeviceData;
MyDeviceData deviceData;
class MyDataProvider: public MbusDataTableInterface
{
public:
int readHoldingRegistersTable(int startRef, short regArr[], int refCnt)
{
startRef--;
if (startRef + refCnt > int(sizeof(deviceData) / sizeof(short)))
return (0);
memcpy(regArr, &((short *) &deviceData)[startRef],
refCnt * sizeof(short));
return (1);
}
int writeHoldingRegistersTable(int startRef,
const short regArr[],
int refCnt)
{
startRef--;
if (startRef + refCnt > int(sizeof(deviceData) / sizeof(short)))
return (0);
memcpy(&((short *) &deviceData)[startRef],
regArr, refCnt * sizeof(short));
return (1);
}
} dataProvider;
MbusRtuSlaveProtocol mbusServer;
void startupServer()
{
int result;
result = mbusServer.addDataTable(1, &dataProvider);
if (result == FTALK_SUCCESS)
result = mbusServer.startupServer(portName,
9600L,
8,
1,
0);
if (result != FTALK_SUCCESS)
{
fprintf(stderr, "Error starting server: %s!\n",
getBusProtocolErrorText(result));
exit(EXIT_FAILURE);
}
}
void shutdownServer()
{
mbusServer.shutdownServer();
}
void runServer()
{
int result = FTALK_SUCCESS;
while (result == FTALK_SUCCESS)
{
result = mbusServer.serverLoop();
if (result != FTALK_SUCCESS)
fprintf(stderr, "%s!\n", getBusProtocolErrorText(result));
}
}
int main()
{
atexit(shutdownServer);
startupServer();
runServer();
return (EXIT_FAILURE);
}
The following example shows how to implement a Data Provider which serves it's data from shared memory:
class ShmemMbusDataTable: public MbusDataTableInterface
{
public:
ShmemMbusDataTable(int table0Size, int table1Size, int table3Size, int table4Size)
{
int i;
int fd;
for (i = 0; i < 5; i++)
{
switch (i)
{
case 0:
if (table0Size <= 0)
break;
dataTableArr[i].size = table0Size * sizeof(char);
break;
case 1:
if (table1Size <= 0)
break;
dataTableArr[i].size = table1Size * sizeof(char);
break;
case 2:
dataTableArr[i].size = sizeof(SlaveStatusInfo);
break;
case 3:
if (table3Size <= 0)
break;
dataTableArr[i].size = table3Size * sizeof(short);
break;
case 4:
if (table4Size <= 0)
break;
dataTableArr[i].size = table4Size * sizeof(short);
break;
}
shm_unlink(dataTableArr[i].name);
if (dataTableArr[i].size == 0)
continue;
fd = shm_open(dataTableArr[i].name, O_CREAT | O_RDWR | O_EXCL, S_IRWXU);
if (fd < 0)
{
perror("Shared memory open failed");
abort();
}
if (ftruncate(fd, dataTableArr[i].size) < 0)
{
perror("Shared memory ftruncate failed");
abort();
}
dataTableArr[i].ptr = (short *) mmap(0, dataTableArr[i].size,
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0L);
if (dataTableArr[i].ptr == NULL)
{
perror("Shared memory mmap failed");
abort();
}
close (fd);
memset(dataTableArr[i].ptr, 0, dataTableArr[i].size);
}
if (table3Size == -1)
{
dataTableArr[3].size = dataTableArr[4].size;
dataTableArr[3].ptr = dataTableArr[4].ptr;
}
if (table1Size == -1)
{
dataTableArr[1].size = dataTableArr[0].size;
dataTableArr[1].ptr = dataTableArr[0].ptr;
}
slaveStatusInfoPtr = (SlaveStatusInfo *) dataTableArr[2].ptr;
}
~ShmemMbusDataTable()
{
int i;
for (i = 0; i < 5; i++)
{
if (dataTableArr[i].ptr != NULL)
{
munmap(dataTableArr[i].ptr, dataTableArr[i].size);
shm_unlink(dataTableArr[i].name);
}
}
}
void timeOutHandler()
{
slaveStatusInfoPtr->timeoutCnt++;
}
int readInputDiscretesTable(int startRef,
char bitArr[],
int refCnt)
{
printf("\nreadInputDiscretesTable: %d, %d\n", startRef, refCnt);
startRef--;
if (startRef + refCnt > (int) (dataTableArr[1].size / sizeof(char)))
return (0);
memcpy(bitArr, &((char *) dataTableArr[1].ptr)[startRef], refCnt * sizeof(char));
slaveStatusInfoPtr->readDiscretesCnt++;
return (1);
}
int readCoilsTable(int startRef,
char bitArr[],
int refCnt)
{
printf("\nreadCoilsTable: %d, %d\n", startRef, refCnt);
startRef--;
if (startRef + refCnt > (int) (dataTableArr[0].size / sizeof(char)))
return (0);
memcpy(bitArr, &((char *) dataTableArr[0].ptr)[startRef], refCnt * sizeof(char));
slaveStatusInfoPtr->readDiscretesCnt++;
return (1);
}
int writeCoilsTable(int startRef,
const char bitArr[],
int refCnt)
{
printf("\nriteCoilsTable: %d, %d\n", startRef, refCnt);
startRef--;
if (startRef + refCnt > (int) (dataTableArr[0].size / sizeof(char)))
return (0);
memcpy(&((char *) dataTableArr[0].ptr)[startRef], bitArr, refCnt * sizeof(char));
slaveStatusInfoPtr->writeDiscretesCnt++;
return (1);
}
int readInputRegistersTable(int startRef,
short regArr[],
int refCnt)
{
printf("\nreadInputRegistersTable: %d, %d\n", startRef, refCnt);
startRef--;
if (startRef + refCnt > (int) (dataTableArr[3].size / sizeof(short)))
return (0);
memcpy(regArr, &((short *) dataTableArr[3].ptr)[startRef], refCnt * sizeof(short));
slaveStatusInfoPtr->readRegistersCnt++;
return (1);
}
int readHoldingRegistersTable(int startRef,
short regArr[],
int refCnt)
{
printf("\nreadHoldingRegistersTable: %d, %d\n", startRef, refCnt);
startRef--;
if (startRef + refCnt > (int) (dataTableArr[4].size / sizeof(short)))
return (0);
memcpy(regArr, &((short *) dataTableArr[4].ptr)[startRef], refCnt * sizeof(short));
slaveStatusInfoPtr->readRegistersCnt++;
return (1);
}
int writeHoldingRegistersTable(int startRef,
const short regArr[],
int refCnt)
{
printf("\nwriteHoldingRegistersTable: %d, %d\n", startRef, refCnt);
startRef--;
if (startRef + refCnt > (int) (dataTableArr[4].size / sizeof(short)))
return (0);
memcpy(&((short *) dataTableArr[4].ptr)[startRef], regArr, refCnt * sizeof(short));
slaveStatusInfoPtr->writeRegistersCnt++;
return (1);
}
private:
SlaveStatusInfo *slaveStatusInfoPtr;
};
The following more complex example diagslave.cpp shows how to use the protocol stack in a context where the user can select the protocol type (TCP, RTU and ASCII) and other parameters. Diagslave is a slave simulator and test tool.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "MbusRtuSlaveProtocol.hpp"
#include "MbusAsciiSlaveProtocol.hpp"
#include "MbusTcpSlaveProtocol.hpp"
#include "DiagnosticDataTable.hpp"
#ifdef __WIN32__
# include "getopt.c"
#else
# include <unistd.h>
#endif
const char versionStr[]= "$Revision: 1.13 $";
const char progName[] = "diagslave";
const char bannerStr[] =
"%s - FieldTalk(tm) Modbus(R) Diagnostic Slave\n"
"Copyright (c) 2002-2005 FOCUS Software Engineering Pty Ltd\n"
#ifdef __WIN32__
"Getopt Library Copyright (C) 1987-1997 Free Software Foundation, Inc.\n"
#endif
;
const char usageStr[] =
"%s [options] [serialport]\n"
"Arguments: \n"
"serialport Serial port when using Modbus ASCII or Modbus RTU protocol \n"
" /dev/ttyS0, /dev/ttyS1 ... on GNU/Linux \n"
" COM1, COM2 ... on Win32 \n"
" /dev/ser1, /dev/ser2 ... on QNX \n"
"General options:\n"
"-m ascii Modbus ASCII protocol\n"
"-m rtu Modbus RTU protocol (default)\n"
"-m tcp MODBUS/TCP protocol\n"
"-t # Master poll time-out in ms (0-100000, 3000 is default)\n"
"-a # Slave address (1-255 for RTU/ASCII, 0-255 for TCP)\n"
"Options for MODBUS/TCP:\n"
"-p # TCP port number (502 is default)\n"
"Options for Modbus ASCII and Modbus RTU:\n"
"-b # Baudrate (e.g. 9600, 19200, ...) (9600 is default)\n"
"-d # Databits (7 or 8 for ASCII protocol, 8 for RTU)\n"
"-s # Stopbits (1 or 2, 1 is default)\n"
"-p none No parity (default)\n"
"-p even Even parity\n"
"-p odd Odd parity\n"
"";
enum
{
RTU,
ASCII,
TCP
};
int address = -1;
long timeout = 3000;
long baudRate = 9600;
int dataBits = 8;
int stopBits = 1;
int parity = MbusSerialSlaveProtocol::SER_PARITY_NONE;
int protocol = RTU;
char *portName = NULL;
int port = 502;
DiagnosticMbusDataTable *dataTablePtrArr[256];
MbusSlaveServer *mbusServerPtr = NULL;
void printUsage()
{
printf("Usage: ");
printf(usageStr, progName);
exit(EXIT_SUCCESS);
}
void printVersion()
{
printf(bannerStr, progName);
printf("Version: %s using FieldTalk package version %s\n",
versionStr, MbusSlaveServer::getPackageVersion());
}
void printConfig()
{
printf(bannerStr, progName);
printf("Protocol configuration: ");
switch (protocol)
{
case RTU:
printf("Modbus RTU\n");
break;
case ASCII:
printf("Modbus ASCII\n");
break;
case TCP:
printf("MODBUS/TCP\n");
break;
default:
printf("unknown\n");
break;
}
printf("Slave configuration: ");
printf("Address = %d, ", address);
printf("Master Time-out = %ld\n", timeout);
if (protocol == TCP)
{
printf("TCP configuration: ");
printf("Port = %d\n", port);
}
else
{
printf("Serial port configuration: ");
printf("%s, ", portName);
printf("%ld, ", baudRate);
printf("%d, ", dataBits);
printf("%d, ", stopBits);
switch (parity)
{
case MbusSerialSlaveProtocol::SER_PARITY_NONE:
printf("none\n");
break;
case MbusSerialSlaveProtocol::SER_PARITY_EVEN:
printf("even\n");
break;
case MbusSerialSlaveProtocol::SER_PARITY_ODD:
printf("odd\n");
break;
default:
printf("unknown\n");
break;
}
}
printf("\n");
}
void exitBadOption(const char *const text)
{
fprintf(stderr, "%s: %s! Try -h for help.\n", progName, text);
exit(EXIT_FAILURE);
}
void scanOptions(int argc, char **argv)
{
int c;
for (c = 1; c < argc; c++)
{
if (strcmp (argv[c], "--version") == 0)
{
printVersion();
exit(EXIT_SUCCESS);
}
}
for (c = 1; c < argc; c++)
{
if (strcmp (argv[c], "--help") == 0)
printUsage();
}
opterr = 0;
for(;;)
{
c = getopt(argc, argv, "ha:b:d:s:p:m:");
if (c == -1)
break;
switch (c)
{
case 'm':
if (strcmp(optarg, "tcp") == 0)
{
protocol = TCP;
}
else
if (strcmp(optarg, "rtu") == 0)
{
protocol = RTU;
}
else
if (strcmp(optarg, "ascii") == 0)
{
protocol = ASCII;
}
else
{
exitBadOption("Invalid protocol parameter");
}
break;
case 'a':
address = strtol(optarg, NULL, 0);
if ((address < -1) || (address > 255))
exitBadOption("Invalid address parameter");
break;
case 't':
timeout = strtol(optarg, NULL, 0);
if ((timeout < 0) || (timeout > 100000))
exitBadOption("Invalid time-out parameter");
break;
case 'b':
baudRate = strtol(optarg, NULL, 0);
if (baudRate == 0)
exitBadOption("Invalid baudrate parameter");
break;
case 'd':
dataBits = (int) strtol(optarg, NULL, 0);
if ((dataBits != 7) || (dataBits != 8))
exitBadOption("Invalid databits parameter");
break;
case 's':
stopBits = (int) strtol(optarg, NULL, 0);
if ((stopBits != 1) || (stopBits != 2))
exitBadOption("Invalid stopbits parameter");
break;
case 'p':
if (strcmp(optarg, "none") == 0)
{
parity = MbusSerialSlaveProtocol::SER_PARITY_NONE;
}
else
if (strcmp(optarg, "odd") == 0)
{
parity = MbusSerialSlaveProtocol::SER_PARITY_ODD;
}
else
if (strcmp(optarg, "even") == 0)
{
parity = MbusSerialSlaveProtocol::SER_PARITY_EVEN;
}
else
{
port = strtol(optarg, NULL, 0);
if ((port <= 0) || (port > 0xFFFF))
exitBadOption("Invalid parity or port parameter");
}
break;
case 'h':
printUsage();
break;
default:
exitBadOption("Unrecognized option or missing option parameter");
break;
}
}
if (protocol == TCP)
{
if ((argc - optind) != 0)
exitBadOption("Invalid number of parameters");
}
else
{
if ((argc - optind) != 1)
exitBadOption("Invalid number of parameters");
else
portName = argv[optind];
}
}
int validateMasterIpAddr(char* masterIpAddrSz)
{
printf("\nvalidateMasterIpAddr: accepting connection from %s\n",
masterIpAddrSz);
return (1);
}
void startupServer()
{
int i;
int result = -1;
switch (protocol)
{
case RTU:
mbusServerPtr = new MbusRtuSlaveProtocol();
if (address == -1)
{
for (i = 1; i < 255; i++)
mbusServerPtr->addDataTable(i, dataTablePtrArr[i]);
}
else
mbusServerPtr->addDataTable(address, dataTablePtrArr[address]);
mbusServerPtr->setTimeout(timeout);
result = ((MbusRtuSlaveProtocol *) mbusServerPtr)->startupServer(
portName, baudRate, dataBits, stopBits, parity);
break;
case ASCII:
mbusServerPtr = new MbusAsciiSlaveProtocol();
if (address == -1)
{
for (i = 1; i < 255; i++)
mbusServerPtr->addDataTable(i, dataTablePtrArr[i]);
}
else
mbusServerPtr->addDataTable(address, dataTablePtrArr[address]);
mbusServerPtr->setTimeout(timeout);
result = ((MbusAsciiSlaveProtocol *) mbusServerPtr)->startupServer(
portName, baudRate, dataBits, stopBits, parity);
break;
case TCP:
mbusServerPtr = new MbusTcpSlaveProtocol();
if (address == -1)
{
for (i = 0; i < 255; i++)
mbusServerPtr->addDataTable(i, dataTablePtrArr[i]);
}
else
mbusServerPtr->addDataTable(address, dataTablePtrArr[address]);
mbusServerPtr->setTimeout(timeout);
((MbusTcpSlaveProtocol *) mbusServerPtr)->installIpAddrValidationCallBack(validateMasterIpAddr);
((MbusTcpSlaveProtocol *) mbusServerPtr)->setPort(port);
result = ((MbusTcpSlaveProtocol *) mbusServerPtr)->startupServer();
break;
}
switch (result)
{
case FTALK_SUCCESS:
printf("Server started up successfully.\n");
break;
case FTALK_ILLEGAL_ARGUMENT_ERROR:
fprintf(stderr, "Configuration setting not supported!\n");
exit(EXIT_FAILURE);
break;
default:
fprintf(stderr, "%s!\n", getBusProtocolErrorText(result));
exit(EXIT_FAILURE);
break;
}
}
void shutdownServer()
{
printf("Shutting down server.\n");
delete mbusServerPtr;
}
void runServer()
{
int result = FTALK_SUCCESS;
printf("Listening to network (Ctrl-C to stop)\n");
while (result == FTALK_SUCCESS)
{
result = mbusServerPtr->serverLoop();
if (result != FTALK_SUCCESS)
fprintf(stderr, "%s!\n", getBusProtocolErrorText(result));\
else
{
printf(".");
fflush(stdout);
}
}
}
int main(int argc, char **argv)
{
int i;
for (i = 0; i < 255; i++)
{
dataTablePtrArr[i] = new DiagnosticMbusDataTable(i);
}
scanOptions(argc, argv);
printConfig();
atexit(shutdownServer);
startupServer();
runServer();
return (EXIT_FAILURE);
}
Diagslave uses the following diagnostic data table as Data Provider:
#ifndef _DIAGNOSTICDATATABLE_H_INCLUDED
#define _DIAGNOSTICDATATABLE_H_INCLUDED
#include <stdio.h>
#include <string.h>
#include "MbusDataTableInterface.hpp"
class DiagnosticMbusDataTable: public MbusDataTableInterface
{
public:
DiagnosticMbusDataTable(int slaveAddr)
{
this->slaveAddr = slaveAddr;
memset(regData, 0, sizeof(regData));
memset(bitData, 0, sizeof(bitData));
}
~DiagnosticMbusDataTable()
{
}
char readExceptionStatus()
{
printf("\rSlave %3d: readExceptionStatus\n", slaveAddr);
return (0x55);
}
int readInputDiscretesTable(int startRef,
char bitArr[],
int refCnt)
{
printf("\rSlave %3d: readInputDiscretes from %d, %d references\n",
slaveAddr, startRef, refCnt);
startRef--;
if (startRef + refCnt > int(sizeof(bitData) / sizeof(char)))
return (0);
memcpy(bitArr, &bitData[startRef], refCnt * sizeof(char));
return (1);
}
int readCoilsTable(int startRef,
char bitArr[],
int refCnt)
{
printf("\rSlave %3d: readCoils from %d, %d references\n",
slaveAddr, startRef, refCnt);
startRef--;
if (startRef + refCnt > int(sizeof(bitData) / sizeof(char)))
return (0);
memcpy(bitArr, &bitData[startRef], refCnt * sizeof(char));
return (1);
}
int writeCoilsTable(int startRef,
const char bitArr[],
int refCnt)
{
printf("\rSlave %3d: writeCoils from %d, %d references\n",
slaveAddr, startRef, refCnt);
startRef--;
if (startRef + refCnt > int(sizeof(bitData) / sizeof(char)))
return (0);
memcpy(&bitData[startRef], bitArr, refCnt * sizeof(char));
return (1);
}
int readInputRegistersTable(int startRef,
short regArr[],
int refCnt)
{
printf("\rSlave %3d: readInputRegisters from %d, %d references\n",
slaveAddr, startRef, refCnt);
startRef--;
if (startRef + refCnt > int(sizeof(regData) / sizeof(short)))
return (0);
memcpy(regArr, ®Data[startRef], refCnt * sizeof(short));
return (1);
}
int readHoldingRegistersTable(int startRef,
short regArr[],
int refCnt)
{
printf("\rSlave %3d: readHoldingRegisters from %d, %d references\n",
slaveAddr, startRef, refCnt);
startRef--;
if (startRef + refCnt > int(sizeof(regData) / sizeof(short)))
return (0);
memcpy(regArr, ®Data[startRef], refCnt * sizeof(short));
return (1);
}
int writeHoldingRegistersTable(int startRef,
const short regArr[],
int refCnt)
{
printf("\rSlave %3d: writeHoldingRegisters from %d, %d references\n",
slaveAddr, startRef, refCnt);
startRef--;
if (startRef + refCnt > int(sizeof(regData) / sizeof(short)))
return (0);
memcpy(®Data[startRef], regArr, refCnt * sizeof(short));
return (1);
}
int validateMasterIpAddr(char* masterIpAddrSz)
{
printf("\nvalidateMasterIpAddr: accepting connection from %s\n",
masterIpAddrSz);
return (1);
}
private:
int slaveAddr;
short regData[0x10000];
char bitData[2000];
};
#endif // ifdef ..._H_INCLUDED
|
 |
|