def main(): if len(sys.argv) != 4: print( """Usage: ./orno_modbus.py serial_port device_address target_device_address Example: ./orno_modbus.py /dev/ttyUSB0 1 11 If you have only one device you can set the device_address to 0 to change its address. """) sys.exit(0) port = sys.argv[1] address = int(sys.argv[2]) target_address = int(sys.argv[3]) client = ModbusClient(method="rtu", port=port, baudrate=9600) client.connect() request = SendOrnoPassword('00000000', unit=address) client.execute(request) response = client.write_registers(15, [target_address], unit=address) if response: if address: print "Success. Changed address from %d to %d." % (address, target_address) else: print "Success. Changed address to %d." % (target_address) else: print "Address change failed" client.close()
class EPsolarTracerClient: ''' EPsolar Tracer client ''' def __init__(self, unit = 1, serialclient = None, **kwargs): ''' Initialize a serial client instance ''' self.unit = unit if serialclient == None: port = kwargs.get('port', '/dev/ttyXRUSB0') baudrate = kwargs.get('baudrate', 115200) self.client = ModbusClient(method = 'rtu', port = port, baudrate = baudrate, kwargs = kwargs) else: self.client = serialclient def connect(self): ''' Connect to the serial :returns: True if connection succeeded, False otherwise ''' return self.client.connect() def close(self): ''' Closes the underlying connection ''' return self.client.close() def read_device_info(self): request = ReadDeviceInformationRequest (unit = self.unit) response = self.client.execute(request) return response def read_input(self, name): register = registerByName(name) if register.is_coil(): response = self.client.read_coils(register.address, register.size, unit = self.unit) elif register.is_discrete_input(): response = self.client.read_discrete_inputs(register.address, register.size, unit = self.unit) elif register.is_input_register(): response = self.client.read_input_registers(register.address, register.size, unit = self.unit) else: response = self.client.read_holding_registers(register.address, register.size, unit = self.unit) return register.decode(response) def write_output(self, name, value): register = registerByName(name) values = register.encode(value) response = False if register.is_coil(): self.client.write_coil(register.address, values, unit = self.unit) response = True elif register.is_discrete_input(): _logger.error("Cannot write discrete input " + repr(name)) pass elif register.is_input_register(): _logger.error("Cannot write input register " + repr(name)) pass else: self.client.write_registers(register.address, values, unit = self.unit) response = True return response
class ModbusSlaveReader: """ This class uses the pymodbus library to communicate with modbus slaves via serial connection. """ def __init__(self, serial_socket): self.logger = logging.getLogger(__name__) self.client = ModbusClient(method='rtu', port=serial_socket, timeout=1) modbus_client_filter = ModbusClientFilter() logging.getLogger("pymodbus.client.sync").addFilter( modbus_client_filter) def read_register_value(self, slave, register): value = self.client.read_holding_registers(register, 1, unit=slave) self.logger.debug('value read from modbus slave: ' + value) return value def check_connection(self): """Before a timer is made in the ReadSensorScheduler to read the data from the modbus slaves, the connection with the modbus client is checked. """ try: self.client.connect() except: self.logger.warning( 'Unable to connect to modbus network, check serial port') raise return False try: rq = ReportSlaveIdRequest() rr = self.client.execute(rq) assert(rr is None) self.logger.warning('Able to see modbus master on network') return True except: self.logger.warning('Unable to see modbus master on network') return False
# # request = ClearCountersRequest() # response = client.execute(request) # if isinstance(response, ClearCountersResponse): # ... do something with the response # # # What follows is a listing of all the supported methods. Feel free to # comment, uncomment, or modify each result set to match with your reference. #---------------------------------------------------------------------------# #---------------------------------------------------------------------------# # information requests #---------------------------------------------------------------------------# rq = ReadDeviceInformationRequest(unit=1) rr = client.execute(rq) #assert(rr == None) # not supported by reference assert (rr.function_code < 0x80) # test that we are not an error assert (rr.information[0] == 'Pymodbus') # test the vendor name assert (rr.information[1] == 'PM') # test the product code assert (rr.information[2] == '1.0') # test the code revision rq = ReportSlaveIdRequest(unit=1) rr = client.execute(rq) # assert(rr == None) # not supported by reference #assert(rr.function_code < 0x80) # test that we are not an error #assert(rr.identifier == 0x00) # test the slave identifier #assert(rr.status == 0x00) # test that the status is ok rq = ReadExceptionStatusRequest(unit=1) rr = client.execute(rq)
if (connection == 1): if (DEBUG): print "connection:", connection else: print "connection: trying insmod" cmd = "/sbin/insmod /home/solar/xr_usb_serial_common-1a/xr_usb_serial_common.ko" returned_value = os.system(cmd) # returns the exit code in unix print('returned value:', returned_value) if (returned_value == 0): #all ok print('insmod installed USB driver ok') else: print('insmod installing USB driver failed. XXXX') sys.exit() request = ReadDeviceInformationRequest(unit=1) response = client.execute(request) # print "Response:" print repr(response.information), "\n" #################################### result = client.read_input_registers(0x3100, 8, unit=1) #print "Result 0x3100:", result if (result.registers): pvVoltage = float(result.registers[0] / 100.0) pvCurrent = float(result.registers[1] / 100.0) pvPowerL = float(result.registers[2] / 100.0) pvPowerH = float(result.registers[3]) batteryChargeV = float(result.registers[4] / 100.0) batteryChargeC = float(result.registers[5] / 100.0)
class SenseAirDevice(object): def __init__(self): #---------------------------------------------------------------------------# # This will simply send everything logged to console #---------------------------------------------------------------------------# logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) self.client = None def connect(self, deviceName): self.client = ModbusSerialClient(method='rtu',port=deviceName,stopbits=1, bytesize=8, baudrate=9600, timeout=0.2) if not self.client.connect(): rospy.logerr("Unable to connect to %s", device) return False return True def close(self): self.client.close() # Not working right now def getDeviceIdentification(self, objectId): rq = ReadDeviceInformationRequest(read_code=4, object_id=objectId, unit=0xFE) #rospy.loginfo("encoded: %h", encoded[0]) rr = self.client.execute(rq) print rr return "" if rr is None: rospy.logerr("No response from device") return None if rr.function_code < 0x80: # test that we are not an error return rr.information[0] #vendor_name = rr.information[0] #product_code = rr.information[1] #code_revision = rr.information[2] #rospy.loginfo("vendor: %s", vendor_name) #rospy.loginfo("product code: %s", product_code) #rospy.loginfo("revision: %s", code_revision) else: rospy.logwarn("error reading device identification: %h", rr.function_code) def getVendor(self): vendor = self.getDeviceIdentification(0) print vendor def readCO2(self): response = self.client.read_input_registers(address=3, count=1, unit=0xFE ) return response.getRegister(0) def readTemperature(self): response = self.client.read_input_registers(address=4, count=1, unit=0xFE ) return response.getRegister(0)*100.0 def sendCommand(self, data): #make sure data has an even number of elements if(len(data) % 2 == 1): data.append(0) #Initiate message as an empty list message = [] #Fill message by combining two bytes in one register for i in range(0, len(data)/2): message.append((data[2*i] << 8) + data[2*i+1]) #To do!: Implement try/except self.client.write_registers(0x03E8, message, unit=0x0009) def getStatus(self, numBytes): """Sends a request to read, wait for the response and returns the Gripper status. The method gets the number of bytes to read as an argument""" numRegs = int(ceil(numBytes/2.0)) #To do!: Implement try/except #Get status from the device response = self.client.read_holding_registers(0x07D0, numRegs, unit=0x0009) #Instantiate output as an empty list output = [] #Fill the output with the bytes in the appropriate order for i in range(0, numRegs): output.append((response.getRegister(i) & 0xFF00) >> 8) output.append( response.getRegister(i) & 0x00FF) #Output the result return output
class ImtSiRs485Sensor(object): """ Class that represents an ImtSiRs485 sensor attached via modbus RTU """ def __init__(self, tty): # type: (unicode) -> ImtSiRs485Sensor self.tty = '/dev/' + tty self.serial = ModbusSerialClient(port=self.tty, method=c.MODE, baudrate=c.BAUD_RATE, stopbits=c.STOP_BITS, bytesize=c.BYTE_SIZE, timeout=c.SERIAL_TIMEOUT, parity=c.PARITY) self.serial.close() # close for now, only open while in use def identify(self): # type: () -> (float, float) _log.info('searching for IMT SI-RS485 sensor on ' + self.tty) # noinspection PyBroadException try: self.serial.connect() request = FirmwareVersionModbusRequest(unit=c.SLAVE_ADDRESS) response = self.serial.execute(request) hw_ver = response.hardware_version fw_ver = response.firmware_version _log.info('found IMT SI-RS485 sensor') _log.info('hardware version: ' + str(hw_ver)) _log.info('firmware version: ' + str(fw_ver)) return hw_ver, fw_ver except Exception: msg = 'no IMT SI-RS485 sensor found' if c.LOG_LEVEL > logging.DEBUG: raise Exception(msg) else: _log.info(msg) ex = exc_info() raise ex[0](ex[1]).with_traceback(ex[2]) finally: self.serial.close() # close in any case def read_modbus_registers(self): # type: () -> (Sequence[int]) _log.debug('requesting modbus registers {0}-{1}'.format( c.BASE_ADDRESS, c.BASE_ADDRESS + c.NO_OF_REGISTERS - 1)) try: self.serial.connect() reply = self.serial.read_input_registers(c.BASE_ADDRESS, c.NO_OF_REGISTERS, unit=c.SLAVE_ADDRESS) if reply.function_code > 0x80: raise Exception('sensor signaled an error') if not hasattr(reply, 'function_code') or not hasattr( reply, 'registers'): raise Exception('received an unexpected reply from sensor') if len(reply.registers) != c.NO_OF_REGISTERS: raise Exception('received unexpected number of registers') return reply.registers finally: self.serial.close() # close in any case
def execute_extended_requests(): # ------------------------------------------------------------------------# # choose the client you want # ------------------------------------------------------------------------# # make sure to start an implementation to hit against. For this # you can use an existing device, the reference implementation in the tools # directory, or start a pymodbus server. # # It should be noted that you can supply an ipv4 or an ipv6 host address # for both the UDP and TCP clients. # ------------------------------------------------------------------------# client = ModbusClient(method='rtu', port="/dev/ttyp0") # client = ModbusClient('127.0.0.1', port=5020) client.connect() # ----------------------------------------------------------------------- # # extra requests # ----------------------------------------------------------------------- # # If you are performing a request that is not available in the client # mixin, you have to perform the request like this instead:: # # from pymodbus.diag_message import ClearCountersRequest # from pymodbus.diag_message import ClearCountersResponse # # request = ClearCountersRequest() # response = client.execute(request) # if isinstance(response, ClearCountersResponse): # ... do something with the response # # # What follows is a listing of all the supported methods. Feel free to # comment, uncomment, or modify each result set to match with your ref. # ----------------------------------------------------------------------- # # ----------------------------------------------------------------------- # # information requests # ----------------------------------------------------------------------- # log.debug("Running ReadDeviceInformationRequest") rq = ReadDeviceInformationRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference # assert (rr.function_code < 0x80) # test that we are not an error # assert (rr.information[0] == b'Pymodbus') # test the vendor name # assert (rr.information[1] == b'PM') # test the product code # assert (rr.information[2] == b'1.0') # test the code revision log.debug("Running ReportSlaveIdRequest") rq = ReportSlaveIdRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference # assert(rr.function_code < 0x80) # test that we are not an error # assert(rr.identifier == 0x00) # test the slave identifier # assert(rr.status == 0x00) # test that the status is ok log.debug("Running ReadExceptionStatusRequest") rq = ReadExceptionStatusRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference # assert(rr.function_code < 0x80) # test that we are not an error # assert(rr.status == 0x55) # test the status code log.debug("Running GetCommEventCounterRequest") rq = GetCommEventCounterRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference # assert(rr.function_code < 0x80) # test that we are not an error # assert(rr.status == True) # test the status code # assert(rr.count == 0x00) # test the status code log.debug("Running GetCommEventLogRequest") rq = GetCommEventLogRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference # assert(rr.function_code < 0x80) # test that we are not an error # assert(rr.status == True) # test the status code # assert(rr.event_count == 0x00) # test the number of events # assert(rr.message_count == 0x00) # test the number of messages # assert(len(rr.events) == 0x00) # test the number of events # ------------------------------------------------------------------------# # diagnostic requests # ------------------------------------------------------------------------# log.debug("Running ReturnQueryDataRequest") rq = ReturnQueryDataRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference # assert(rr.message[0] == 0x0000) # test the resulting message log.debug("Running RestartCommunicationsOptionRequest") rq = RestartCommunicationsOptionRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference # assert(rr.message == 0x0000) # test the resulting message log.debug("Running ReturnDiagnosticRegisterRequest") rq = ReturnDiagnosticRegisterRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference log.debug("Running ChangeAsciiInputDelimiterRequest") rq = ChangeAsciiInputDelimiterRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference log.debug("Running ForceListenOnlyModeRequest") rq = ForceListenOnlyModeRequest(unit=UNIT) rr = client.execute(rq) # does not send a response print(rr) log.debug("Running ClearCountersRequest") rq = ClearCountersRequest() rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference log.debug("Running ReturnBusCommunicationErrorCountRequest") rq = ReturnBusCommunicationErrorCountRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference log.debug("Running ReturnBusExceptionErrorCountRequest") rq = ReturnBusExceptionErrorCountRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference log.debug("Running ReturnSlaveMessageCountRequest") rq = ReturnSlaveMessageCountRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference log.debug("Running ReturnSlaveNoResponseCountRequest") rq = ReturnSlaveNoResponseCountRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference log.debug("Running ReturnSlaveNAKCountRequest") rq = ReturnSlaveNAKCountRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference log.debug("Running ReturnSlaveBusyCountRequest") rq = ReturnSlaveBusyCountRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference log.debug("Running ReturnSlaveBusCharacterOverrunCountRequest") rq = ReturnSlaveBusCharacterOverrunCountRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference log.debug("Running ReturnIopOverrunCountRequest") rq = ReturnIopOverrunCountRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference log.debug("Running ClearOverrunCountRequest") rq = ClearOverrunCountRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference log.debug("Running GetClearModbusPlusRequest") rq = GetClearModbusPlusRequest(unit=UNIT) rr = client.execute(rq) print(rr) # assert(rr == None) # not supported by reference # ------------------------------------------------------------------------# # close the client # ------------------------------------------------------------------------# client.close()
class SenseAirDevice(object): def __init__(self): #---------------------------------------------------------------------------# # This will simply send everything logged to console #---------------------------------------------------------------------------# logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) self.client = None def connect(self, deviceName): self.client = ModbusSerialClient(method='rtu', port=deviceName, stopbits=1, bytesize=8, baudrate=9600, timeout=0.2) if not self.client.connect(): rospy.logerr("Unable to connect to %s", device) return False return True def close(self): self.client.close() # Not working right now def getDeviceIdentification(self, objectId): rq = ReadDeviceInformationRequest(read_code=4, object_id=objectId, unit=0xFE) #rospy.loginfo("encoded: %h", encoded[0]) rr = self.client.execute(rq) print rr return "" if rr is None: rospy.logerr("No response from device") return None if rr.function_code < 0x80: # test that we are not an error return rr.information[0] #vendor_name = rr.information[0] #product_code = rr.information[1] #code_revision = rr.information[2] #rospy.loginfo("vendor: %s", vendor_name) #rospy.loginfo("product code: %s", product_code) #rospy.loginfo("revision: %s", code_revision) else: rospy.logwarn("error reading device identification: %h", rr.function_code) def getVendor(self): vendor = self.getDeviceIdentification(0) print vendor def readCO2(self): response = self.client.read_input_registers(address=3, count=1, unit=0xFE) return response.getRegister(0) def readTemperature(self): response = self.client.read_input_registers(address=4, count=1, unit=0xFE) return response.getRegister(0) * 100.0 def sendCommand(self, data): #make sure data has an even number of elements if (len(data) % 2 == 1): data.append(0) #Initiate message as an empty list message = [] #Fill message by combining two bytes in one register for i in range(0, len(data) / 2): message.append((data[2 * i] << 8) + data[2 * i + 1]) #To do!: Implement try/except self.client.write_registers(0x03E8, message, unit=0x0009) def getStatus(self, numBytes): """Sends a request to read, wait for the response and returns the Gripper status. The method gets the number of bytes to read as an argument""" numRegs = int(ceil(numBytes / 2.0)) #To do!: Implement try/except #Get status from the device response = self.client.read_holding_registers(0x07D0, numRegs, unit=0x0009) #Instantiate output as an empty list output = [] #Fill the output with the bytes in the appropriate order for i in range(0, numRegs): output.append((response.getRegister(i) & 0xFF00) >> 8) output.append(response.getRegister(i) & 0x00FF) #Output the result return output
# # request = ClearCountersRequest() # response = client.execute(request) # if isinstance(response, ClearCountersResponse): # ... do something with the response # # # What follows is a listing of all the supported methods. Feel free to # comment, uncomment, or modify each result set to match with your reference. #---------------------------------------------------------------------------# #---------------------------------------------------------------------------# # information requests #---------------------------------------------------------------------------# rq = ReadDeviceInformationRequest(unit=1) rr = client.execute(rq) #assert(rr == None) # not supported by reference assert(rr.function_code < 0x80) # test that we are not an error assert(rr.information[0] == b'Pymodbus') # test the vendor name assert(rr.information[1] == b'PM') # test the product code assert(rr.information[2] == b'1.0') # test the code revision rq = ReportSlaveIdRequest(unit=1) rr = client.execute(rq) # assert(rr == None) # not supported by reference #assert(rr.function_code < 0x80) # test that we are not an error #assert(rr.identifier == 0x00) # test the slave identifier #assert(rr.status == 0x00) # test that the status is ok rq = ReadExceptionStatusRequest(unit=1) rr = client.execute(rq)
class EPsolarTracerClient: ''' EPsolar Tracer client ''' def __init__(self, unit=1, serialclient=None, **kwargs): ''' Initialize a serial client instance ''' self.unit = unit if serialclient is None: port = kwargs.get('port', 'COM1') baudrate = kwargs.get('baudrate', 115200) self.client = ModbusClient( method="rtu", port=port, stopbits=1, bytesize=8, parity='N', baudrate=baudrate, timeout = 1.0 ) else: self.client = serialclient def connect(self): ''' Connect to the serial :returns: True if connection succeeded, False otherwise ''' cc = self.client.connect() if cc is False: print("Unable to open port. Quitting") quit() return cc def close(self): ''' Closes the underlying connection ''' return self.client.close() def read_device_info(self): #request = ReadDeviceInformationRequest(unit = self.unit) request = ReadDeviceInformationRequest(unit=self.unit) response = self.client.execute(request) return response def read_input(self, name): register = registerByName(name) if register.is_coil(): response = self.client.read_coils(register.address, register.size, unit=self.unit) elif register.is_discrete_input(): response = self.client.read_discrete_inputs(register.address, register.size, unit=self.unit) elif register.is_input_register(): response = self.client.read_input_registers(register.address, register.size, unit=self.unit) else: response = self.client.read_holding_registers(register.address, register.size, unit=self.unit) return register.decode(response) def write_output(self, name, value): register = registerByName(name) values = register.encode(value) response = False if register.is_coil(): self.client.write_coil(register.address, values, unit=self.unit) response = True elif register.is_discrete_input(): log.error("Cannot write discrete input " + repr(name)) pass elif register.is_input_register(): log.error("Cannot write input register " + repr(name)) pass else: self.client.write_registers(register.address, values, unit=self.unit) response = True return response def __enter__(self): self.connect() print("Context mngr connect") return self def __exit__(self,type,value,traceback): self.close() print("Context mngr close")
class EPsolarTracerClient: ''' EPsolar Tracer client ''' def __init__(self, unit=1, serialclient=None, **kwargs): ''' Initialize a serial client instance ''' self.unit = unit if serialclient == None: port = kwargs.get('port', '/dev/ttyXRUSB0') baudrate = kwargs.get('baudrate', 115200) self.client = ModbusClient(method='rtu', port=port, baudrate=baudrate, kwargs=kwargs) else: self.client = serialclient def connect(self): ''' Connect to the serial :returns: True if connection succeeded, False otherwise ''' return self.client.connect() def close(self): ''' Closes the underlying connection ''' return self.client.close() def read_device_info(self): request = ReadDeviceInformationRequest(unit=self.unit) response = self.client.execute(request) return response def read_input(self, name): register = registerByName(name) if register.is_coil(): response = self.client.read_coils(register.address, register.size, unit=self.unit) elif register.is_discrete_input(): response = self.client.read_discrete_inputs(register.address, register.size, unit=self.unit) elif register.is_input_register(): response = self.client.read_input_registers(register.address, register.size, unit=self.unit) else: response = self.client.read_holding_registers(register.address, register.size, unit=self.unit) return register.decode(response) def write_output(self, name, value): register = registerByName(name) values = register.encode(value) response = False if register.is_coil(): self.client.write_coil(register.address, values, unit=self.unit) response = True elif register.is_discrete_input(): _logger.error("Cannot write discrete input " + repr(name)) pass elif register.is_input_register(): _logger.error("Cannot write input register " + repr(name)) pass else: print(name, register.address, values, self.unit) registers = self.client.write_registers(register.address, values, unit=self.unit) print(registers) response = True return response def readRTC(self): register = registerByName('Real time clock 1') sizeAddress = 3 result = self.client.read_holding_registers(register.address, sizeAddress, unit=self.unit) return self.decodeRTC(result.registers) def writeRTC(self, datetime): register = registerByName('Real time clock 1') values = self.encodeRTC(datetime) self.client.write_registers(register.address, values, unit=self.unit) return True def decodeRTC(self, rtc): s = 2000 secMin = rtc[0] hourDay = rtc[1] monthYear = rtc[2] secs = (secMin & 0xff) hour = (hourDay & 0xff) month = (monthYear & 0xff) minut = secMin >> 8 day = hourDay >> 8 year = monthYear >> 8 return datetime(s + year, month, day, hour, minut, secs) def encodeRTC(self, datetime): s = 2000 rtc1 = int((datetime.minute << 8) | datetime.second) rtc2 = int((datetime.day << 8) | datetime.hour) rtc3 = int((datetime.year - s << 8) | datetime.month) return [rtc1, rtc2, rtc3]
class EPsolarTracerClient: ''' EPsolar Tracer client ''' def __init__(self, unit=1, serialclient=None, **kwargs): ''' Initialize a serial client instance ''' self.unit = unit if serialclient == None: port = kwargs.get('port', '/dev/ttyXRUSB0') baudrate = kwargs.get('baudrate', 115200) self.client = ModbusClient(method='rtu', port=port, baudrate=baudrate, kwargs=kwargs) else: self.client = serialclient def connect(self): ''' Connect to the serial :returns: True if connection succeeded, False otherwise ''' return self.client.connect() def close(self): ''' Closes the underlying connection ''' return self.client.close() def read_device_info(self): request = ReadDeviceInformationRequest(unit=self.unit) response = self.client.execute(request) return response def read_input(self, name): register = registerByName(name) if register.is_coil(): response = self.client.read_coils(register.address, register.size, unit=self.unit) elif register.is_discrete_input(): response = self.client.read_discrete_inputs(register.address, register.size, unit=self.unit) elif register.is_input_register(): response = self.client.read_input_registers(register.address, register.size, unit=self.unit) else: response = self.client.read_holding_registers(register.address, register.size, unit=self.unit) return register.decode(response) def write_output(self, name, value): register = registerByName(name) values = register.encode(value) response = False if register.is_coil(): self.client.write_coil(register.address, values, unit=self.unit) response = True elif register.is_discrete_input(): _logger.error("Cannot write discrete input " + repr(name)) pass elif register.is_input_register(): _logger.error("Cannot write input register " + repr(name)) pass else: self.client.write_registers(register.address, values, unit=self.unit) response = True return response
class EPsolarTracerClient: """ EPsolar Tracer client """ def __init__(self, unit: int=1, serialclient: ModbusClient=None, **kwargs): """ Initialize a serial client instance """ self.unit = unit if serialclient is None: if not 'port' in kwargs: kwargs['port'] = '/dev/ttyXRUSB0' if not 'baudrate' in kwargs: kwargs['baudrate'] = 115200 self.client = ModbusClient(method='rtu', **kwargs) else: self.client = serialclient def connect(self): """ Connect to the serial :returns: True if connection succeeded, False otherwise """ try: return self.client.connect() except AttributeError: # !FIXME there is bug in pymodbus when rtu mode is used and connection is not made: # !FIXME AttributeError: 'NoneType' object has no attribute 'interCharTimeout' that simply means connection failed return False def close(self): """ Closes the underlying connection """ return self.client.close() def read_device_info(self): request = ReadDeviceInformationRequest(unit=self.unit) response = self.client.execute(request) return response def read_input(self, register_type: Union[RegisterTypeEnum, CoilTypeEnum]): if type(register_type) == 'str': register = registerByName(register_type) elif type(register_type) == RegisterTypeEnum: register = registers.get(register_type) elif type(register_type) == CoilTypeEnum: register = coils.get(register_type) if not register: raise Exception("Unknown register {}".format(register_type.name)) if register.is_coil(): response = self.client.read_coils(register.address, register.size, unit=self.unit) elif register.is_discrete_input(): response = self.client.read_discrete_inputs(register.address, register.size, unit=self.unit) elif register.is_input_register(): response = self.client.read_input_registers(register.address, register.size, unit=self.unit) else: response = self.client.read_holding_registers(register.address, register.size, unit=self.unit) return register.decode(response) def write_output(self, register_type: Union[RegisterTypeEnum, CoilTypeEnum], value): if type(register_type) == 'str': register = registerByName(register_type) elif type(register_type) == RegisterTypeEnum: register = registers.get(register_type) elif type(register_type) == CoilTypeEnum: register = coils.get(register_type) if not register: raise Exception("Unknown register {}".format(register_type.name)) values = register.encode(value) response = False if register.is_coil(): self.client.write_coil(register.address, values, unit=self.unit) response = True elif register.is_discrete_input(): _logger.error("Cannot write discrete input {}".format(register_type.name)) pass elif register.is_input_register(): _logger.error("Cannot write input register {}".format(register_type.name)) pass else: self.client.write_registers(register.address, values, unit=self.unit) response = True return response
class modbusHandler: def __init__(self, type, tcpIp, tcpPort, Method, Port, Stopbits, Bytesize, Parity, Baudrate): self.modValConverter = ModValConverter() self.exceptionHandler = ModbusHandlerExceptions() self.tcpNumberOfRetries = 20 if (type == "TCP"): self.ModbusClient = ModbusTcpClient(tcpIp, tcpPort, timeout=0.5) else: if (type == "RTU"): if (len(Port) == 0): self.exceptionHandler.generateException( "NoPortSpecifiedError") self.ModbusClient = ModbusClient(method=Method, port=Port, stopbits=Stopbits, bytesize=Bytesize, parity=Parity, baudrate=Baudrate, timeout=1) self.ModbusClient.register(self.ReadFifoRequest.ReadFifoResponse) self.ModbusClient.register(self.Read2FifosRequest.Read2FifosResponse) self.ModbusClient.register( self.ReadFifoAndTimeRequest.ReadFifoAndTimeResponse) def setNumberOfRetriesTCP(self, number): self.tcpNumberOfRetries = number class DataOut: registers = [] class DataOutTwoRegs: registers0 = [] registers1 = [] class Read2FifosRequest(ModbusRequest): function_code = 66 _rtu_frame_size = 8 class Read2FifosResponse(ModbusResponse): function_code = 65 _rtu_byte_count_pos = 3 def __init__(self, values=None, **kwargs): ModbusResponse.__init__(self, **kwargs) self.values = values or [] def encode(self): result = bytes([len(self.values) * 2]) for register in self.values: result += struct.pack('>H', register) return result def decode(self, data): byte_count = int(data[1]) self.values = [] for i in range(2, byte_count + 1, 2): self.values.append(struct.unpack('>H', data[i:i + 2])[0]) def __init__(self, address=None, **kwargs): ModbusRequest.__init__(self, **kwargs) self.address = address self.count = 2 def encode(self): return struct.pack('>HH', self.address, self.count) def decode(self, data): self.address, self.count = struct.unpack('>HH', data) def execute(self, context): if not (1 <= self.count <= 0x7d0): return self.doException(ModbusExceptions.IllegalValue) if not context.validate(self.function_code, self.address, self.count): return self.doException(ModbusExceptions.IllegalAddress) values = context.getValues(self.function_code, self.address, self.count) return self.Read2FifosResponse(values) class ReadFifoRequest(ModbusRequest): function_code = 66 _rtu_frame_size = 8 class ReadFifoResponse(ModbusResponse): function_code = 24 _rtu_byte_count_pos = 3 def __init__(self, values=None, **kwargs): ModbusResponse.__init__(self, **kwargs) self.values = values or [] def encode(self): """ Encodes response pdu :returns: The encoded packet message """ # print(len(self.values)) result = bytes([len(self.values) * 2]) for register in self.values: result += struct.pack('>H', register) return result def decode(self, data): """ Decodes response pdu :param data: The packet data to decode """ # print(data) byte_count = int(data[1]) self.values = [] for i in range(2, byte_count + 1, 2): self.values.append(struct.unpack('>H', data[i:i + 2])[0]) class ReadFifoAndTimeRequest(ModbusRequest): function_code = 67 # _rtu_frame_size = 8 # fifoFreq = 100 class ReadFifoAndTimeResponse(ModbusResponse): function_code = 67 _rtu_double_byte_count_pos = 10 def __init__(self, values=None, **kwargs): ModbusResponse.__init__(self, **kwargs) self.values = values or [] # print(kwargs) def encode(self): """ Encodes response pdu :returns: The encoded packet message """ return self.values def decode(self, data): """ Decodes response pdu :param data: The packet data to decode """ timeFromStart = struct.unpack('>Q', data[0:8])[0] byte_count = struct.unpack('>H', data[8:10])[0] # print(data.hex()) # print(timeFromStart) # print(byte_count) byteCounter = 10 fifoAllRegs = [] sampleFreqs = [] while (byteCounter - 6 < byte_count): sampleFreqs.append( struct.unpack('>H', data[byteCounter:byteCounter + 2])[0]) byteCounter = byteCounter + 2 nextRegCount = struct.unpack( '>H', data[byteCounter:byteCounter + 2])[0] byteCounter = byteCounter + 2 fifoRegs = [] for i in range(nextRegCount): fifoRegs.append( struct.unpack( '>H', data[byteCounter + i * 2:byteCounter + i * 2 + 2])[0]) # fifoTimes.append(timeFromStart - (1/self.fifoFreq)*1000000*i) byteCounter = byteCounter + nextRegCount * 2 fifoAllRegs.append(fifoRegs) # print(fifoAllRegs) self.values = [timeFromStart, fifoAllRegs, sampleFreqs] def __init__(self, address=None, count=1, **kwargs): ModbusRequest.__init__(self, **kwargs) self.address = address self.count = count # print(kwargs) def encode(self): return struct.pack('>HH', self.address, self.count) def decode(self, data): self.address, self.count = struct.unpack('>HH', data) def execute(self, context): if not (1 <= self.count <= 0x7d0): return self.doException(ModbusExceptions.IllegalValue) if not context.validate(self.function_code, self.address, self.count): return self.doException(ModbusExceptions.IllegalAddress) values = context.getValues(self.function_code, self.address, self.count) return self.ReadFifoResponse(values) def connect(self): self.ModbusClient.connect() def isSlaveIsNotResponding(self, result): if (type(result) == pymodbus.exceptions.ModbusIOException): return True return False def writeCoils(self, addresS, valuE, uniT): addresS = addresS - 1 # when I ask 30001, python sends request for 30002, why??? Bug??? for x in range(self.tcpNumberOfRetries): try: result = self.ModbusClient.write_coils(address=addresS, values=valuE, unit=uniT) if (not self.isSlaveIsNotResponding(result)): break except pymodbus.exceptions.ConnectionException: pass if (type(result) == pymodbus.exceptions.ModbusIOException): self.exceptionHandler.generateException("ModbusIOError") return result def write16BitUnsignedRegister(self, addresS, valuE, uniT): addresS = addresS - 1 # when I ask 30001, python sends request for 30002, why??? Bug??? for x in range(self.tcpNumberOfRetries): try: result = self.ModbusClient.write_register(address=addresS, value=valuE, unit=uniT) if (not self.isSlaveIsNotResponding(result)): break except pymodbus.exceptions.ConnectionException: pass if (type(result) == pymodbus.exceptions.ModbusIOException): self.exceptionHandler.generateException("ModbusIOError") return result def write16BitUnsignedRegisters(self, addresS, valuE, uniT): addresS = addresS - 1 # when I ask 30001, python sends request for 30002, why??? Bug??? for x in range(self.tcpNumberOfRetries): try: result = self.ModbusClient.write_registers(address=addresS, values=valuE, unit=uniT) if (not self.isSlaveIsNotResponding(result)): break except pymodbus.exceptions.ConnectionException: pass if (type(result) == pymodbus.exceptions.ModbusIOException): self.exceptionHandler.generateException("ModbusIOError") return result def readCoils(self, addresS, counT, uniT): addresS = addresS - 1 # when I ask 30001, python sends request for 30002, why??? Bug??? for x in range(self.tcpNumberOfRetries): try: result = self.ModbusClient.read_coils(address=addresS, count=counT, unit=uniT) if (not self.isSlaveIsNotResponding(result)): break except pymodbus.exceptions.ConnectionException: pass if (type(result) == pymodbus.exceptions.ModbusIOException): self.exceptionHandler.generateException("ModbusIOError") return result def readDiscreteInputs(self, addresS, counT, uniT): addresS = addresS - 1 # when I ask 30001, python sends request for 30002, why??? Bug??? for x in range(self.tcpNumberOfRetries): try: result = self.ModbusClient.read_discrete_inputs( address=addresS, count=counT, unit=uniT) if (not self.isSlaveIsNotResponding(result)): break except pymodbus.exceptions.ConnectionException: pass if (type(result) == pymodbus.exceptions.ModbusIOException): self.exceptionHandler.generateException("ModbusIOError") return result def readInputRegisters(self, addresS, counT, uniT): addresS = addresS - 1 # when I ask 30001, python sends request for 30002, why??? Bug??? for x in range(self.tcpNumberOfRetries): try: result = self.ModbusClient.read_input_registers( address=addresS, count=counT, unit=uniT) if (not self.isSlaveIsNotResponding(result)): break except pymodbus.exceptions.ConnectionException: pass if (type(result) == pymodbus.exceptions.ModbusIOException): self.exceptionHandler.generateException("ModbusIOError") return result def readHoldingRegisters(self, addresS, counT, uniT): addresS = addresS - 1 # when I ask 30001, python sends request for 30002, why??? Bug??? for x in range(self.tcpNumberOfRetries): try: result = self.ModbusClient.read_holding_registers( address=addresS, count=counT, unit=uniT) if (not self.isSlaveIsNotResponding(result)): break except pymodbus.exceptions.ConnectionException: pass if (type(result) == pymodbus.exceptions.ModbusIOException): self.exceptionHandler.generateException("ModbusIOError") return result def getFifoAndTime(self, addresS, number, uniT): addresS = addresS - 1 # when I ask 30001, python sends request for 30002, why??? Bug??? for x in range(self.tcpNumberOfRetries): # print(x) try: request = self.ReadFifoAndTimeRequest(address=addresS, count=number, unit=uniT) result = self.ModbusClient.execute(request) if (not self.isSlaveIsNotResponding(result)): break except pymodbus.exceptions.ConnectionException: pass if (type(result) == pymodbus.exceptions.ModbusIOException): self.exceptionHandler.generateException("ModbusIOError") try: encoded = result.encode() except AttributeError: self.exceptionHandler.generateException("EncodeError") return result if type(encoded) is not list: if (type(result) == pymodbus.pdu.ExceptionResponse): self.exceptionHandler.generateException(encoded) else: self.exceptionHandler.generateException("NotAListReturned") dataOut = [] # encoded[0] = encoded[0] # print(encoded) for i in range(number): fifoLocal = [] timeLineLocal = [] rang = len(encoded[1][i]) timeStep = (1 / encoded[2][i]) * 1000 for j in range(rang): fifoLocal.append(encoded[1][i][j]) timeLineLocal.append(encoded[0] - (rang - j) * timeStep) newAr = [fifoLocal, timeLineLocal] dataOut.append(newAr) return dataOut def getFifoAndTime16BitUnsigned(self, addresS, number, uniT): result = self.getFifoAndTime(addresS, number, uniT) result = list(map(tuple, result)) return result def getFifoAndTime16BitSigned(self, addresS, number, uniT): result = self.getFifoAndTime(addresS, number, uniT) for x in range(len(result)): result[x][0] = list( map(self.modValConverter.modTo16BitSigned, result[x][0])) result = list(map(tuple, result)) return result def getFifoAndTime32BitUnsigned(self, addresS, number, uniT): result = self.getFifoAndTime(addresS, number * 2, uniT) outData = [] for x in range(number): timeLine = result[x * 2][1] if (len(result[x * 2][0]) != len(result[x * 2][1])): self.exceptionHandler.generateException( "RegsFIFOsHaveDifferentSizes") for i in range(len(result[x * 2][0])): result[x * 2][0][i] = self.modValConverter.modTo32BitUnsigned( result[x * 2][0][i], result[x * 2 + 1][0][i]) dataLine = result[x * 2][0] outData.append((dataLine, timeLine)) return outData def getFifoAndTime32BitSigned(self, addresS, number, uniT): result = self.getFifoAndTime(addresS, number * 2, uniT) outData = [] for x in range(number): timeLine = result[x * 2][1] if (len(result[x * 2][0]) != len(result[x * 2][1])): self.exceptionHandler.generateException( "RegsFIFOsHaveDifferentSizes") for i in range(len(result[x * 2][0])): result[x * 2][0][i] = self.modValConverter.modTo32BitSigned( result[x * 2][0][i], result[x * 2 + 1][0][i]) dataLine = result[x * 2][0] outData.append((dataLine, timeLine)) return outData def getFifoAndTimeFloat(self, addresS, number, uniT): result = self.getFifoAndTime(addresS, number * 2, uniT) outData = [] for x in range(number): timeLine = result[x * 2][1] if (len(result[x * 2][0]) != len(result[x * 2][1])): self.exceptionHandler.generateException( "RegsFIFOsHaveDifferentSizes") for i in range(len(result[x * 2][0])): result[x * 2][0][i] = self.modValConverter.modToFloat( result[x * 2][0][i], result[x * 2 + 1][0][i]) dataLine = result[x * 2][0] outData.append((dataLine, timeLine)) return outData