FieldTalk Modbus® Slave Library C++ Editions |
![]() |
// Platform header #include <stdio.h> #include <stdlib.h> #include <string.h> // Include FieldTalk package header #include "MbusRtuSlaveProtocol.hpp" /***************************************************************************** * Gobal data *****************************************************************************/ #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 /***************************************************************************** * Modbus data table *****************************************************************************/ typedef struct { short actTemp; // Register 1 short minTemp; // Register 2 long scanCounter; // Register 3 and 4 float setPoint; // Register 5 and 6 short statusReg; // Register 7 short configType; // Register 8 } MyDeviceData; MyDeviceData deviceData; /***************************************************************************** * Data provider *****************************************************************************/ class MyDataProvider: public MbusDataTableInterface { public: int readHoldingRegistersTable(int startRef, short regArr[], int refCnt) { // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > int(sizeof(deviceData) / sizeof(short))) return (0); // // Copy data // memcpy(regArr, &((short *) &deviceData)[startRef], refCnt * sizeof(short)); return (1); } int writeHoldingRegistersTable(int startRef, const short regArr[], int refCnt) { // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > int(sizeof(deviceData) / sizeof(short))) return (0); // // Copy data // memcpy(&((short *) &deviceData)[startRef], regArr, refCnt * sizeof(short)); return (1); } } dataProvider; /***************************************************************************** * Modbus protocol declaration *****************************************************************************/ MbusRtuSlaveProtocol mbusServer; /***************************************************************************** * Function implementation *****************************************************************************/ void startupServer() { int result; result = mbusServer.addDataTable(1, &dataProvider); if (result == FTALK_SUCCESS) result = mbusServer.startupServer(portName, 9600L, // Baudrate 8, // Databits 1, // Stopbits 0); // Parity 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); }
class ShmemMbusDataTable: public MbusDataTableInterface { public: ShmemMbusDataTable(int table0Size, int table1Size, int table3Size, int table4Size) { int i; int fd; for (i = 0; i < 5; i++) { // // Determine table sizes // switch (i) { case 0: if (table0Size <= 0) break; // No table will be created for this type dataTableArr[i].size = table0Size * sizeof(char); break; case 1: if (table1Size <= 0) break; // No table will be created for this type dataTableArr[i].size = table1Size * sizeof(char); break; case 2: dataTableArr[i].size = sizeof(SlaveStatusInfo); break; case 3: if (table3Size <= 0) break; // No table will be created for this type dataTableArr[i].size = table3Size * sizeof(short); break; case 4: if (table4Size <= 0) break; // No table will be created for this type dataTableArr[i].size = table4Size * sizeof(short); break; } // // Open shared memory tables // 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(); } // // Size it // if (ftruncate(fd, dataTableArr[i].size) < 0) { perror("Shared memory ftruncate failed"); abort(); } // // Map shared memory into address space // 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); // No we can close the file descriptor memset(dataTableArr[i].ptr, 0, dataTableArr[i].size); } // // Handle special cases where we map table 3 to point to table 4 and // table 1 to point to table 0 // if (table3Size == -1) // Map table 3 to table 4 { dataTableArr[3].size = dataTableArr[4].size; dataTableArr[3].ptr = dataTableArr[4].ptr; } if (table1Size == -1) // Map table 1 to table 0 { dataTableArr[1].size = dataTableArr[0].size; dataTableArr[1].ptr = dataTableArr[0].ptr; } // Setup pointer to status table 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); // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > (int) (dataTableArr[1].size / sizeof(char))) return (0); // // Copy data // 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); // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > (int) (dataTableArr[0].size / sizeof(char))) return (0); // // Copy data // 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); // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > (int) (dataTableArr[0].size / sizeof(char))) return (0); // // Copy data // 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); // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > (int) (dataTableArr[3].size / sizeof(short))) return (0); // // Copy data // 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); // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > (int) (dataTableArr[4].size / sizeof(short))) return (0); // // Copy data // 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); // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > (int) (dataTableArr[4].size / sizeof(short))) return (0); // // Copy data // memcpy(&((short *) dataTableArr[4].ptr)[startRef], regArr, refCnt * sizeof(short)); slaveStatusInfoPtr->writeRegistersCnt++; return (1); } private: SlaveStatusInfo *slaveStatusInfoPtr; };
// Platform header #include <stdio.h> #include <string.h> #include <stdlib.h> // Include FieldTalk package header #include "MbusRtuSlaveProtocol.hpp" #include "MbusAsciiSlaveProtocol.hpp" #include "MbusTcpSlaveProtocol.hpp" #include "DiagnosticDataTable.hpp" #ifdef _WIN32 # include "getopt.h" #else # include <unistd.h> #endif /***************************************************************************** * String constants *****************************************************************************/ const char versionStr[]= "$Revision: 1.14 $"; const char progName[] = "diagslave"; const char bannerStr[] = "\n" "%s - FieldTalk(tm) Modbus(R) Diagnostic Slave\n" "Copyright (c) 2002-2006 FOCUS Software Engineering Pty Ltd\n" "Visit http://www.modbusdriver.com for Modbus libraries and tools.\n" "\n"; const char usageStr[] = "%s [options] [serialport]\n" "Arguments: \n" "serialport Serial port when using Modbus ASCII or Modbus RTU protocol \n" " COM1, COM2 ... on Windows \n" " /dev/ttyS0, /dev/ttyS1 ... on Linux \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\n" "-p even Even parity (default)\n" "-p odd Odd parity\n" ""; /***************************************************************************** * Enums *****************************************************************************/ enum { RTU, ASCII, TCP }; /***************************************************************************** * Gobal configuration data *****************************************************************************/ int address = -1; long timeout = 3000; long baudRate = 9600; int dataBits = 8; int stopBits = 1; int parity = MbusSerialSlaveProtocol::SER_PARITY_EVEN; int protocol = RTU; char *portName = NULL; int port = 502; /***************************************************************************** * Protocol and data table *****************************************************************************/ DiagnosticMbusDataTable *dataTablePtrArr[256]; MbusSlaveServer *mbusServerPtr = NULL; /***************************************************************************** * Function implementation *****************************************************************************/ 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; // Check for --version option for (c = 1; c < argc; c++) { if (strcmp (argv[c], "--version") == 0) { printVersion(); exit(EXIT_SUCCESS); } } // Check for --help option for (c = 1; c < argc; c++) { if (strcmp (argv[c], "--help") == 0) printUsage(); } opterr = 0; // Disable getopt's error messages 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++) // Note: TCP support slave addres of 0 mbusServerPtr->addDataTable(i, dataTablePtrArr[i]); } else mbusServerPtr->addDataTable(address, dataTablePtrArr[address]); mbusServerPtr->setTimeout(timeout); ((MbusTcpSlaveProtocol *) mbusServerPtr)->installIpAddrValidationCallBack(validateMasterIpAddr); ((MbusTcpSlaveProtocol *) mbusServerPtr)->setPort((unsigned short) 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; // Construct data tables 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 // Platform header #include <stdio.h> #include <string.h> // Package header #include "MbusDataTableInterface.hpp" /***************************************************************************** * DiagnosticMbusDataTable class declaration *****************************************************************************/ 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); // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > int(sizeof(bitData) / sizeof(char))) return (0); // // Copy data // 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); // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > int(sizeof(bitData) / sizeof(char))) return (0); // // Copy data // 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); // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > int(sizeof(bitData) / sizeof(char))) return (0); // // Copy data // 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); // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > int(sizeof(regData) / sizeof(short))) return (0); // // Copy data // 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); // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > int(sizeof(regData) / sizeof(short))) return (0); // // Copy data // 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); // Adjust Modbus reference counting startRef--; // // Validate range // if (startRef + refCnt > int(sizeof(regData) / sizeof(short))) return (0); // // Copy data // 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
Copyright © 2002-2006
FOCUS Software Engineering Pty Ltd, Australia.
All rights reserved.
Please see the Notices page for trademark notices. Last updated: 20 Oct 2006 |