Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Galen Collins committed Jul 23, 2014
1 parent 741d6f0 commit 1e0bcde
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 40 deletions.
34 changes: 21 additions & 13 deletions pymodbus/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +81,28 @@ class Defaults(Singleton):
Indicates if the slave datastore should use indexing at 0 or 1.
Mor about this can be read in section 4.4 of the modbus specification.
.. attribute:: IgnoreMissingSlaves
In case a request is made to a missing slave, this defines if an error
should be returned or simply ignored. This is useful for the case of a
serial server emulater where a request to a non-existant slave on a bus
will never respond. The client in this case will simply timeout.
'''
Port = 502
Retries = 3
RetryOnEmpty = False
Timeout = 3
Reconnects = 0
TransactionId = 0
ProtocolId = 0
UnitId = 0x00
Baudrate = 19200
Parity = 'N'
Bytesize = 8
Stopbits = 1
ZeroMode = False
Port = 502
Retries = 3
RetryOnEmpty = False
Timeout = 3
Reconnects = 0
TransactionId = 0
ProtocolId = 0
UnitId = 0x00
Baudrate = 19200
Parity = 'N'
Bytesize = 8
Stopbits = 1
ZeroMode = False
IgnoreMissingSlaves = False


class ModbusStatus(Singleton):
Expand Down
8 changes: 4 additions & 4 deletions pymodbus/datastore/context.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pymodbus.exceptions import ParameterException
from pymodbus.exceptions import NoSuchSlaveException
from pymodbus.interfaces import IModbusSlaveContext
from pymodbus.datastore.store import ModbusSequentialDataBlock
from pymodbus.constants import Defaults
Expand Down Expand Up @@ -129,7 +129,7 @@ def __setitem__(self, slave, context):
if self.single: slave = Defaults.UnitId
if 0xf7 >= slave >= 0x00:
self.__slaves[slave] = context
else: raise ParameterException('slave index out of range')
else: raise NoSuchSlaveException('slave index[%d] out of range' % slave)

def __delitem__(self, slave):
''' Wrapper used to access the slave context
Expand All @@ -138,7 +138,7 @@ def __delitem__(self, slave):
'''
if not self.single and (0xf7 >= slave >= 0x00):
del self.__slaves[slave]
else: raise ParameterException('slave index out of range')
else: raise NoSuchSlaveException('slave index[%d] out of range' % slave)

def __getitem__(self, slave):
''' Used to get access to a slave context
Expand All @@ -149,4 +149,4 @@ def __getitem__(self, slave):
if self.single: slave = Defaults.UnitId
if slave in self.__slaves:
return self.__slaves.get(slave)
else: raise ParameterException("slave does not exist, or is out of range")
else: raise NoSuchSlaveException('slave index[%d] out of range' % slave)
20 changes: 19 additions & 1 deletion pymodbus/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class ModbusException(Exception):

def __init__(self, string):
''' Initialize the exception
:param string: The message to append to the error
'''
self.string = string
Expand All @@ -24,6 +25,7 @@ class ModbusIOException(ModbusException):

def __init__(self, string=""):
''' Initialize the exception
:param string: The message to append to the error
'''
message = "[Input/Output] %s" % string
Expand All @@ -35,17 +37,32 @@ class ParameterException(ModbusException):

def __init__(self, string=""):
''' Initialize the exception
:param string: The message to append to the error
'''
message = "[Invalid Paramter] %s" % string
ModbusException.__init__(self, message)


class NoSuchSlaveException(ModbusException):
''' Error resulting from making a request to a slave
that does not exist '''

def __init__(self, string=""):
''' Initialize the exception
:param string: The message to append to the error
'''
message = "[No Such Slave] %s" % string
ModbusException.__init__(self, message)


class NotImplementedException(ModbusException):
''' Error resulting from not implemented function '''

def __init__(self, string=""):
''' Initialize the exception
:param string: The message to append to the error
'''
message = "[Not Implemented] %s" % string
Expand All @@ -57,6 +74,7 @@ class ConnectionException(ModbusException):

def __init__(self, string=""):
''' Initialize the exception
:param string: The message to append to the error
'''
message = "[Connection] %s" % string
Expand All @@ -68,5 +86,5 @@ def __init__(self, string=""):
__all__ = [
"ModbusException", "ModbusIOException",
"ParameterException", "NotImplementedException",
"ConnectionException",
"ConnectionException", "NoSuchSlaveException",
]
33 changes: 24 additions & 9 deletions pymodbus/server/async.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ def _execute(self, request):
try:
context = self.factory.store[request.unit_id]
response = request.execute(context)
except NoSuchSlaveException, ex:
_logger.debug("requested slave does not exist: %s; %s", ex, traceback.format_exc() )
if self.factory.ignore_missing_slaves:
return # the client will simply timeout waiting for a response
response = request.doException(merror.GatewayNoResponse)
except Exception, ex:
_logger.debug("Datastore unable to fulfill request: %s" % ex)
response = request.doException(merror.SlaveFailure)
Expand Down Expand Up @@ -96,7 +101,7 @@ class ModbusServerFactory(ServerFactory):

protocol = ModbusTcpProtocol

def __init__(self, store, framer=None, identity=None):
def __init__(self, store, framer=None, identity=None, **kwargs):
''' Overloaded initializer for the modbus factory
If the identify structure is not passed in, the ModbusControlBlock
Expand All @@ -105,13 +110,14 @@ def __init__(self, store, framer=None, identity=None):
:param store: The ModbusServerContext datastore
:param framer: The framer strategy to use
:param identity: An optional identify structure
:param ignore_missing_slaves: True to not send errors on a request to a missing slave
'''
self.decoder = ServerDecoder()
self.framer = framer or ModbusSocketFramer
self.store = store or ModbusServerContext()
self.control = ModbusControlBlock()
self.access = ModbusAccessControl()
self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves', Defaults.IgnoreMissingSlaves)

if isinstance(identity, ModbusDeviceIdentification):
self.control.Identity.update(identity)
Expand All @@ -123,7 +129,7 @@ def __init__(self, store, framer=None, identity=None):
class ModbusUdpProtocol(protocol.DatagramProtocol):
''' Implements a modbus udp server in twisted '''

def __init__(self, store, framer=None, identity=None):
def __init__(self, store, framer=None, identity=None, **kwargs):
''' Overloaded initializer for the modbus factory
If the identify structure is not passed in, the ModbusControlBlock
Expand All @@ -132,13 +138,14 @@ def __init__(self, store, framer=None, identity=None):
:param store: The ModbusServerContext datastore
:param framer: The framer strategy to use
:param identity: An optional identify structure
:param ignore_missing_slaves: True to not send errors on a request to a missing slave
'''
framer = framer or ModbusSocketFramer
self.framer = framer(decoder=ServerDecoder())
self.store = store or ModbusServerContext()
self.control = ModbusControlBlock()
self.access = ModbusAccessControl()
self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves', Defaults.IgnoreMissingSlaves)

if isinstance(identity, ModbusDeviceIdentification):
self.control.Identity.update(identity)
Expand All @@ -163,6 +170,11 @@ def _execute(self, request, addr):
try:
context = self.store[request.unit_id]
response = request.execute(context)
except NoSuchSlaveException, ex:
_logger.debug("requested slave does not exist: %s; %s", ex, traceback.format_exc() )
if self.ignore_missing_slaves:
return # the client will simply timeout waiting for a response
response = request.doException(merror.GatewayNoResponse)
except Exception, ex:
_logger.debug("Datastore unable to fulfill request: %s" % ex)
response = request.doException(merror.SlaveFailure)
Expand All @@ -187,38 +199,40 @@ def _send(self, message, addr):
#---------------------------------------------------------------------------#
# Starting Factories
#---------------------------------------------------------------------------#
def StartTcpServer(context, identity=None, address=None, console=False):
def StartTcpServer(context, identity=None, address=None, console=False, **kwargs):
''' Helper method to start the Modbus Async TCP server
:param context: The server data context
:param identify: The server identity to use (default empty)
:param address: An optional (interface, port) to bind to.
:param console: A flag indicating if you want the debug console
:param ignore_missing_slaves: True to not send errors on a request to a missing slave
'''
from twisted.internet import reactor

address = address or ("", Defaults.Port)
framer = ModbusSocketFramer
factory = ModbusServerFactory(context, framer, identity)
factory = ModbusServerFactory(context, framer, identity, **kwargs)
if console: InstallManagementConsole({'factory': factory})

_logger.info("Starting Modbus TCP Server on %s:%s" % address)
reactor.listenTCP(address[1], factory, interface=address[0])
reactor.run()


def StartUdpServer(context, identity=None, address=None):
def StartUdpServer(context, identity=None, address=None, **kwargs):
''' Helper method to start the Modbus Async Udp server
:param context: The server data context
:param identify: The server identity to use (default empty)
:param address: An optional (interface, port) to bind to.
:param ignore_missing_slaves: True to not send errors on a request to a missing slave
'''
from twisted.internet import reactor

address = address or ("", Defaults.Port)
framer = ModbusSocketFramer
server = ModbusUdpProtocol(context, framer, identity)
server = ModbusUdpProtocol(context, framer, identity, **kwargs)

_logger.info("Starting Modbus UDP Server on %s:%s" % address)
reactor.listenUDP(address[1], server, interface=address[0])
Expand All @@ -235,6 +249,7 @@ def StartSerialServer(context, identity=None,
:param port: The serial port to attach to
:param baudrate: The baud rate to use for the serial device
:param console: A flag indicating if you want the debug console
:param ignore_missing_slaves: True to not send errors on a request to a missing slave
'''
from twisted.internet import reactor
from twisted.internet.serialport import SerialPort
Expand All @@ -244,7 +259,7 @@ def StartSerialServer(context, identity=None,
console = kwargs.get('console', False)

_logger.info("Starting Modbus Serial Server on %s" % port)
factory = ModbusServerFactory(context, framer, identity)
factory = ModbusServerFactory(context, framer, identity, **kwargs)
if console: InstallManagementConsole({'factory': factory})

protocol = factory.buildProtocol(None)
Expand Down
Loading

0 comments on commit 1e0bcde

Please sign in to comment.