// // ModBusKit.mm // ModBusKit // // Created by Matthew Butch on 03/02/08. // Copyright 2008 Volitans Software and R Engineering, Inc. /* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. These library of functions are designed to enable a program send and receive data from a device that communicates using the Modbus protocol. */ // // This is an Objective-C interface to libmodbus #import "ModBusKit.h" @implementation ModBusKit - (void)initialize { NSDictionary *infoDict = [[NSBundle bundleForClass: [self class]] infoDictionary]; compatibilityVersionNumber=[[infoDict objectForKey: @"VSCompatibilityVersion"] intValue]; modbuskitVersionString=[NSString stringWithFormat:@"%@ (%@).", [infoDict objectForKey:@"CFBundleShortVersionString"], [infoDict objectForKey:@"CFBundleVersion"]]; //libmodbusVersionString=[[NSBundle mainBundle] objectForInfoDictionaryKey:@"VSlibmodbusVersion"]; libmodbusVersionString=[infoDict objectForKey:@"VSlibmodbusVersion"]; outputDebugMessages=FALSE; } - (id)init { if ((self = [super init]) != nil) { [self initialize]; lmbWrap= new libmodbusWrapper(); /*if (someError) { [self release]; self = nil; }*/ } return self; } - (id) initWithSerialPort: (NSString *)serialLocation baudRate: (int) baudRate parity: (int) parity dataBits: (int) dataBits stopBits: (int) stopBits { [self init]; if ([self setupSerialPort:serialLocation baudRate: baudRate parity: parity dataBits: dataBits stopBits: stopBits]) return (self); return(NULL); } - (id)initWithTCP: (NSString *)ipAddress port :(int)port { [self init]; if ([self setupTCP: ipAddress port: port]) return(self); return (NULL); } - (int)compatibilityVersion { return(compatibilityVersionNumber); } - (NSString *)modbuskitVersion { return(modbuskitVersionString); } - (NSString *)libmodbusVersion { return(libmodbusVersionString); } - (BOOL) setupSerialPort: (NSString *)serialLocation baudRate: (int) baudRate parity: (int) parity dataBits: (int) dataBits stopBits: (int) stopBits; { if (address!=NULL) [address release]; address=[[NSString alloc] initWithString: serialLocation]; return(lmbWrap->lmbInitSerial([serialLocation cStringUsingEncoding: NSASCIIStringEncoding], baudRate, parity, dataBits, stopBits)); } - (BOOL)setupTCP: (NSString *)ipAddress port: (int)port { if (address!=NULL) [address release]; address=[[NSString alloc] initWithString: ipAddress]; return(lmbWrap->lmbInitTCP([ipAddress cStringUsingEncoding: NSASCIIStringEncoding], port)); } - (int)openConnection { if (outputDebugMessages) NSLog(@"Opening connection..."); return(lmbWrap->lmbConnect()); } - (void)closeConnection { if (outputDebugMessages) NSLog(@"Closing connection..."); lmbWrap->lmbDisconnect(); } - (NSArray *) readDiscreteInput: (int) slaveID startAddress: (int) startAddress inputCount: (int) inputCount; { uint8_t *data; int i; NSMutableArray *dataArray; // NSArray *returnArray; if (outputDebugMessages) NSLog(@"Reading Discrete Input registers..."); data=(uint8_t *) malloc(inputCount * sizeof(uint8_t)); dataArray=[[NSMutableArray alloc] initWithCapacity: inputCount]; if (lmbWrap->lmbReadDiscreteInput(slaveID, startAddress, inputCount, data)) { for(i=0;ilmbReadCoil(slaveID, startAddress, coilCount, data)) { for(i=0;ilmbWriteSingleCoil(slaveID, coilAddress, state)); } - (BOOL) writeMultipleCoils: (int) slaveID startAddress: (int) startAddress coilCount: (int) coilCount stateData: (NSArray *) stateData; { uint8_t *data; int i; data=(uint8_t *)malloc(coilCount * sizeof(uint8_t)); for (i=0;i<[stateData count];i++) { data[i]= [[stateData objectAtIndex:i] intValue]; } return(lmbWrap->lmbWriteMultipleCoils(slaveID, startAddress, coilCount, data)); } - (NSArray *) readInputRegisters: (int) slaveID startAddress: (int) startAddress registerCount: (int) registerCount; { uint16_t *data; int i; NSMutableArray *dataArray; // NSArray *returnArray; if (outputDebugMessages) NSLog(@"Reading Input registers..."); data=(uint16_t *) malloc(registerCount * sizeof(uint16_t)); dataArray=[[NSMutableArray alloc] initWithCapacity: registerCount]; if (lmbWrap->lmbReadInputRegisters(slaveID, startAddress, registerCount, data)) { for(i=0;ilmbReadHoldingRegisters(slaveID, startAddress, registerCount, data)) { for(i=0;ilmbWriteSingleHoldingRegister(slaveID, registerAddress, value)); } - (BOOL) writeMultipleHoldingRegisters: (int) slaveID startAddress: (int) startAddress registerCount: (int) registerCount valueData: (NSArray *) valueData; { uint16_t *data; int i; data=(uint16_t*)malloc([valueData count] * sizeof(uint16_t)); for (i=0;i<[valueData count];i++) { data[i]= [[valueData objectAtIndex:i] intValue]; } return(lmbWrap->lmbWriteMultipleHoldingRegisters(slaveID, startAddress, registerCount, data)); } - (int) getLastError { return(lmbWrap->lmbErrorCode()); } - (NSString *) getLastErrorString { switch(lmbWrap->lmbErrorCode()) { case ILLEGAL_FUNCTION: return(@"Illegal Function"); case ILLEGAL_DATA_ADDRESS: return(@"Illegal Data Address"); case ILLEGAL_DATA_VALUE: return(@"Illegal Data Value"); case SLAVE_DEVICE_FAILURE: return(@"Slave Device Failure"); case ACKNOWLEDGE: return(@"Did not receive Acknowledge"); case SLAVE_DEVICE_BUSY: return(@"Slave Device Busy"); case NEGATIVE_ACKNOWLEDGE: return(@"Did not receive Acknowledge"); case MEMORY_PARITY_ERROR: return(@"Memory Parity Error"); case GATEWAY_PROBLEM_PATH: return(@"Problem with Gateway Path"); case GATEWAY_PROBLEM_TARGET: return(@"Problem with Gateway Target"); case COMM_TIME_OUT: return(@"Communication Time Out"); case PORT_SOCKET_FAILURE: return(@"Socket Failure"); case SELECT_FAILURE: return(@"Select Failure"); case TOO_MANY_DATA: return(@"Too Much Data"); case INVALID_CRC: return(@"Invalid CRC"); case INVALID_EXCEPTION_CODE: return(@"Unknown Exception"); } return(@"Unknown Error"); } - (NSString *) getAddress { return([NSString stringWithString:address]); } - (void) setDebug: (BOOL)debugToggle { lmbWrap->lmbSetDebug(debugToggle); outputDebugMessages=debugToggle; } - (BOOL) getDebug { return(outputDebugMessages); } - (void) setSaveRawData: (BOOL)toggle { lmbWrap->lmbSetRawDataSave(toggle); } - (NSArray *) getLastRawQuery { uint8_t *data; int i; NSMutableArray *dataArray; // NSArray *returnArray; if (outputDebugMessages) NSLog(@"Getting last raw query..."); data=(uint8_t *) malloc(MIN_QUERY_LENGTH * sizeof(uint8_t)); dataArray=[[NSMutableArray alloc] initWithCapacity: lmbWrap->lmbLastRawQueryLength()]; if (lmbWrap->lmbLastRawQuery(data)) { for(i=0;ilmbLastRawQueryLength();i++) { [dataArray addObject:[NSNumber numberWithInt: data[i]]]; } free(data); // returnArray=[NSArray arrayWithArray:dataArray]; // [dataArray autorelease]; return (dataArray); } return(NULL); } - (NSArray *) getLastRawResponse{ uint8_t *data; int i; NSMutableArray *dataArray; // NSArray *returnArray; if (outputDebugMessages) NSLog(@"Getting last raw response..."); data=(uint8_t *) malloc(MAX_MESSAGE_LENGTH * sizeof(uint8_t)); if (lmbWrap->lmbLastRawResponse(data)) { dataArray=[[NSMutableArray alloc] initWithCapacity: lmbWrap->lmbLastRawResponseLength()]; for(i=0;ilmbLastRawResponseLength();i++) { [dataArray addObject:[NSNumber numberWithInt: data[i]]]; } free(data); // returnArray=[NSArray arrayWithArray:dataArray]; // [dataArray autorelease]; return (dataArray); } return(NULL); } + (NSArray *)scanSerialPorts { kern_return_t kernResult; CFMutableDictionaryRef classesToMatch; io_iterator_t serialPortIterator; NSString* serialPort; NSMutableArray *serialPorts; serialPorts=[[NSMutableArray alloc] initWithCapacity:5]; // Serial devices are instances of class IOSerialBSDClient classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); if (classesToMatch != NULL) { CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes)); // This function decrements the refcount of the dictionary passed it kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, &serialPortIterator); if (kernResult == KERN_SUCCESS) { while ((serialPort = [self getNextSerialPort:serialPortIterator]) != nil) { [serialPorts addObject:serialPort]; } (void)IOObjectRelease(serialPortIterator); } } [serialPorts autorelease]; return(serialPorts); } + (NSString *)getNextSerialPort:(io_iterator_t)serialPortIterator { NSString *serialPort = nil; io_object_t serialService = IOIteratorNext(serialPortIterator); if (serialService != 0) { CFStringRef bsdPath = (CFStringRef)IORegistryEntryCreateCFProperty(serialService, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0); if (bsdPath) { // If the port already exists in the list of ports, we want that one. We only create a new one as a last resort. // if ([serialPorts containsObject:(NSString*)bsdPath]) serialPort = (NSString *)bsdPath; } // We have sucked this service dry of information so release it now. (void)IOObjectRelease(serialService); } return serialPort; } @end