pymodbus/0000755000175500017550000000000012607273654012466 5ustar debacledebaclepymodbus/ez_setup.py0000755000175500017550000002276412607272152014704 0ustar debacledebacle#!python """Bootstrap setuptools installation If you want to use setuptools in your package's setup.py, just include this file in the same directory with it, and add this to the top of your setup.py:: from ez_setup import use_setuptools use_setuptools() If you want to require a specific version of setuptools, set a download mirror, or use an alternate download directory, you can do so by supplying the appropriate options to ``use_setuptools()``. This file can also be run as a script to install or upgrade setuptools. """ import sys DEFAULT_VERSION = "0.6c9" DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] md5_data = { 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03', 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a', 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6', 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a', } import sys, os try: from hashlib import md5 except ImportError: from md5 import md5 def _validate_md5(egg_name, data): if egg_name in md5_data: digest = md5(data).hexdigest() if digest != md5_data[egg_name]: print >>sys.stderr, ( "md5 validation of %s failed! (Possible download problem?)" % egg_name ) sys.exit(2) return data def use_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, download_delay=15 ): """Automatically find/download setuptools and make it available on sys.path `version` should be a valid setuptools version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where setuptools will be downloaded, if it is not already available. If `download_delay` is specified, it should be the number of seconds that will be paused before initiating a download, should one be required. If an older version of setuptools is installed, this routine will print a message to ``sys.stderr`` and raise SystemExit in an attempt to abort the calling script. """ was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules def do_download(): egg = download_setuptools(version, download_base, to_dir, download_delay) sys.path.insert(0, egg) import setuptools; setuptools.bootstrap_install_from = egg try: import pkg_resources except ImportError: return do_download() try: pkg_resources.require("setuptools>="+version); return except pkg_resources.VersionConflict, e: if was_imported: print >>sys.stderr, ( "The required version of setuptools (>=%s) is not available, and\n" "can't be installed while this script is running. Please install\n" " a more recent version first, using 'easy_install -U setuptools'." "\n\n(Currently using %r)" ) % (version, e.args[0]) sys.exit(2) else: del pkg_resources, sys.modules['pkg_resources'] # reload ok return do_download() except pkg_resources.DistributionNotFound: return do_download() def download_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, delay = 15 ): """Download setuptools from a specified location and return its filename `version` should be a valid setuptools version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where the egg will be downloaded. `delay` is the number of seconds to pause before an actual download attempt. """ import urllib2, shutil egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) url = download_base + egg_name saveto = os.path.join(to_dir, egg_name) src = dst = None if not os.path.exists(saveto): # Avoid repeated downloads try: from distutils import log if delay: log.warn(""" --------------------------------------------------------------------------- This script requires setuptools version %s to run (even to display help). I will attempt to download it for you (from %s), but you may need to enable firewall access for this script first. I will start the download in %d seconds. (Note: if this machine does not have network access, please obtain the file %s and place it in this directory before rerunning this script.) ---------------------------------------------------------------------------""", version, download_base, delay, url ); from time import sleep; sleep(delay) log.warn("Downloading %s", url) src = urllib2.urlopen(url) # Read/write all in one block, so we don't create a corrupt file # if the download is interrupted. data = _validate_md5(egg_name, src.read()) dst = open(saveto,"wb"); dst.write(data) finally: if src: src.close() if dst: dst.close() return os.path.realpath(saveto) def main(argv, version=DEFAULT_VERSION): """Install or upgrade setuptools and EasyInstall""" try: import setuptools except ImportError: egg = None try: egg = download_setuptools(version, delay=0) sys.path.insert(0,egg) from setuptools.command.easy_install import main return main(list(argv)+[egg]) # we're done here finally: if egg and os.path.exists(egg): os.unlink(egg) else: if setuptools.__version__ == '0.0.1': print >>sys.stderr, ( "You have an obsolete version of setuptools installed. Please\n" "remove it from your system entirely before rerunning this script." ) sys.exit(2) req = "setuptools>="+version import pkg_resources try: pkg_resources.require(req) except pkg_resources.VersionConflict: try: from setuptools.command.easy_install import main except ImportError: from easy_install import main main(list(argv)+[download_setuptools(delay=0)]) sys.exit(0) # try to force an exit else: if argv: from setuptools.command.easy_install import main main(argv) else: print "Setuptools version",version,"or greater has been installed." print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' def update_md5(filenames): """Update our built-in md5 registry""" import re for name in filenames: base = os.path.basename(name) f = open(name,'rb') md5_data[base] = md5(f.read()).hexdigest() f.close() data = [" %r: %r,\n" % it for it in md5_data.items()] data.sort() repl = "".join(data) import inspect srcfile = inspect.getsourcefile(sys.modules[__name__]) f = open(srcfile, 'rb'); src = f.read(); f.close() match = re.search("\nmd5_data = {\n([^}]+)}", src) if not match: print >>sys.stderr, "Internal error!" sys.exit(2) src = src[:match.start(1)] + repl + src[match.end(1):] f = open(srcfile,'w') f.write(src) f.close() if __name__=='__main__': if len(sys.argv)>2 and sys.argv[1]=='--md5update': update_md5(sys.argv[2:]) else: main(sys.argv[1:]) pymodbus/setup_commands.py0000755000175500017550000001264612607272152016065 0ustar debacledebaclefrom distutils.core import Command import sys, os, shutil #---------------------------------------------------------------------------# # Extra Commands #---------------------------------------------------------------------------# class BuildApiDocsCommand(Command): ''' Helper command to build the available api documents This scans all the subdirectories under api and runs the build.py script underneath trying to build the api documentation for the given format. ''' description = "build all the projects api documents" user_options = [] def initialize_options(self): ''' options setup ''' if not os.path.exists('./build'): os.mkdir('./build') def finalize_options(self): ''' options teardown ''' pass def run(self): ''' command runner ''' old_cwd = os.getcwd() directories = (d for d in os.listdir('./doc/api') if not d.startswith('.')) for entry in directories: os.chdir('./doc/api/%s' % entry) os.system('python build.py') os.chdir(old_cwd) class DeepCleanCommand(Command): ''' Helper command to return the directory to a completely clean state. ''' description = "clean everything that we don't want" user_options = [] def initialize_options(self): ''' options setup ''' self.trash = ['build', 'dist', 'pymodbus.egg-info', os.path.join(os.path.join('doc','sphinx'),'build'), ] def finalize_options(self): pass def run(self): ''' command runner ''' self.__delete_pyc_files() self.__delete_trash_dirs() def __delete_trash_dirs(self): ''' remove all directories created in building ''' self.__delete_pyc_files() for directory in self.trash: if os.path.exists(directory): shutil.rmtree(directory) def __delete_pyc_files(self): ''' remove all python cache files ''' for root,dirs,files in os.walk('.'): for file in files: if file.endswith('.pyc'): os.remove(os.path.join(root,file)) class LintCommand(Command): ''' Helper command to perform a lint scan of the sourcecode and return the results. ''' description = "perform a lint scan of the code" user_options = [] def initialize_options(self): ''' options setup ''' if not os.path.exists('./build'): os.mkdir('./build') def finalize_options(self): pass def run(self): ''' command runner ''' scanners = [s for s in dir(self) if s.find('__try') >= 0] for scanner in scanners: if getattr(self, scanner)(): break def __try_pyflakes(self): try: from pyflakes.scripts.pyflakes import main sys.argv = '''pyflakes pymodbus'''.split() main() return True except: return False def __try_pychecker(self): try: import pychecker sys.argv = '''pychecker pymodbus/*.py'''.split() main() return True except: return False def __try_pylint(self): try: import pylint sys.argv = '''pylint pymodbus/*.py'''.split() main() return True except: return False class Python3Command(Command): ''' Helper command to scan for potential python 3 errors. ./setup.py scan_2to3 > build/diffs_2to3 build/report_2to3 ''' description = "perform 2to3 scan of the code" user_options = [] def initialize_options(self): ''' options setup ''' if not os.path.exists('./build'): os.mkdir('./build') self.directories = ['pymodbus', 'test', 'examples'] def finalize_options(self): pass def run(self): ''' command runner ''' self.__run_python3() def __run_python3(self): try: from lib2to3.main import main sys.argv = ['2to3'] + self.directories main("lib2to3.fixes") return True except: return False class Pep8Command(Command): ''' Helper command to scan for potential pep8 violations ''' description = "perform pep8 scan of the code" user_options = [] def initialize_options(self): ''' options setup ''' if not os.path.exists('./build'): os.mkdir('./build') self.directories = ['pymodbus'] def finalize_options(self): pass def run(self): ''' command runner ''' self.__run_pep8() def __run_pep8(self): try: from pep8 import _main as main sys.argv = '''pep8 --repeat --count --statistics '''.split() + self.directories main() return True except: return False #---------------------------------------------------------------------------# # Command Configuration #---------------------------------------------------------------------------# command_classes = { 'deep_clean' : DeepCleanCommand, 'build_apidocs' : BuildApiDocsCommand, 'lint' : LintCommand, 'scan_2to3' : Python3Command, 'pep8' : Pep8Command, } #---------------------------------------------------------------------------# # Export command list #---------------------------------------------------------------------------# __all__ = ['command_classes'] pymodbus/examples/0000755000175500017550000000000012607273533014300 5ustar debacledebaclepymodbus/examples/contrib/0000755000175500017550000000000012607272152015734 5ustar debacledebaclepymodbus/examples/contrib/sunspec_client.py0000644000175500017550000002537612607272152021341 0ustar debacledebaclefrom pymodbus.constants import Endian from pymodbus.client.sync import ModbusTcpClient from pymodbus.payload import BinaryPayloadDecoder from twisted.internet.defer import Deferred #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging _logger = logging.getLogger(__name__) _logger.setLevel(logging.DEBUG) logging.basicConfig() #---------------------------------------------------------------------------# # Sunspec Common Constants #---------------------------------------------------------------------------# class SunspecDefaultValue(object): ''' A collection of constants to indicate if a value is not implemented. ''' Signed16 = 0x8000 Unsigned16 = 0xffff Accumulator16 = 0x0000 Scale = 0x8000 Signed32 = 0x80000000 Float32 = 0x7fc00000 Unsigned32 = 0xffffffff Accumulator32 = 0x00000000 Signed64 = 0x8000000000000000 Unsigned64 = 0xffffffffffffffff Accumulator64 = 0x0000000000000000 String = '\x00' class SunspecStatus(object): ''' Indicators of the current status of a sunspec device ''' Normal = 0x00000000 Error = 0xfffffffe Unknown = 0xffffffff class SunspecIdentifier(object): ''' Assigned identifiers that are pre-assigned by the sunspec protocol. ''' Sunspec = 0x53756e53 class SunspecModel(object): ''' Assigned device indentifiers that are pre-assigned by the sunspec protocol. ''' #--------------------------------------------- # 0xx Common Models #--------------------------------------------- CommonBlock = 1 AggregatorBlock = 2 #--------------------------------------------- # 1xx Inverter Models #--------------------------------------------- SinglePhaseIntegerInverter = 101 SplitPhaseIntegerInverter = 102 ThreePhaseIntegerInverter = 103 SinglePhaseFloatsInverter = 103 SplitPhaseFloatsInverter = 102 ThreePhaseFloatsInverter = 103 #--------------------------------------------- # 2xx Meter Models #--------------------------------------------- SinglePhaseMeter = 201 SplitPhaseMeter = 201 WyeConnectMeter = 201 DeltaConnectMeter = 201 #--------------------------------------------- # 3xx Environmental Models #--------------------------------------------- BaseMeteorological = 301 Irradiance = 302 BackOfModuleTemperature = 303 Inclinometer = 304 Location = 305 ReferencePoint = 306 BaseMeteorological = 307 MiniMeteorological = 308 #--------------------------------------------- # 4xx String Combiner Models #--------------------------------------------- BasicStringCombiner = 401 AdvancedStringCombiner = 402 #--------------------------------------------- # 5xx Panel Models #--------------------------------------------- PanelFloat = 501 PanelInteger = 502 #--------------------------------------------- # 641xx Outback Blocks #--------------------------------------------- OutbackDeviceIdentifier = 64110 OutbackChargeController = 64111 OutbackFMSeriesChargeController = 64112 OutbackFXInverterRealTime = 64113 OutbackFXInverterConfiguration = 64114 OutbackSplitPhaseRadianInverter = 64115 OutbackRadianInverterConfiguration = 64116 OutbackSinglePhaseRadianInverterRealTime = 64117 OutbackFLEXNetDCRealTime = 64118 OutbackFLEXNetDCConfiguration = 64119 OutbackSystemControl = 64120 #--------------------------------------------- # 64xxx Vender Extension Block #--------------------------------------------- EndOfSunSpecMap = 65535 @classmethod def lookup(klass, code): ''' Given a device identifier, return the device model name for that identifier :param code: The device code to lookup :returns: The device model name, or None if none available ''' values = dict((v, k) for k, v in klass.__dict__.iteritems() if not callable(v)) return values.get(code, None) class SunspecOffsets(object): ''' Well known offsets that are used throughout the sunspec protocol ''' CommonBlock = 40000 CommonBlockLength = 69 AlternateCommonBlock = 50000 #---------------------------------------------------------------------------# # Common Functions #---------------------------------------------------------------------------# def defer_or_apply(func): ''' Decorator to apply an adapter method to a result regardless if it is a deferred or a concrete response. :param func: The function to decorate ''' def closure(future, adapt): if isinstance(defer, Deferred): d = Deferred() future.addCallback(lambda r: d.callback(adapt(r))) return d return adapt(future) return closure def create_sunspec_sync_client(host): ''' A quick helper method to create a sunspec client. :param host: The host to connect to :returns: an initialized SunspecClient ''' modbus = ModbusTcpClient(host) modbus.connect() client = SunspecClient(modbus) client.initialize() return client #---------------------------------------------------------------------------# # Sunspec Client #---------------------------------------------------------------------------# class SunspecDecoder(BinaryPayloadDecoder): ''' A decoder that deals correctly with the sunspec binary format. ''' def __init__(self, payload, endian): ''' Initialize a new instance of the SunspecDecoder .. note:: This is always set to big endian byte order as specified in the protocol. ''' endian = Endian.Big BinaryPayloadDecoder.__init__(self, payload, endian) def decode_string(self, size=1): ''' Decodes a string from the buffer :param size: The size of the string to decode ''' self._pointer += size string = self._payload[self._pointer - size:self._pointer] return string.split(SunspecDefaultValue.String)[0] class SunspecClient(object): def __init__(self, client): ''' Initialize a new instance of the client :param client: The modbus client to use ''' self.client = client self.offset = SunspecOffsets.CommonBlock def initialize(self): ''' Initialize the underlying client values :returns: True if successful, false otherwise ''' decoder = self.get_device_block(self.offset, 2) if decoder.decode_32bit_uint() == SunspecIdentifier.Sunspec: return True self.offset = SunspecOffsets.AlternateCommonBlock decoder = self.get_device_block(self.offset, 2) return decoder.decode_32bit_uint() == SunspecIdentifier.Sunspec def get_common_block(self): ''' Read and return the sunspec common information block. :returns: A dictionary of the common block information ''' length = SunspecOffsets.CommonBlockLength decoder = self.get_device_block(self.offset, length) return { 'SunSpec_ID': decoder.decode_32bit_uint(), 'SunSpec_DID': decoder.decode_16bit_uint(), 'SunSpec_Length': decoder.decode_16bit_uint(), 'Manufacturer': decoder.decode_string(size=32), 'Model': decoder.decode_string(size=32), 'Options': decoder.decode_string(size=16), 'Version': decoder.decode_string(size=16), 'SerialNumber': decoder.decode_string(size=32), 'DeviceAddress': decoder.decode_16bit_uint(), 'Next_DID': decoder.decode_16bit_uint(), 'Next_DID_Length': decoder.decode_16bit_uint(), } def get_device_block(self, offset, size): ''' A helper method to retrieve the next device block .. note:: We will read 2 more registers so that we have the information for the next block. :param offset: The offset to start reading at :param size: The size of the offset to read :returns: An initialized decoder for that result ''' _logger.debug("reading device block[{}..{}]".format(offset, offset + size)) response = self.client.read_holding_registers(offset, size + 2) return SunspecDecoder.fromRegisters(response.registers) def get_all_device_blocks(self): ''' Retrieve all the available blocks in the supplied sunspec device. .. note:: Since we do not know how to decode the available blocks, this returns a list of dictionaries of the form: decoder: the-binary-decoder, model: the-model-identifier (name) :returns: A list of the available blocks ''' blocks = [] offset = self.offset + 2 model = SunspecModel.CommonBlock while model != SunspecModel.EndOfSunSpecMap: decoder = self.get_device_block(offset, 2) model = decoder.decode_16bit_uint() length = decoder.decode_16bit_uint() blocks.append({ 'model' : model, 'name' : SunspecModel.lookup(model), 'length': length, 'offset': offset + length + 2 }) offset += length + 2 return blocks #------------------------------------------------------------ # A quick test runner #------------------------------------------------------------ if __name__ == "__main__": client = create_sunspec_sync_client("YOUR.HOST.GOES.HERE") # print out all the device common block common = client.get_common_block() for key, value in common.iteritems(): if key == "SunSpec_DID": value = SunspecModel.lookup(value) print "{:<20}: {}".format(key, value) # print out all the available device blocks blocks = client.get_all_device_blocks() for block in blocks: print block client.client.close() pymodbus/examples/contrib/redis-datastore.py0000644000175500017550000002201612607272152021401 0ustar debacledebacleimport redis from pymodbus.interfaces import IModbusSlaveContext from pymodbus.utilities import pack_bitstring, unpack_bitstring #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging; _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Context #---------------------------------------------------------------------------# class RedisSlaveContext(IModbusSlaveContext): ''' This is a modbus slave context using redis as a backing store. ''' def __init__(self, **kwargs): ''' Initializes the datastores :param host: The host to connect to :param port: The port to connect to :param prefix: A prefix for the keys ''' host = kwargs.get('host', 'localhost') port = kwargs.get('port', 6379) self.prefix = kwargs.get('prefix', 'pymodbus') self.client = kwargs.get('client', redis.Redis(host=host, port=port)) self.__build_mapping() def __str__(self): ''' Returns a string representation of the context :returns: A string representation of the context ''' return "Redis Slave Context %s" % self.client def reset(self): ''' Resets all the datastores to their default values ''' self.client.flushall() def validate(self, fx, address, count=1): ''' Validates the request to make sure it is in range :param fx: The function we are working with :param address: The starting address :param count: The number of values to test :returns: True if the request in within range, False otherwise ''' address = address + 1 # section 4.4 of specification _logger.debug("validate[%d] %d:%d" % (fx, address, count)) return self.__val_callbacks[self.decode(fx)](address, count) def getValues(self, fx, address, count=1): ''' Validates the request to make sure it is in range :param fx: The function we are working with :param address: The starting address :param count: The number of values to retrieve :returns: The requested values from a:a+c ''' address = address + 1 # section 4.4 of specification _logger.debug("getValues[%d] %d:%d" % (fx, address, count)) return self.__get_callbacks[self.decode(fx)](address, count) def setValues(self, fx, address, values): ''' Sets the datastore with the supplied values :param fx: The function we are working with :param address: The starting address :param values: The new values to be set ''' address = address + 1 # section 4.4 of specification _logger.debug("setValues[%d] %d:%d" % (fx, address, len(values))) self.__set_callbacks[self.decode(fx)](address, values) #--------------------------------------------------------------------------# # Redis Helper Methods #--------------------------------------------------------------------------# def __get_prefix(self, key): ''' This is a helper to abstract getting bit values :param key: The key prefix to use :returns: The key prefix to redis ''' return "%s:%s" % (self.prefix, key) def __build_mapping(self): ''' A quick helper method to build the function code mapper. ''' self.__val_callbacks = { 'd' : lambda o, c: self.__val_bit('d', o, c), 'c' : lambda o, c: self.__val_bit('c', o, c), 'h' : lambda o, c: self.__val_reg('h', o, c), 'i' : lambda o, c: self.__val_reg('i', o, c), } self.__get_callbacks = { 'd' : lambda o, c: self.__get_bit('d', o, c), 'c' : lambda o, c: self.__get_bit('c', o, c), 'h' : lambda o, c: self.__get_reg('h', o, c), 'i' : lambda o, c: self.__get_reg('i', o, c), } self.__set_callbacks = { 'd' : lambda o, v: self.__set_bit('d', o, v), 'c' : lambda o, v: self.__set_bit('c', o, v), 'h' : lambda o, v: self.__set_reg('h', o, v), 'i' : lambda o, v: self.__set_reg('i', o, v), } #--------------------------------------------------------------------------# # Redis discrete implementation #--------------------------------------------------------------------------# __bit_size = 16 __bit_default = '\x00' * (__bit_size % 8) def __get_bit_values(self, key, offset, count): ''' This is a helper to abstract getting bit values :param key: The key prefix to use :param offset: The address offset to start at :param count: The number of bits to read ''' key = self.__get_prefix(key) s = divmod(offset, self.__bit_size)[0] e = divmod(offset + count, self.__bit_size)[0] request = ('%s:%s' % (key, v) for v in range(s, e + 1)) response = self.client.mget(request) return response def __val_bit(self, key, offset, count): ''' Validates that the given range is currently set in redis. If any of the keys return None, then it is invalid. :param key: The key prefix to use :param offset: The address offset to start at :param count: The number of bits to read ''' response = self.__get_bit_values(key, offset, count) return None not in response def __get_bit(self, key, offset, count): ''' :param key: The key prefix to use :param offset: The address offset to start at :param count: The number of bits to read ''' response = self.__get_bit_values(key, offset, count) response = (r or self.__bit_default for r in response) result = ''.join(response) result = unpack_bitstring(result) return result[offset:offset + count] def __set_bit(self, key, offset, values): ''' :param key: The key prefix to use :param offset: The address offset to start at :param values: The values to set ''' count = len(values) s = divmod(offset, self.__bit_size)[0] e = divmod(offset + count, self.__bit_size)[0] value = pack_bitstring(values) current = self.__get_bit_values(key, offset, count) current = (r or self.__bit_default for r in current) current = ''.join(current) current = current[0:offset] + value + current[offset + count:] final = (current[s:s + self.__bit_size] for s in range(0, count, self.__bit_size)) key = self.__get_prefix(key) request = ('%s:%s' % (key, v) for v in range(s, e + 1)) request = dict(zip(request, final)) self.client.mset(request) #--------------------------------------------------------------------------# # Redis register implementation #--------------------------------------------------------------------------# __reg_size = 16 __reg_default = '\x00' * (__reg_size % 8) def __get_reg_values(self, key, offset, count): ''' This is a helper to abstract getting register values :param key: The key prefix to use :param offset: The address offset to start at :param count: The number of bits to read ''' key = self.__get_prefix(key) #s = divmod(offset, self.__reg_size)[0] #e = divmod(offset+count, self.__reg_size)[0] #request = ('%s:%s' % (key, v) for v in range(s, e + 1)) request = ('%s:%s' % (key, v) for v in range(offset, count + 1)) response = self.client.mget(request) return response def __val_reg(self, key, offset, count): ''' Validates that the given range is currently set in redis. If any of the keys return None, then it is invalid. :param key: The key prefix to use :param offset: The address offset to start at :param count: The number of bits to read ''' response = self.__get_reg_values(key, offset, count) return None not in response def __get_reg(self, key, offset, count): ''' :param key: The key prefix to use :param offset: The address offset to start at :param count: The number of bits to read ''' response = self.__get_reg_values(key, offset, count) response = [r or self.__reg_default for r in response] return response[offset:offset + count] def __set_reg(self, key, offset, values): ''' :param key: The key prefix to use :param offset: The address offset to start at :param values: The values to set ''' count = len(values) #s = divmod(offset, self.__reg_size) #e = divmod(offset+count, self.__reg_size) #current = self.__get_reg_values(key, offset, count) key = self.__get_prefix(key) request = ('%s:%s' % (key, v) for v in range(offset, count + 1)) request = dict(zip(request, values)) self.client.mset(request) pymodbus/examples/contrib/message-generator.py0000755000175500017550000001712212607272152021724 0ustar debacledebacle#!/usr/bin/env python ''' Modbus Message Generator -------------------------------------------------------------------------- The following is an example of how to generate example encoded messages for the supplied modbus format: * tcp - `./generate-messages.py -f tcp -m rx -b` * ascii - `./generate-messages.py -f ascii -m tx -a` * rtu - `./generate-messages.py -f rtu -m rx -b` * binary - `./generate-messages.py -f binary -m tx -b` ''' from optparse import OptionParser #--------------------------------------------------------------------------# # import all the available framers #--------------------------------------------------------------------------# from pymodbus.transaction import ModbusSocketFramer from pymodbus.transaction import ModbusBinaryFramer from pymodbus.transaction import ModbusAsciiFramer from pymodbus.transaction import ModbusRtuFramer #--------------------------------------------------------------------------# # import all available messages #--------------------------------------------------------------------------# from pymodbus.bit_read_message import * from pymodbus.bit_write_message import * from pymodbus.diag_message import * from pymodbus.file_message import * from pymodbus.other_message import * from pymodbus.mei_message import * from pymodbus.register_read_message import * from pymodbus.register_write_message import * #--------------------------------------------------------------------------# # initialize logging #--------------------------------------------------------------------------# import logging modbus_log = logging.getLogger("pymodbus") #--------------------------------------------------------------------------# # enumerate all request messages #--------------------------------------------------------------------------# _request_messages = [ ReadHoldingRegistersRequest, ReadDiscreteInputsRequest, ReadInputRegistersRequest, ReadCoilsRequest, WriteMultipleCoilsRequest, WriteMultipleRegistersRequest, WriteSingleRegisterRequest, WriteSingleCoilRequest, ReadWriteMultipleRegistersRequest, ReadExceptionStatusRequest, GetCommEventCounterRequest, GetCommEventLogRequest, ReportSlaveIdRequest, ReadFileRecordRequest, WriteFileRecordRequest, MaskWriteRegisterRequest, ReadFifoQueueRequest, ReadDeviceInformationRequest, ReturnQueryDataRequest, RestartCommunicationsOptionRequest, ReturnDiagnosticRegisterRequest, ChangeAsciiInputDelimiterRequest, ForceListenOnlyModeRequest, ClearCountersRequest, ReturnBusMessageCountRequest, ReturnBusCommunicationErrorCountRequest, ReturnBusExceptionErrorCountRequest, ReturnSlaveMessageCountRequest, ReturnSlaveNoResponseCountRequest, ReturnSlaveNAKCountRequest, ReturnSlaveBusyCountRequest, ReturnSlaveBusCharacterOverrunCountRequest, ReturnIopOverrunCountRequest, ClearOverrunCountRequest, GetClearModbusPlusRequest, ] #--------------------------------------------------------------------------# # enumerate all response messages #--------------------------------------------------------------------------# _response_messages = [ ReadHoldingRegistersResponse, ReadDiscreteInputsResponse, ReadInputRegistersResponse, ReadCoilsResponse, WriteMultipleCoilsResponse, WriteMultipleRegistersResponse, WriteSingleRegisterResponse, WriteSingleCoilResponse, ReadWriteMultipleRegistersResponse, ReadExceptionStatusResponse, GetCommEventCounterResponse, GetCommEventLogResponse, ReportSlaveIdResponse, ReadFileRecordResponse, WriteFileRecordResponse, MaskWriteRegisterResponse, ReadFifoQueueResponse, ReadDeviceInformationResponse, ReturnQueryDataResponse, RestartCommunicationsOptionResponse, ReturnDiagnosticRegisterResponse, ChangeAsciiInputDelimiterResponse, ForceListenOnlyModeResponse, ClearCountersResponse, ReturnBusMessageCountResponse, ReturnBusCommunicationErrorCountResponse, ReturnBusExceptionErrorCountResponse, ReturnSlaveMessageCountResponse, ReturnSlaveNoReponseCountResponse, ReturnSlaveNAKCountResponse, ReturnSlaveBusyCountResponse, ReturnSlaveBusCharacterOverrunCountResponse, ReturnIopOverrunCountResponse, ClearOverrunCountResponse, GetClearModbusPlusResponse, ] #--------------------------------------------------------------------------# # build an arguments singleton #--------------------------------------------------------------------------# # Feel free to override any values here to generate a specific message # in question. It should be noted that many argument names are reused # between different messages, and a number of messages are simply using # their default values. #--------------------------------------------------------------------------# _arguments = { 'address' : 0x12, 'count' : 0x08, 'value' : 0x01, 'values' : [0x01] * 8, 'read_address' : 0x12, 'read_count' : 0x08, 'write_address ' : 0x12, 'write_registers' : [0x01] * 8, 'transaction' : 0x01, 'protocol' : 0x00, 'unit' : 0x01, } #---------------------------------------------------------------------------# # generate all the requested messages #---------------------------------------------------------------------------# def generate_messages(framer, options): ''' A helper method to parse the command line options :param framer: The framer to encode the messages with :param options: The message options to use ''' messages = _request_messages if options.messages == 'tx' else _response_messages for message in messages: message = message(**_arguments) print "%-44s = " % message.__class__.__name__, packet = framer.buildPacket(message) if not options.ascii: packet = packet.encode('hex') + '\n' print packet, # because ascii ends with a \r\n #---------------------------------------------------------------------------# # initialize our program settings #---------------------------------------------------------------------------# def get_options(): ''' A helper method to parse the command line options :returns: The options manager ''' parser = OptionParser() parser.add_option("-f", "--framer", help="The type of framer to use (tcp, rtu, binary, ascii)", dest="framer", default="tcp") parser.add_option("-D", "--debug", help="Enable debug tracing", action="store_true", dest="debug", default=False) parser.add_option("-a", "--ascii", help="The indicates that the message is ascii", action="store_true", dest="ascii", default=True) parser.add_option("-b", "--binary", help="The indicates that the message is binary", action="store_false", dest="ascii") parser.add_option("-m", "--messages", help="The messages to encode (rx, tx)", dest="messages", default='rx') (opt, arg) = parser.parse_args() return opt def main(): ''' The main runner function ''' option = get_options() if option.debug: try: modbus_log.setLevel(logging.DEBUG) logging.basicConfig() except Exception, e: print "Logging is not supported on this system" framer = lookup = { 'tcp': ModbusSocketFramer, 'rtu': ModbusRtuFramer, 'binary': ModbusBinaryFramer, 'ascii': ModbusAsciiFramer, }.get(option.framer, ModbusSocketFramer)(None) generate_messages(framer, option) if __name__ == "__main__": main() pymodbus/examples/contrib/modbus_mapper.py0000644000175500017550000002516312607272152021152 0ustar debacledebacle''' Given a modbus mapping file, this is used to generate decoder blocks so that non-programmers can define the register values and then decode a modbus device all without having to write a line of code for decoding. Currently supported formats are: * csv * json * xml Here is an example of generating and using a mapping decoder (note that this is still in the works and will be greatly simplified in the final api; it is just an example of the requested functionality):: from modbus_mapper import csv_mapping_parser from modbus_mapper import mapping_decoder from pymodbus.client.sync import ModbusTcpClient from pymodbus.payload import BinaryModbusDecoder template = ['address', 'size', 'function', 'name', 'description'] raw_mapping = csv_mapping_parser('input.csv', template) mapping = mapping_decoder(raw_mapping) index, size = 1, 100 client = ModbusTcpClient('localhost') response = client.read_holding_registers(index, size) decoder = BinaryModbusDecoder.fromRegisters(response.registers) while index < size: print "[{}]\t{}".format(i, mapping[i]['type'](decoder)) index += mapping[i]['size'] Also, using the same input mapping parsers, we can generate populated slave contexts that can be run behing a modbus server:: from modbus_mapper import csv_mapping_parser from modbus_mapper import modbus_context_decoder from pymodbus.client.ssync import StartTcpServer from pymodbus.datastore.context import ModbusServerContext template = ['address', 'value', 'function', 'name', 'description'] raw_mapping = csv_mapping_parser('input.csv', template) slave_context = modbus_context_decoder(raw_mapping) context = ModbusServerContext(slaves=slave_context, single=True) StartTcpServer(context) ''' import csv import json from collections import defaultdict from StringIO import StringIO from tokenize import generate_tokens from pymodbus.payload import BinaryPayloadDecoder from pymodbus.datastore.store import ModbusSparseDataBlock from pymodbus.datastore.context import ModbusSlaveContext #---------------------------------------------------------------------------# # raw mapping input parsers #---------------------------------------------------------------------------# # These generate the raw mapping_blocks from some form of input # which can then be passed to the decoder in question to supply # the requested output result. #---------------------------------------------------------------------------# def csv_mapping_parser(path, template): ''' Given a csv file of the the mapping data for a modbus device, return a mapping layout that can be used to decode an new block. .. note:: For the template, a few values are required to be defined: address, size, function, and type. All the remaining values will be stored, but not formatted by the application. So for example:: template = ['address', 'type', 'size', 'name', 'function'] mappings = json_mapping_parser('mapping.json', template) :param path: The path to the csv input file :param template: The row value template :returns: The decoded csv dictionary ''' mapping_blocks = defaultdict(dict) with open(path, 'r') as handle: reader = csv.reader(handle) reader.next() # skip the csv header for row in reader: mapping = dict(zip(template, row)) fid = mapping.pop('function') aid = int(mapping['address']) mapping_blocks[aid] = mapping return mapping_blocks def json_mapping_parser(path, template): ''' Given a json file of the the mapping data for a modbus device, return a mapping layout that can be used to decode an new block. .. note:: For the template, a few values are required to be mapped: address, size, and type. All the remaining values will be stored, but not formatted by the application. So for example:: template = { 'Start': 'address', 'DataType': 'type', 'Length': 'size' # the remaining keys will just pass through } mappings = json_mapping_parser('mapping.json', template) :param path: The path to the csv input file :param template: The row value template :returns: The decoded csv dictionary ''' mapping_blocks = {} with open(path, 'r') as handle: for tid, rows in json.load(handle).iteritems(): mappings = {} for key, values in rows.iteritems(): mapping = {template.get(k, k) : v for k, v in values.iteritems()} mappings[int(key)] = mapping mapping_blocks[tid] = mappings return mapping_blocks def xml_mapping_parser(path): ''' Given an xml file of the the mapping data for a modbus device, return a mapping layout that can be used to decode an new block. .. note:: The input of the xml file is defined as follows:: :param path: The path to the xml input file :returns: The decoded csv dictionary ''' pass #---------------------------------------------------------------------------# # modbus context decoders #---------------------------------------------------------------------------# # These are used to decode a raw mapping_block into a slave context with # populated function data blocks. #---------------------------------------------------------------------------# def modbus_context_decoder(mapping_blocks): ''' Given a mapping block input, generate a backing slave context with initialized data blocks. .. note:: This expects the following for each block: address, value, and function where function is one of di (discretes), co (coils), hr (holding registers), or ir (input registers). :param mapping_blocks: The mapping blocks :returns: The initialized modbus slave context ''' blocks = defaultdict(dict) for block in mapping_blocks.itervalues(): for mapping in block.itervalues(): value = int(mapping['value']) address = int(mapping['address']) function = mapping['function'] blocks[function][address] = value return ModbusSlaveContext(**blocks) #---------------------------------------------------------------------------# # modbus mapping decoder #---------------------------------------------------------------------------# # These are used to decode a raw mapping_block into a request decoder. # So this allows one to simply grab a number of registers, and then # pass them to this decoder which will do the rest. #---------------------------------------------------------------------------# class ModbusTypeDecoder(object): ''' This is a utility to determine the correct decoder to use given a type name. By default this supports all the types available in the default modbus decoder, however this can easily be extended this class and adding new types to the mapper:: class CustomTypeDecoder(ModbusTypeDecoder): def __init__(self): ModbusTypeDecode.__init__(self) self.mapper['type-token'] = self.callback def parse_my_bitfield(self, tokens): return lambda d: d.decode_my_type() ''' def __init__(self): ''' Initializes a new instance of the decoder ''' self.default = lambda m: self.parse_16bit_uint self.parsers = { 'uint': self.parse_16bit_uint, 'uint8': self.parse_8bit_uint, 'uint16': self.parse_16bit_uint, 'uint32': self.parse_32bit_uint, 'uint64': self.parse_64bit_uint, 'int': self.parse_16bit_int, 'int8': self.parse_8bit_int, 'int16': self.parse_16bit_int, 'int32': self.parse_32bit_int, 'int64': self.parse_64bit_int, 'float': self.parse_32bit_float, 'float32': self.parse_32bit_float, 'float64': self.parse_64bit_float, 'string': self.parse_32bit_int, 'bits': self.parse_bits, } #------------------------------------------------------------ # Type parsers #------------------------------------------------------------ def parse_string(self, tokens): _ = tokens.next() size = int(tokens.next()) return lambda d: d.decode_string(size=size) def parse_bits(self, tokens): return lambda d: d.decode_bits() def parse_8bit_uint(self, tokens): return lambda d: d.decode_8bit_uint() def parse_16bit_uint(self, tokens): return lambda d: d.decode_16bit_uint() def parse_32bit_uint(self, tokens): return lambda d: d.decode_32bit_uint() def parse_64bit_uint(self, tokens): return lambda d: d.decode_64bit_uint() def parse_8bit_int(self, tokens): return lambda d: d.decode_8bit_int() def parse_16bit_int(self, tokens): return lambda d: d.decode_16bit_int() def parse_32bit_int(self, tokens): return lambda d: d.decode_32bit_int() def parse_64bit_int(self, tokens): return lambda d: d.decode_64bit_int() def parse_32bit_float(self, tokens): return lambda d: d.decode_32bit_float() def parse_64bit_float(self, tokens): return lambda d: d.decode_64bit_float() #------------------------------------------------------------ # Public Interface #------------------------------------------------------------ def tokenize(self, value): ''' Given a value, return the tokens :param value: The value to tokenize :returns: A token generator ''' tokens = generate_tokens(StringIO(value).readline) for toknum, tokval, _, _, _ in tokens: yield tokval def parse(self, value): ''' Given a type value, return a function that supplied with a decoder, will decode the correct value. :param value: The type of value to parse :returns: The decoder method to use ''' tokens = self.tokenize(value) token = tokens.next().lower() parser = self.parsers.get(token, self.default) return parser(tokens) def mapping_decoder(mapping_blocks, decoder=None): ''' Given the raw mapping blocks, convert them into modbus value decoder map. :param mapping_blocks: The mapping blocks :param decoder: The type decoder to use ''' decoder = decoder or ModbusTypeDecoder() for block in mapping_blocks.itervalues(): for mapping in block.itervalues(): mapping['address'] = int(mapping['address']) mapping['size'] = int(mapping['size']) mapping['type'] = decoder.parse(mapping['type']) pymodbus/examples/contrib/modbus-scraper.py0000755000175500017550000002424312607272152021244 0ustar debacledebacle#!/usr/bin/env python ''' This is a simple scraper that can be pointed at a modbus device to pull down all its values and store them as a collection of sequential data blocks. ''' import pickle from optparse import OptionParser from twisted.internet import serialport, reactor from twisted.internet.protocol import ClientFactory from pymodbus.datastore import ModbusSequentialDataBlock from pymodbus.datastore import ModbusSlaveContext from pymodbus.factory import ClientDecoder from pymodbus.client.async import ModbusClientProtocol #--------------------------------------------------------------------------# # Configure the client logging #--------------------------------------------------------------------------# import logging log = logging.getLogger("pymodbus") #---------------------------------------------------------------------------# # Choose the framer you want to use #---------------------------------------------------------------------------# from pymodbus.transaction import ModbusBinaryFramer from pymodbus.transaction import ModbusAsciiFramer from pymodbus.transaction import ModbusRtuFramer from pymodbus.transaction import ModbusSocketFramer #---------------------------------------------------------------------------# # Define some constants #---------------------------------------------------------------------------# COUNT = 8 # The number of bits/registers to read at once DELAY = 0 # The delay between subsequent reads SLAVE = 0x01 # The slave unit id to read from #---------------------------------------------------------------------------# # A simple scraper protocol #---------------------------------------------------------------------------# # I tried to spread the load across the device, but feel free to modify the # logic to suit your own purpose. #---------------------------------------------------------------------------# class ScraperProtocol(ModbusClientProtocol): def __init__(self, framer, endpoint): ''' Initializes our custom protocol :param framer: The decoder to use to process messages :param endpoint: The endpoint to send results to ''' ModbusClientProtocol.__init__(self, framer) self.endpoint = endpoint def connectionMade(self): ''' Callback for when the client has connected to the remote server. ''' super(ScraperProtocol, self).connectionMade() log.debug("Beginning the processing loop") self.address = self.factory.starting reactor.callLater(DELAY, self.scrape_holding_registers) def connectionLost(self, reason): ''' Callback for when the client disconnects from the server. :param reason: The reason for the disconnection ''' reactor.callLater(DELAY, reactor.stop) def scrape_holding_registers(self): ''' Defer fetching holding registers ''' log.debug("reading holding registers: %d" % self.address) d = self.read_holding_registers(self.address, count=COUNT, unit=SLAVE) d.addCallbacks(self.scrape_discrete_inputs, self.error_handler) def scrape_discrete_inputs(self, response): ''' Defer fetching holding registers ''' log.debug("reading discrete inputs: %d" % self.address) self.endpoint.write((3, self.address, response.registers)) d = self.read_discrete_inputs(self.address, count=COUNT, unit=SLAVE) d.addCallbacks(self.scrape_input_registers, self.error_handler) def scrape_input_registers(self, response): ''' Defer fetching holding registers ''' log.debug("reading discrete inputs: %d" % self.address) self.endpoint.write((2, self.address, response.bits)) d = self.read_input_registers(self.address, count=COUNT, unit=SLAVE) d.addCallbacks(self.scrape_coils, self.error_handler) def scrape_coils(self, response): ''' Write values of holding registers, defer fetching coils :param response: The response to process ''' log.debug("reading coils: %d" % self.address) self.endpoint.write((4, self.address, response.registers)) d = self.read_coils(self.address, count=COUNT, unit=SLAVE) d.addCallbacks(self.start_next_cycle, self.error_handler) def start_next_cycle(self, response): ''' Write values of coils, trigger next cycle :param response: The response to process ''' log.debug("starting next round: %d" % self.address) self.endpoint.write((1, self.address, response.bits)) self.address += COUNT if self.address >= self.factory.ending: self.endpoint.finalize() self.transport.loseConnection() else: reactor.callLater(DELAY, self.scrape_holding_registers) def error_handler(self, failure): ''' Handle any twisted errors :param failure: The error to handle ''' log.error(failure) #---------------------------------------------------------------------------# # a factory for the example protocol #---------------------------------------------------------------------------# # This is used to build client protocol's if you tie into twisted's method # of processing. It basically produces client instances of the underlying # protocol:: # # Factory(Protocol) -> ProtocolInstance # # It also persists data between client instances (think protocol singelton). #---------------------------------------------------------------------------# class ScraperFactory(ClientFactory): protocol = ScraperProtocol def __init__(self, framer, endpoint, query): ''' Remember things necessary for building a protocols ''' self.framer = framer self.endpoint = endpoint self.starting, self.ending = query def buildProtocol(self, _): ''' Create a protocol and start the reading cycle ''' protocol = self.protocol(self.framer, self.endpoint) protocol.factory = self return protocol #---------------------------------------------------------------------------# # a custom client for our device #---------------------------------------------------------------------------# # Twisted provides a number of helper methods for creating and starting # clients: # - protocol.ClientCreator # - reactor.connectTCP # # How you start your client is really up to you. #---------------------------------------------------------------------------# class SerialModbusClient(serialport.SerialPort): def __init__(self, factory, *args, **kwargs): ''' Setup the client and start listening on the serial port :param factory: The factory to build clients with ''' protocol = factory.buildProtocol(None) self.decoder = ClientDecoder() serialport.SerialPort.__init__(self, protocol, *args, **kwargs) #---------------------------------------------------------------------------# # a custom endpoint for our results #---------------------------------------------------------------------------# # An example line reader, this can replace with: # - the TCP protocol # - a context recorder # - a database or file recorder #---------------------------------------------------------------------------# class LoggingContextReader(object): def __init__(self, output): ''' Initialize a new instance of the logger :param output: The output file to save to ''' self.output = output self.context = ModbusSlaveContext( di = ModbusSequentialDataBlock.create(), co = ModbusSequentialDataBlock.create(), hr = ModbusSequentialDataBlock.create(), ir = ModbusSequentialDataBlock.create()) def write(self, response): ''' Handle the next modbus response :param response: The response to process ''' log.info("Read Data: %s" % str(response)) fx, address, values = response self.context.setValues(fx, address, values) def finalize(self): with open(self.output, "w") as handle: pickle.dump(self.context, handle) #--------------------------------------------------------------------------# # Main start point #--------------------------------------------------------------------------# def get_options(): ''' A helper method to parse the command line options :returns: The options manager ''' parser = OptionParser() parser.add_option("-o", "--output", help="The resulting output file for the scrape", dest="output", default="datastore.pickle") parser.add_option("-p", "--port", help="The port to connect to", type='int', dest="port", default=502) parser.add_option("-s", "--server", help="The server to scrape", dest="host", default="127.0.0.1") parser.add_option("-r", "--range", help="The address range to scan", dest="query", default="0:1000") parser.add_option("-d", "--debug", help="Enable debug tracing", action="store_true", dest="debug", default=False) (opt, arg) = parser.parse_args() return opt def main(): ''' The main runner function ''' options = get_options() if options.debug: try: log.setLevel(logging.DEBUG) logging.basicConfig() except Exception, ex: print "Logging is not supported on this system" # split the query into a starting and ending range query = [int(p) for p in options.query.split(':')] try: log.debug("Initializing the client") framer = ModbusSocketFramer(ClientDecoder()) reader = LoggingContextReader(options.output) factory = ScraperFactory(framer, reader, query) # how to connect based on TCP vs Serial clients if isinstance(framer, ModbusSocketFramer): reactor.connectTCP(options.host, options.port, factory) else: SerialModbusClient(factory, options.port, reactor) log.debug("Starting the client") reactor.run() log.debug("Finished scraping the client") except Exception, ex: print ex #---------------------------------------------------------------------------# # Main jumper #---------------------------------------------------------------------------# if __name__ == "__main__": main() pymodbus/examples/contrib/README.rst0000644000175500017550000000325212607272152017425 0ustar debacledebacle============================================================ Contributed Implementations ============================================================ There are a few example implementations of custom utilities interacting with the pymodbus library just to show what is possible. ------------------------------------------------------------ SqlAlchemy Database Datastore Backend ------------------------------------------------------------ This module allows one to use any database available through the sqlalchemy package as a datastore for the modbus server. This could be useful to have many servers who have data they agree upon and is transactional. ------------------------------------------------------------ Redis Datastore Backend ------------------------------------------------------------ This module allows one to use redis as a modbus server datastore backend. This achieves the same thing as the sqlalchemy backend, however, it is much more lightweight and easier to set up. ------------------------------------------------------------ Binary Coded Decimal Payload ------------------------------------------------------------ This module allows one to write binary coded decimal data to the modbus server using the payload encoder/decoder interfaces. ------------------------------------------------------------ Message Generator and Parser ------------------------------------------------------------ These are two utilities that can be used to create a number of modbus messages for any of the available protocols as well as to decode the messages and print descriptive text about them. Also included are a number of request and response messages in tx-messages and rx-messages. pymodbus/examples/contrib/thread_safe_datastore.py0000644000175500017550000002010412607272152022616 0ustar debacledebacleimport threading from contextlib import contextmanager from pymodbus.datastore.store import BaseModbusDataBlock class ContextWrapper(object): ''' This is a simple wrapper around enter and exit functions that conforms to the pyhton context manager protocol: with ContextWrapper(enter, leave): do_something() ''' def __init__(self, enter=None, leave=None, factory=None): self._enter = enter self._leave = leave self._factory = factory def __enter__(self): if self.enter: self._enter() return self if not self._factory else self._factory() def __exit__(self, args): if self._leave: self._leave() class ReadWriteLock(object): ''' This reader writer lock gurantees write order, but not read order and is generally biased towards allowing writes if they are available to prevent starvation. TODO: * allow user to choose between read/write/random biasing - currently write biased - read biased allow N readers in queue - random is 50/50 choice of next ''' def __init__(self): ''' Initializes a new instance of the ReadWriteLock ''' self.queue = [] # the current writer queue self.lock = threading.Lock() # the underlying condition lock self.read_condition = threading.Condition(self.lock) # the single reader condition self.readers = 0 # the number of current readers self.writer = False # is there a current writer def __is_pending_writer(self): return (self.writer # if there is a current writer or (self.queue # or if there is a waiting writer and (self.queue[0] != self.read_condition))) # or if the queue head is not a reader def acquire_reader(self): ''' Notifies the lock that a new reader is requesting the underlying resource. ''' with self.lock: if self.__is_pending_writer(): # if there are existing writers waiting if self.read_condition not in self.queue: # do not pollute the queue with readers self.queue.append(self.read_condition) # add the readers in line for the queue while self.__is_pending_writer(): # until the current writer is finished self.read_condition.wait(1) # wait on our condition if self.queue and self.read_condition == self.queue[0]: # if the read condition is at the queue head self.queue.pop(0) # then go ahead and remove it self.readers += 1 # update the current number of readers def acquire_writer(self): ''' Notifies the lock that a new writer is requesting the underlying resource. ''' with self.lock: if self.writer or self.readers: # if we need to wait on a writer or readers condition = threading.Condition(self.lock) # create a condition just for this writer self.queue.append(condition) # and put it on the waiting queue while self.writer or self.readers: # until the write lock is free condition.wait(1) # wait on our condition self.queue.pop(0) # remove our condition after our condition is met self.writer = True # stop other writers from operating def release_reader(self): ''' Notifies the lock that an existing reader is finished with the underlying resource. ''' with self.lock: self.readers = max(0, self.readers - 1) # readers should never go below 0 if not self.readers and self.queue: # if there are no active readers self.queue[0].notify_all() # then notify any waiting writers def release_writer(self): ''' Notifies the lock that an existing writer is finished with the underlying resource. ''' with self.lock: self.writer = False # give up current writing handle if self.queue: # if someone is waiting in the queue self.queue[0].notify_all() # wake them up first else: self.read_condition.notify_all() # otherwise wake up all possible readers @contextmanager def get_reader_lock(self): ''' Wrap some code with a reader lock using the python context manager protocol:: with rwlock.get_reader_lock(): do_read_operation() ''' try: self.acquire_reader() yield self finally: self.release_reader() @contextmanager def get_writer_lock(self): ''' Wrap some code with a writer lock using the python context manager protocol:: with rwlock.get_writer_lock(): do_read_operation() ''' try: self.acquire_writer() yield self finally: self.release_writer() class ThreadSafeDataBlock(BaseModbusDataBlock): ''' This is a simple decorator for a data block. This allows a user to inject an existing data block which can then be safely operated on from multiple cocurrent threads. It should be noted that the choice was made to lock around the datablock instead of the manager as there is less source of contention (writes can occur to slave 0x01 while reads can occur to slave 0x02). ''' def __init__(self, block): ''' Initialize a new thread safe decorator :param block: The block to decorate ''' self.rwlock = ReadWriteLock() self.block = block def validate(self, address, count=1): ''' Checks to see if the request is in range :param address: The starting address :param count: The number of values to test for :returns: True if the request in within range, False otherwise ''' with self.rwlock.get_reader_lock(): return self.block.validate(address, count) def getValues(self, address, count=1): ''' Returns the requested values of the datastore :param address: The starting address :param count: The number of values to retrieve :returns: The requested values from a:a+c ''' with self.rwlock.get_reader_lock(): return self.block.getValues(address, count) def setValues(self, address, values): ''' Sets the requested values of the datastore :param address: The starting address :param values: The new values to be set ''' with self.rwlock.get_writer_lock(): return self.block.setValues(address, values) if __name__ == "__main__": class AtomicCounter(object): def __init__(self, **kwargs): self.counter = kwargs.get('start', 0) self.finish = kwargs.get('finish', 1000) self.lock = threading.Lock() def increment(self, count=1): with self.lock: self.counter += count def is_running(self): return self.counter <= self.finish locker = ReadWriteLock() readers, writers = AtomicCounter(), AtomicCounter() def read(): while writers.is_running() and readers.is_running(): with locker.get_reader_lock(): readers.increment() def write(): while writers.is_running() and readers.is_running(): with locker.get_writer_lock(): writers.increment() rthreads = [threading.Thread(target=read) for i in range(50)] wthreads = [threading.Thread(target=write) for i in range(2)] for t in rthreads + wthreads: t.start() for t in rthreads + wthreads: t.join() print "readers[%d] writers[%d]" % (readers.counter, writers.counter) pymodbus/examples/contrib/concurrent-client.py0000755000175500017550000002323612607272152021755 0ustar debacledebacle#!/usr/bin/env python ''' Concurrent Modbus Client --------------------------------------------------------------------------- This is an example of writing a high performance modbus client that allows a high level of concurrency by using worker threads/processes to handle writing/reading from one or more client handles at once. ''' #--------------------------------------------------------------------------# # import system libraries #--------------------------------------------------------------------------# import multiprocessing import threading import logging import time import itertools from collections import namedtuple # we are using the future from the concurrent.futures released with # python3. Alternatively we will try the backported library:: # pip install futures try: from concurrent.futures import Future except ImportError: from futures import Future #--------------------------------------------------------------------------# # import neccessary modbus libraries #--------------------------------------------------------------------------# from pymodbus.client.common import ModbusClientMixin #--------------------------------------------------------------------------# # configure the client logging #--------------------------------------------------------------------------# import logging log = logging.getLogger("pymodbus") log.setLevel(logging.DEBUG) logging.basicConfig() #--------------------------------------------------------------------------# # Initialize out concurrency primitives #--------------------------------------------------------------------------# class _Primitives(object): ''' This is a helper class used to group the threading primitives depending on the type of worker situation we want to run (threads or processes). ''' def __init__(self, **kwargs): self.queue = kwargs.get('queue') self.event = kwargs.get('event') self.worker = kwargs.get('worker') @classmethod def create(klass, in_process=False): ''' Initialize a new instance of the concurrency primitives. :param in_process: True for threaded, False for processes :returns: An initialized instance of concurrency primitives ''' if in_process: from Queue import Queue from threading import Thread from threading import Event return klass(queue=Queue, event=Event, worker=Thread) else: from multiprocessing import Queue from multiprocessing import Event from multiprocessing import Process return klass(queue=Queue, event=Event, worker=Process) #--------------------------------------------------------------------------# # Define our data transfer objects #--------------------------------------------------------------------------# # These will be used to serialize state between the various workers. # We use named tuples here as they are very lightweight while giving us # all the benefits of classes. #--------------------------------------------------------------------------# WorkRequest = namedtuple('WorkRequest', 'request, work_id') WorkResponse = namedtuple('WorkResponse', 'is_exception, work_id, response') #--------------------------------------------------------------------------# # Define our worker processes #--------------------------------------------------------------------------# def _client_worker_process(factory, input_queue, output_queue, is_shutdown): ''' This worker process takes input requests, issues them on its client handle, and then sends the client response (success or failure) to the manager to deliver back to the application. It should be noted that there are N of these workers and they can be run in process or out of process as all the state serializes. :param factory: A client factory used to create a new client :param input_queue: The queue to pull new requests to issue :param output_queue: The queue to place client responses :param is_shutdown: Condition variable marking process shutdown ''' log.info("starting up worker : %s", threading.current_thread()) client = factory() while not is_shutdown.is_set(): try: workitem = input_queue.get(timeout=1) log.debug("dequeue worker request: %s", workitem) if not workitem: continue try: log.debug("executing request on thread: %s", workitem) result = client.execute(workitem.request) output_queue.put(WorkResponse(False, workitem.work_id, result)) except Exception, exception: log.exception("error in worker thread: %s", threading.current_thread()) output_queue.put(WorkResponse(True, workitem.work_id, exception)) except Exception, ex: pass log.info("request worker shutting down: %s", threading.current_thread()) def _manager_worker_process(output_queue, futures, is_shutdown): ''' This worker process manages taking output responses and tying them back to the future keyed on the initial transaction id. Basically this can be thought of as the delivery worker. It should be noted that there are one of these threads and it must be an in process thread as the futures will not serialize across processes.. :param output_queue: The queue holding output results to return :param futures: The mapping of tid -> future :param is_shutdown: Condition variable marking process shutdown ''' log.info("starting up manager worker: %s", threading.current_thread()) while not is_shutdown.is_set(): try: workitem = output_queue.get() future = futures.get(workitem.work_id, None) log.debug("dequeue manager response: %s", workitem) if not future: continue if workitem.is_exception: future.set_exception(workitem.response) else: future.set_result(workitem.response) log.debug("updated future result: %s", future) del futures[workitem.work_id] except Exception, ex: log.exception("error in manager") log.info("manager worker shutting down: %s", threading.current_thread()) #--------------------------------------------------------------------------# # Define our concurrent client #--------------------------------------------------------------------------# class ConcurrentClient(ModbusClientMixin): ''' This is a high performance client that can be used to read/write a large number of reqeusts at once asyncronously. This operates with a backing worker pool of processes or threads to achieve its performance. ''' def __init__(self, **kwargs): ''' Initialize a new instance of the client ''' worker_count = kwargs.get('count', multiprocessing.cpu_count()) self.factory = kwargs.get('factory') primitives = _Primitives.create(kwargs.get('in_process', False)) self.is_shutdown = primitives.event() # condition marking process shutdown self.input_queue = primitives.queue() # input requests to process self.output_queue = primitives.queue() # output results to return self.futures = {} # mapping of tid -> future self.workers = [] # handle to our worker threads self.counter = itertools.count() # creating the response manager self.manager = threading.Thread(target=_manager_worker_process, args=(self.output_queue, self.futures, self.is_shutdown)) self.manager.start() self.workers.append(self.manager) # creating the request workers for i in range(worker_count): worker = primitives.worker(target=_client_worker_process, args=(self.factory, self.input_queue, self.output_queue, self.is_shutdown)) worker.start() self.workers.append(worker) def shutdown(self): ''' Shutdown all the workers being used to concurrently process the requests. ''' log.info("stating to shut down workers") self.is_shutdown.set() self.output_queue.put(WorkResponse(None, None, None)) # to wake up the manager for worker in self.workers: worker.join() log.info("finished shutting down workers") def execute(self, request): ''' Given a request, enqueue it to be processed and then return a future linked to the response of the call. :param request: The request to execute :returns: A future linked to the call's response ''' future, work_id = Future(), self.counter.next() self.input_queue.put(WorkRequest(request, work_id)) self.futures[work_id] = future return future def execute_silently(self, request): ''' Given a write request, enqueue it to be processed without worrying about calling the application back (fire and forget) :param request: The request to execute ''' self.input_queue.put(WorkRequest(request, None)) if __name__ == "__main__": from pymodbus.client.sync import ModbusTcpClient def client_factory(): log.debug("creating client for: %s", threading.current_thread()) client = ModbusTcpClient('127.0.0.1', port=5020) client.connect() return client client = ConcurrentClient(factory = client_factory) try: log.info("issuing concurrent requests") futures = [client.read_coils(i * 8, 8) for i in range(10)] log.info("waiting on futures to complete") for future in futures: log.info("future result: %s", future.result(timeout=1)) finally: client.shutdown() pymodbus/examples/contrib/rx-messages0000644000175500017550000001421512607272152020120 0ustar debacledebacle# ------------------------------------------------------------ # What follows is a collection of encoded messages that can # be used to test the message-parser. Simply uncomment the # messages you want decoded and run the message parser with # the given arguments. What follows is the listing of messages # that are encoded in each format: # # - ReadHoldingRegistersResponse # - ReadDiscreteInputsResponse # - ReadInputRegistersResponse # - ReadCoilsResponse # - WriteMultipleCoilsResponse # - WriteMultipleRegistersResponse # - WriteSingleRegisterResponse # - WriteSingleCoilResponse # - ReadWriteMultipleRegistersResponse # - ReadExceptionStatusResponse # - GetCommEventCounterResponse # - GetCommEventLogResponse # - ReportSlaveIdResponse # - ReadFileRecordResponse # - WriteFileRecordResponse # - MaskWriteRegisterResponse # - ReadFifoQueueResponse # - ReadDeviceInformationResponse # - ReturnQueryDataResponse # - RestartCommunicationsOptionResponse # - ReturnDiagnosticRegisterResponse # - ChangeAsciiInputDelimiterResponse # - ForceListenOnlyModeResponse # - ClearCountersResponse # - ReturnBusMessageCountResponse # - ReturnBusCommunicationErrorCountResponse # - ReturnBusExceptionErrorCountResponse # - ReturnSlaveMessageCountResponse # - ReturnSlaveNoReponseCountResponse # - ReturnSlaveNAKCountResponse # - ReturnSlaveBusyCountResponse # - ReturnSlaveBusCharacterOverrunCountResponse # - ReturnIopOverrunCountResponse # - ClearOverrunCountResponse # - GetClearModbusPlusResponse # ------------------------------------------------------------ # Modbus TCP Messages # ------------------------------------------------------------ # [ MBAP Header ] [ Function Code] [ Data ] # [ tid ][ pid ][ length ][ uid ] # 2b 2b 2b 1b 1b Nb # # ./message-parser -b -p tcp -f messages # ------------------------------------------------------------ #00010000001301031000010001000100010001000100010001 #000100000004010201ff #00010000001301041000010001000100010001000100010001 #000100000004010101ff #000100000006010f00120008 #000100000006011000120008 #000100000006010600120001 #00010000000601050012ff00 #00010000001301171000010001000100010001000100010001 #000100000003010700 #000100000006010b00000008 #000100000009010c06000000000000 #00010000000501110300ff #000100000003011400 #000100000003011500 #00010000000801160012ffff0000 #00010000001601180012001000010001000100010001000100010001 #000100000008012b0e0183000000 #000100000006010800000000 #000100000006010800010000 #000100000006010800020000 #000100000006010800030000 #00010000000401080004 #0001000000060108000a0000 #0001000000060108000b0000 #0001000000060108000c0000 #0001000000060108000d0000 #0001000000060108000e0000 #0001000000060108000f0000 #000100000006010800100000 #000100000006010800110000 #000100000006010800120000 #000100000006010800130000 #000100000006010800140000 #000100000006010800150000 # ------------------------------------------------------------ # Modbus RTU Messages # ------------------------------------------------------------ # [Address ][ Function Code] [ Data ][ CRC ] # 1b 1b Nb 2b # # ./message-parser -b -p rtu -f messages # ------------------------------------------------------------ #0103100001000100010001000100010001000193b4 #010201ffe1c8 #0104100001000100010001000100010001000122c1 #010101ff11c8 #010f00120008f408 #01100012000861ca #010600120001e80f #01050012ff002c3f #01171000010001000100010001000100010001d640 #0107002230 #010b00000008a5cd #010c060000000000006135 #01110300ffacbc #0114002f00 #0115002e90 #01160012ffff00004e21 #01180012001000010001000100010001000100010001d74d #012b0e01830000000faf #010800000000e00b #010800010000b1cb #01080002000041cb #010800030000100b #0108000481d9 #0108000a0000c009 #0108000b000091c9 #0108000c00002008 #0108000d000071c8 #0108000e000081c8 #0108000f0000d008 #010800100000e1ce #010800110000b00e #010800120000400e #01080013000011ce #010800140000a00f #010800150000f1cf # ------------------------------------------------------------ # Modbus ASCII Messages # ------------------------------------------------------------ # [ Start ][Address ][ Function ][ Data ][ LRC ][ End ] # 1c 2c 2c Nc 2c 2c # # ./message-parser -a -p ascii -f messages # ------------------------------------------------------------ #:01031000010001000100010001000100010001E4 #:010201FFFD #:01041000010001000100010001000100010001E3 #:010101FFFE #:010F00120008D6 #:011000120008D5 #:010600120001E6 #:01050012FF00E9 #:01171000010001000100010001000100010001D0 #:010700F8 #:010B00000008EC #:010C06000000000000ED #:01110300FFEC #:011400EB #:011500EA #:01160012FFFF0000D9 #:01180012001000010001000100010001000100010001BD #:012B0E018300000042 #:010800000000F7 #:010800010000F6 #:010800020000F5 #:010800030000F4 #:01080004F3 #:0108000A0000ED #:0108000B0000EC #:0108000C0000EB #:0108000D0000EA #:0108000E0000E9 #:0108000F0000E8 #:010800100000E7 #:010800110000E6 #:010800120000E5 #:010800130000E4 #:010800140000E3 #:010800150000E2 # ------------------------------------------------------------ # Modbus Binary Messages # ------------------------------------------------------------ # [ Start ][Address ][ Function ][ Data ][ CRC ][ End ] # 1b 1b 1b Nb 2b 1b # # ./message-parser -b -p binary -f messages # ------------------------------------------------------------ #7b0103100001000100010001000100010001000193b47d #7b010201ffe1c87d #7b0104100001000100010001000100010001000122c17d #7b010101ff11c87d #7b010f00120008f4087d #7b01100012000861ca7d #7b010600120001e80f7d #7b01050012ff002c3f7d #7b01171000010001000100010001000100010001d6407d #7b01070022307d #7b010b00000008a5cd7d #7b010c0600000000000061357d #7b01110300ffacbc7d #7b0114002f007d #7b0115002e907d #7b01160012ffff00004e217d #7b01180012001000010001000100010001000100010001d74d7d #7b012b0e01830000000faf7d #7b010800000000e00b7d #7b010800010000b1cb7d #7b01080002000041cb7d #7b010800030000100b7d #7b0108000481d97d #7b0108000a0000c0097d #7b0108000b000091c97d #7b0108000c000020087d #7b0108000d000071c87d #7b0108000e000081c87d #7b0108000f0000d0087d #7b010800100000e1ce7d #7b010800110000b00e7d #7b010800120000400e7d #7b01080013000011ce7d #7b010800140000a00f7d #7b010800150000f1cf7d pymodbus/examples/contrib/remote_server_context.py0000644000175500017550000001644212607272152022742 0ustar debacledebacle''' Although there is a remote server context already in the main library, it works under the assumption that users would have a server context of the following form:: server_context = { 0x00: client('host1.something.com'), 0x01: client('host2.something.com'), 0x02: client('host3.something.com') } This example is how to create a server context where the client is pointing to the same host, but the requested slave id is used as the slave for the client:: server_context = { 0x00: client('host1.something.com', 0x00), 0x01: client('host1.something.com', 0x01), 0x02: client('host1.something.com', 0x02) } ''' from pymodbus.exceptions import NotImplementedException from pymodbus.interfaces import IModbusSlaveContext #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Slave Context #---------------------------------------------------------------------------# # Basically we create a new slave context for the given slave identifier so # that this slave context will only make requests to that slave with the # client that the server is maintaining. #---------------------------------------------------------------------------# class RemoteSingleSlaveContext(IModbusSlaveContext): ''' This is a remote server context that allows one to create a server context backed by a single client that may be attached to many slave units. This can be used to effectively create a modbus forwarding server. ''' def __init__(self, context, unit_id): ''' Initializes the datastores :param context: The underlying context to operate with :param unit_id: The slave that this context will contact ''' self.context = context self.unit_id = unit_id def reset(self): ''' Resets all the datastores to their default values ''' raise NotImplementedException() def validate(self, fx, address, count=1): ''' Validates the request to make sure it is in range :param fx: The function we are working with :param address: The starting address :param count: The number of values to test :returns: True if the request in within range, False otherwise ''' _logger.debug("validate[%d] %d:%d" % (fx, address, count)) result = context.get_callbacks[self.decode(fx)](address, count, self.unit_id) return result.function_code < 0x80 def getValues(self, fx, address, count=1): ''' Validates the request to make sure it is in range :param fx: The function we are working with :param address: The starting address :param count: The number of values to retrieve :returns: The requested values from a:a+c ''' _logger.debug("get values[%d] %d:%d" % (fx, address, count)) result = context.get_callbacks[self.decode(fx)](address, count, self.unit_id) return self.__extract_result(self.decode(fx), result) def setValues(self, fx, address, values): ''' Sets the datastore with the supplied values :param fx: The function we are working with :param address: The starting address :param values: The new values to be set ''' _logger.debug("set values[%d] %d:%d" % (fx, address, len(values))) context.set_callbacks[self.decode(fx)](address, values, self.unit_id) def __str__(self): ''' Returns a string representation of the context :returns: A string representation of the context ''' return "Remote Single Slave Context(%s)" % self.unit_id def __extract_result(self, fx, result): ''' A helper method to extract the values out of a response. The future api should make the result consistent so we can just call `result.getValues()`. :param fx: The function to call :param result: The resulting data ''' if result.function_code < 0x80: if fx in ['d', 'c']: return result.bits if fx in ['h', 'i']: return result.registers else: return result #---------------------------------------------------------------------------# # Server Context #---------------------------------------------------------------------------# # Think of this as simply a dictionary of { unit_id: client(req, unit_id) } #---------------------------------------------------------------------------# class RemoteServerContext(object): ''' This is a remote server context that allows one to create a server context backed by a single client that may be attached to many slave units. This can be used to effectively create a modbus forwarding server. ''' def __init__(self, client): ''' Initializes the datastores :param client: The client to retrieve values with ''' self.get_callbacks = { 'd': lambda a, c, s: client.read_discrete_inputs(a, c, s), 'c': lambda a, c, s: client.read_coils(a, c, s), 'h': lambda a, c, s: client.read_holding_registers(a, c, s), 'i': lambda a, c, s: client.read_input_registers(a, c, s), } self.set_callbacks = { 'd': lambda a, v, s: client.write_coils(a, v, s), 'c': lambda a, v, s: client.write_coils(a, v, s), 'h': lambda a, v, s: client.write_registers(a, v, s), 'i': lambda a, v, s: client.write_registers(a, v, s), } self.slaves = {} # simply a cache def __str__(self): ''' Returns a string representation of the context :returns: A string representation of the context ''' return "Remote Server Context(%s)" % self._client def __iter__(self): ''' Iterater over the current collection of slave contexts. :returns: An iterator over the slave contexts ''' # note, this may not include all slaves return self.__slaves.iteritems() def __contains__(self, slave): ''' Check if the given slave is in this list :param slave: slave The slave to check for existance :returns: True if the slave exists, False otherwise ''' # we don't want to check the cache here as the # slave may not exist yet or may not exist any # more. The best thing to do is try and fail. return True def __setitem__(self, slave, context): ''' Used to set a new slave context :param slave: The slave context to set :param context: The new context to set for this slave ''' raise NotImplementedException() # doesn't make sense here def __delitem__(self, slave): ''' Wrapper used to access the slave context :param slave: The slave context to remove ''' raise NotImplementedException() # doesn't make sense here def __getitem__(self, slave): ''' Used to get access to a slave context :param slave: The slave context to get :returns: The requested slave context ''' if slave not in self.slaves: self.slaves[slave] = RemoteSingleSlaveContext(self, slave) return self.slaves[slave] pymodbus/examples/contrib/tx-messages0000644000175500017550000001360212607272152020121 0ustar debacledebacle# ------------------------------------------------------------ # What follows is a collection of encoded messages that can # be used to test the message-parser. Simply uncomment the # messages you want decoded and run the message parser with # the given arguments. What follows is the listing of messages # that are encoded in each format: # # - ReadHoldingRegistersRequest # - ReadDiscreteInputsRequest # - ReadInputRegistersRequest # - ReadCoilsRequest # - WriteMultipleCoilsRequest # - WriteMultipleRegistersRequest # - WriteSingleRegisterRequest # - WriteSingleCoilRequest # - ReadWriteMultipleRegistersRequest # - ReadExceptionStatusRequest # - GetCommEventCounterRequest # - GetCommEventLogRequest # - ReportSlaveIdRequest # - ReadFileRecordRequest # - WriteFileRecordRequest # - MaskWriteRegisterRequest # - ReadFifoQueueRequest # - ReadDeviceInformationRequest # - ReturnQueryDataRequest # - RestartCommunicationsOptionRequest # - ReturnDiagnosticRegisterRequest # - ChangeAsciiInputDelimiterRequest # - ForceListenOnlyModeRequest # - ClearCountersRequest # - ReturnBusMessageCountRequest # - ReturnBusCommunicationErrorCountRequest # - ReturnBusExceptionErrorCountRequest # - ReturnSlaveMessageCountRequest # - ReturnSlaveNoReponseCountRequest # - ReturnSlaveNAKCountRequest # - ReturnSlaveBusyCountRequest # - ReturnSlaveBusCharacterOverrunCountRequest # - ReturnIopOverrunCountRequest # - ClearOverrunCountRequest # - GetClearModbusPlusRequest # ------------------------------------------------------------ # Modbus TCP Messages # ------------------------------------------------------------ # [ MBAP Header ] [ Function Code] [ Data ] # [ tid ][ pid ][ length ][ uid ] # 2b 2b 2b 1b 1b Nb # # ./message-parser -b -p tcp -f messages # ------------------------------------------------------------ #000100000006010300120008 #000100000006010200120008 #000100000006010400120008 #000100000006010100120008 #000100000008010f0012000801ff #0001000000170110001200081000010001000100010001000100010001 #000100000006010600120001 #00010000000601050012ff00 #00010000001b011700120008000000081000010001000100010001000100010001 #0001000000020107 #000100000002010b #000100000002010c #0001000000020111 #000100000003011400 #000100000003011500 #00010000000801160012ffff0000 #00010000000401180012 #000100000005012b0e0100 #000100000006010800000000 #000100000006010800010000 #000100000006010800020000 #000100000006010800030000 #000100000006010800040000 #0001000000060108000a0000 #0001000000060108000b0000 #0001000000060108000c0000 #0001000000060108000d0000 #0001000000060108000e0000 #0001000000060108000f0000 #000100000006010800100000 #000100000006010800110000 #000100000006010800120000 #000100000006010800130000 #000100000006010800140000 #000100000006010800150000 # ------------------------------------------------------------ # Modbus RTU Messages # ------------------------------------------------------------ # [Address ][ Function Code] [ Data ][ CRC ] # 1b 1b Nb 2b # # ./message-parser -b -p rtu -f messages # ------------------------------------------------------------ #010300120008e409 #010200120008d9c9 #01040012000851c9 #0101001200089dc9 #010f0012000801ff06d6 #0110001200081000010001000100010001000100010001d551 #010600120001e80f #01050012ff002c3f #011700120008000000081000010001000100010001000100010001e6f8 #010741e2 #010b41e7 #010c0025 #0111c02c #0114002f00 #0115002e90 #01160012ffff00004e21 #0118001201d2 #012b0e01007077 #010800000000e00b #010800010000b1cb #01080002000041cb #010800030000100b #010800040000a1ca #0108000a0000c009 #0108000b000091c9 #0108000c00002008 #0108000d000071c8 #0108000e000081c8 #0108000f0000d008 #010800100000e1ce #010800110000b00e #010800120000400e #01080013000011ce #010800140000a00f #010800150000f1cf # ------------------------------------------------------------ # Modbus ASCII Messages # ------------------------------------------------------------ # [ Start ][Address ][ Function ][ Data ][ LRC ][ End ] # 1c 2c 2c Nc 2c 2c # # ./message-parser -a -p ascii -f messages # ------------------------------------------------------------ #:010300120008E2 #:010200120008E3 #:010400120008E1 #:010100120008E4 #:010F0012000801FFD6 #:0110001200081000010001000100010001000100010001BD #:010600120001E6 #:01050012FF00E9 #:011700120008000000081000010001000100010001000100010001AE #:0107F8 #:010BF4 #:010CF3 #:0111EE #:011400EB #:011500EA #:01160012FFFF0000D9 #:01180012D5 #:012B0E0100C5 #:010800000000F7 #:010800010000F6 #:010800020000F5 #:010800030000F4 #:010800040000F3 #:0108000A0000ED #:0108000B0000EC #:0108000C0000EB #:0108000D0000EA #:0108000E0000E9 #:0108000F0000E8 #:010800100000E7 #:010800110000E6 #:010800120000E5 #:010800130000E4 #:010800140000E3 #:010800150000E2 # ------------------------------------------------------------ # Modbus Binary Messages # ------------------------------------------------------------ # [ Start ][Address ][ Function ][ Data ][ CRC ][ End ] # 1b 1b 1b Nb 2b 1b # # ./message-parser -b -p binary -f messages # ------------------------------------------------------------ #7b010300120008e4097d #7b010200120008d9c97d #7b01040012000851c97d #7b0101001200089dc97d #7b010f0012000801ff06d67d #7b0110001200081000010001000100010001000100010001d5517d #7b010600120001e80f7d #7b01050012ff002c3f7d #7b011700120008000000081000010001000100010001000100010001e6f87d #7b010741e27d #7b010b41e77d #7b010c00257d #7b0111c02c7d #7b0114002f007d #7b0115002e907d #7b01160012ffff00004e217d #7b0118001201d27d #7b012b0e010070777d #7b010800000000e00b7d #7b010800010000b1cb7d #7b01080002000041cb7d #7b010800030000100b7d #7b010800040000a1ca7d #7b0108000a0000c0097d #7b0108000b000091c97d #7b0108000c000020087d #7b0108000d000071c87d #7b0108000e000081c87d #7b0108000f0000d0087d #7b010800100000e1ce7d #7b010800110000b00e7d #7b010800120000400e7d #7b01080013000011ce7d #7b010800140000a00f7d #7b010800150000f1cf7d pymodbus/examples/contrib/message-parser.py0000755000175500017550000001350212607272152021230 0ustar debacledebacle#!/usr/bin/env python ''' Modbus Message Parser -------------------------------------------------------------------------- The following is an example of how to parse modbus messages using the supplied framers for a number of protocols: * tcp * ascii * rtu * binary ''' #---------------------------------------------------------------------------# # import needed libraries #---------------------------------------------------------------------------# import sys import collections import textwrap from optparse import OptionParser from pymodbus.utilities import computeCRC, computeLRC from pymodbus.factory import ClientDecoder, ServerDecoder from pymodbus.transaction import ModbusSocketFramer from pymodbus.transaction import ModbusBinaryFramer from pymodbus.transaction import ModbusAsciiFramer from pymodbus.transaction import ModbusRtuFramer #--------------------------------------------------------------------------# # Logging #--------------------------------------------------------------------------# import logging modbus_log = logging.getLogger("pymodbus") #---------------------------------------------------------------------------# # build a quick wrapper around the framers #---------------------------------------------------------------------------# class Decoder(object): def __init__(self, framer, encode=False): ''' Initialize a new instance of the decoder :param framer: The framer to use :param encode: If the message needs to be encoded ''' self.framer = framer self.encode = encode def decode(self, message): ''' Attempt to decode the supplied message :param message: The messge to decode ''' value = message if self.encode else message.encode('hex') print "="*80 print "Decoding Message %s" % value print "="*80 decoders = [ self.framer(ServerDecoder()), self.framer(ClientDecoder()), ] for decoder in decoders: print "%s" % decoder.decoder.__class__.__name__ print "-"*80 try: decoder.addToFrame(message) if decoder.checkFrame(): decoder.advanceFrame() decoder.processIncomingPacket(message, self.report) else: self.check_errors(decoder, message) except Exception, ex: self.check_errors(decoder, message) def check_errors(self, decoder, message): ''' Attempt to find message errors :param message: The message to find errors in ''' pass def report(self, message): ''' The callback to print the message information :param message: The message to print ''' print "%-15s = %s" % ('name', message.__class__.__name__) for k,v in message.__dict__.iteritems(): if isinstance(v, dict): print "%-15s =" % k for kk,vv in v.items(): print " %-12s => %s" % (kk, vv) elif isinstance(v, collections.Iterable): print "%-15s =" % k value = str([int(x) for x in v]) for line in textwrap.wrap(value, 60): print "%-15s . %s" % ("", line) else: print "%-15s = %s" % (k, hex(v)) print "%-15s = %s" % ('documentation', message.__doc__) #---------------------------------------------------------------------------# # and decode our message #---------------------------------------------------------------------------# def get_options(): ''' A helper method to parse the command line options :returns: The options manager ''' parser = OptionParser() parser.add_option("-p", "--parser", help="The type of parser to use (tcp, rtu, binary, ascii)", dest="parser", default="tcp") parser.add_option("-D", "--debug", help="Enable debug tracing", action="store_true", dest="debug", default=False) parser.add_option("-m", "--message", help="The message to parse", dest="message", default=None) parser.add_option("-a", "--ascii", help="The indicates that the message is ascii", action="store_true", dest="ascii", default=True) parser.add_option("-b", "--binary", help="The indicates that the message is binary", action="store_false", dest="ascii") parser.add_option("-f", "--file", help="The file containing messages to parse", dest="file", default=None) (opt, arg) = parser.parse_args() if not opt.message and len(arg) > 0: opt.message = arg[0] return opt def get_messages(option): ''' A helper method to generate the messages to parse :param options: The option manager :returns: The message iterator to parse ''' if option.message: if not option.ascii: option.message = option.message.decode('hex') yield option.message elif option.file: with open(option.file, "r") as handle: for line in handle: if line.startswith('#'): continue if not option.ascii: line = line.strip() line = line.decode('hex') yield line def main(): ''' The main runner function ''' option = get_options() if option.debug: try: modbus_log.setLevel(logging.DEBUG) logging.basicConfig() except Exception, e: print "Logging is not supported on this system" framer = lookup = { 'tcp': ModbusSocketFramer, 'rtu': ModbusRtuFramer, 'binary': ModbusBinaryFramer, 'ascii': ModbusAsciiFramer, }.get(option.parser, ModbusSocketFramer) decoder = Decoder(framer, option.ascii) for message in get_messages(option): decoder.decode(message) if __name__ == "__main__": main() pymodbus/examples/contrib/libmodbus-client.py0000755000175500017550000004212212607272152021546 0ustar debacledebacle#!/usr/bin/env python ''' Libmodbus Protocol Wrapper ------------------------------------------------------------ What follows is an example wrapper of the libmodbus library (http://libmodbus.org/documentation/) for use with pymodbus. There are two utilities involved here: * LibmodbusLevel1Client This is simply a python wrapper around the c library. It is mostly a clone of the pylibmodbus implementation, but I plan on extending it to implement all the available protocol using the raw execute methods. * LibmodbusClient This is just another modbus client that can be used just like any other client in pymodbus. For these to work, you must have `cffi` and `libmodbus-dev` installed: sudo apt-get install libmodbus-dev pip install cffi ''' #--------------------------------------------------------------------------# # import system libraries #--------------------------------------------------------------------------# from cffi import FFI #--------------------------------------------------------------------------# # import pymodbus libraries #--------------------------------------------------------------------------# from pymodbus.constants import Defaults from pymodbus.exceptions import ModbusException from pymodbus.client.common import ModbusClientMixin from pymodbus.bit_read_message import ReadCoilsResponse, ReadDiscreteInputsResponse from pymodbus.register_read_message import ReadHoldingRegistersResponse, ReadInputRegistersResponse from pymodbus.register_read_message import ReadWriteMultipleRegistersResponse from pymodbus.bit_write_message import WriteSingleCoilResponse, WriteMultipleCoilsResponse from pymodbus.register_write_message import WriteSingleRegisterResponse, WriteMultipleRegistersResponse #-------------------------------------------------------------------------------- # create the C interface #-------------------------------------------------------------------------------- # * TODO add the protocol needed for the servers #-------------------------------------------------------------------------------- compiler = FFI() compiler.cdef(""" typedef struct _modbus modbus_t; int modbus_connect(modbus_t *ctx); int modbus_flush(modbus_t *ctx); void modbus_close(modbus_t *ctx); const char *modbus_strerror(int errnum); int modbus_set_slave(modbus_t *ctx, int slave); void modbus_get_response_timeout(modbus_t *ctx, uint32_t *to_sec, uint32_t *to_usec); void modbus_set_response_timeout(modbus_t *ctx, uint32_t to_sec, uint32_t to_usec); int modbus_read_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest); int modbus_read_input_bits(modbus_t *ctx, int addr, int nb, uint8_t *dest); int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest); int modbus_read_input_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest); int modbus_write_bit(modbus_t *ctx, int coil_addr, int status); int modbus_write_bits(modbus_t *ctx, int addr, int nb, const uint8_t *data); int modbus_write_register(modbus_t *ctx, int reg_addr, int value); int modbus_write_registers(modbus_t *ctx, int addr, int nb, const uint16_t *data); int modbus_write_and_read_registers(modbus_t *ctx, int write_addr, int write_nb, const uint16_t *src, int read_addr, int read_nb, uint16_t *dest); int modbus_mask_write_register(modbus_t *ctx, int addr, uint16_t and_mask, uint16_t or_mask); int modbus_send_raw_request(modbus_t *ctx, uint8_t *raw_req, int raw_req_length); float modbus_get_float(const uint16_t *src); void modbus_set_float(float f, uint16_t *dest); modbus_t* modbus_new_tcp(const char *ip_address, int port); modbus_t* modbus_new_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit); void modbus_free(modbus_t *ctx); int modbus_receive(modbus_t *ctx, uint8_t *req); int modbus_receive_from(modbus_t *ctx, int sockfd, uint8_t *req); int modbus_receive_confirmation(modbus_t *ctx, uint8_t *rsp); """) LIB = compiler.dlopen('modbus') # create our bindings #-------------------------------------------------------------------------------- # helper utilites #-------------------------------------------------------------------------------- def get_float(data): return LIB.modbus_get_float(data) def set_float(value, data): LIB.modbus_set_float(value, data) def cast_to_int16(data): return int(compiler.cast('int16_t', data)) def cast_to_int32(data): return int(compiler.cast('int32_t', data)) #-------------------------------------------------------------------------------- # level1 client #-------------------------------------------------------------------------------- class LibmodbusLevel1Client(object): ''' A raw wrapper around the libmodbus c library. Feel free to use it if you want increased performance and don't mind the entire protocol not being implemented. ''' @classmethod def create_tcp_client(klass, host='127.0.0.1', port=Defaults.Port): ''' Create a TCP modbus client for the supplied parameters. :param host: The host to connect to :param port: The port to connect to on that host :returns: A new level1 client ''' client = LIB.modbus_new_tcp(host.encode(), port) return klass(client) @classmethod def create_rtu_client(klass, **kwargs): ''' Create a TCP modbus client for the supplied parameters. :param port: The serial port to attach to :param stopbits: The number of stop bits to use :param bytesize: The bytesize of the serial messages :param parity: Which kind of parity to use :param baudrate: The baud rate to use for the serial device :returns: A new level1 client ''' port = kwargs.get('port', '/dev/ttyS0') baudrate = kwargs.get('baud', Defaults.Baudrate) parity = kwargs.get('parity', Defaults.Parity) bytesize = kwargs.get('bytesize', Defaults.Bytesize) stopbits = kwargs.get('stopbits', Defaults.Stopbits) client = LIB.modbus_new_rtu(port, baudrate, parity, bytesize, stopbits) return klass(client) def __init__(self, client): ''' Initalize a new instance of the LibmodbusLevel1Client. This method should not be used, instead new instances should be created using the two supplied factory methods: * LibmodbusLevel1Client.create_rtu_client(...) * LibmodbusLevel1Client.create_tcp_client(...) :param client: The underlying client instance to operate with. ''' self.client = client self.slave = Defaults.UnitId def set_slave(self, slave): ''' Set the current slave to operate against. :param slave: The new slave to operate against :returns: The resulting slave to operate against ''' self.slave = self._execute(LIB.modbus_set_slave, slave) return self.slave def connect(self): ''' Attempt to connect to the client target. :returns: True if successful, throws otherwise ''' return (self.__execute(LIB.modbus_connect) == 0) def flush(self): ''' Discards the existing bytes on the wire. :returns: The number of flushed bytes, or throws ''' return self.__execute(LIB.modbus_flush) def close(self): ''' Closes and frees the underlying connection and context structure. :returns: Always True ''' LIB.modbus_close(self.client) LIB.modbus_free(self.client) return True def __execute(self, command, *args): ''' Run the supplied command against the currently instantiated client with the supplied arguments. This will make sure to correctly handle resulting errors. :param command: The command to execute against the context :param *args: The arguments for the given command :returns: The result of the operation unless -1 which throws ''' result = command(self.client, *args) if result == -1: message = LIB.modbus_strerror(compiler.errno) raise ModbusException(compiler.string(message)) return result def read_bits(self, address, count=1): ''' :param address: The starting address to read from :param count: The number of coils to read :returns: The resulting bits ''' result = compiler.new("uint8_t[]", count) self.__execute(LIB.modbus_read_bits, address, count, result) return result def read_input_bits(self, address, count=1): ''' :param address: The starting address to read from :param count: The number of discretes to read :returns: The resulting bits ''' result = compiler.new("uint8_t[]", count) self.__execute(LIB.modbus_read_input_bits, address, count, result) return result def write_bit(self, address, value): ''' :param address: The starting address to write to :param value: The value to write to the specified address :returns: The number of written bits ''' return self.__execute(LIB.modbus_write_bit, address, value) def write_bits(self, address, values): ''' :param address: The starting address to write to :param values: The values to write to the specified address :returns: The number of written bits ''' count = len(values) return self.__execute(LIB.modbus_write_bits, address, count, values) def write_register(self, address, value): ''' :param address: The starting address to write to :param value: The value to write to the specified address :returns: The number of written registers ''' return self.__execute(LIB.modbus_write_register, address, value) def write_registers(self, address, values): ''' :param address: The starting address to write to :param values: The values to write to the specified address :returns: The number of written registers ''' count = len(values) return self.__execute(LIB.modbus_write_registers, address, count, values) def read_registers(self, address, count=1): ''' :param address: The starting address to read from :param count: The number of registers to read :returns: The resulting read registers ''' result = compiler.new("uint16_t[]", count) self.__execute(LIB.modbus_read_registers, address, count, result) return result def read_input_registers(self, address, count=1): ''' :param address: The starting address to read from :param count: The number of registers to read :returns: The resulting read registers ''' result = compiler.new("uint16_t[]", count) self.__execute(LIB.modbus_read_input_registers, address, count, result) return result def read_and_write_registers(self, read_address, read_count, write_address, write_registers): ''' :param read_address: The address to start reading from :param read_count: The number of registers to read from address :param write_address: The address to start writing to :param write_registers: The registers to write to the specified address :returns: The resulting read registers ''' write_count = len(write_registers) read_result = compiler.new("uint16_t[]", read_count) self.__execute(LIB.modbus_write_and_read_registers, write_address, write_count, write_registers, read_address, read_count, read_result) return read_result #-------------------------------------------------------------------------------- # level2 client #-------------------------------------------------------------------------------- class LibmodbusClient(ModbusClientMixin): ''' A facade around the raw level 1 libmodbus client that implements the pymodbus protocol on top of the lower level client. ''' #-----------------------------------------------------------------------# # these are used to convert from the pymodbus request types to the # libmodbus operations (overloaded operator). #-----------------------------------------------------------------------# __methods = { 'ReadCoilsRequest' : lambda c, r: c.read_bits(r.address, r.count), 'ReadDiscreteInputsRequest' : lambda c, r: c.read_input_bits(r.address, r.count), 'WriteSingleCoilRequest' : lambda c, r: c.write_bit(r.address, r.value), 'WriteMultipleCoilsRequest' : lambda c, r: c.write_bits(r.address, r.values), 'WriteSingleRegisterRequest' : lambda c, r: c.write_register(r.address, r.value), 'WriteMultipleRegistersRequest' : lambda c, r: c.write_registers(r.address, r.values), 'ReadHoldingRegistersRequest' : lambda c, r: c.read_registers(r.address, r.count), 'ReadInputRegistersRequest' : lambda c, r: c.read_input_registers(r.address, r.count), 'ReadWriteMultipleRegistersRequest' : lambda c, r: c.read_and_write_registers(r.read_address, r.read_count, r.write_address, r.write_registers), } #-----------------------------------------------------------------------# # these are used to convert from the libmodbus result to the # pymodbus response type #-----------------------------------------------------------------------# __adapters = { 'ReadCoilsRequest' : lambda tx, rx: ReadCoilsResponse(list(rx)), 'ReadDiscreteInputsRequest' : lambda tx, rx: ReadDiscreteInputsResponse(list(rx)), 'WriteSingleCoilRequest' : lambda tx, rx: WriteSingleCoilResponse(tx.address, rx), 'WriteMultipleCoilsRequest' : lambda tx, rx: WriteMultipleCoilsResponse(tx.address, rx), 'WriteSingleRegisterRequest' : lambda tx, rx: WriteSingleRegisterResponse(tx.address, rx), 'WriteMultipleRegistersRequest' : lambda tx, rx: WriteMultipleRegistersResponse(tx.address, rx), 'ReadHoldingRegistersRequest' : lambda tx, rx: ReadHoldingRegistersResponse(list(rx)), 'ReadInputRegistersRequest' : lambda tx, rx: ReadInputRegistersResponse(list(rx)), 'ReadWriteMultipleRegistersRequest' : lambda tx, rx: ReadWriteMultipleRegistersResponse(list(rx)), } def __init__(self, client): ''' Initalize a new instance of the LibmodbusClient. This should be initialized with one of the LibmodbusLevel1Client instances: * LibmodbusLevel1Client.create_rtu_client(...) * LibmodbusLevel1Client.create_tcp_client(...) :param client: The underlying client instance to operate with. ''' self.client = client #-----------------------------------------------------------------------# # We use the client mixin to implement the api methods which are all # forwarded to this method. It is implemented using the previously # defined lookup tables. Any method not defined simply throws. #-----------------------------------------------------------------------# def execute(self, request): ''' Execute the supplied request against the server. :param request: The request to process :returns: The result of the request execution ''' if self.client.slave != request.unit_id: self.client.set_slave(request.unit_id) method = request.__class__.__name__ operation = self.__methods.get(method, None) adapter = self.__adapters.get(method, None) if not operation or not adapter: raise NotImplementedException("Method not implemented: " + name) response = operation(self.client, request) return adapter(request, response) #-----------------------------------------------------------------------# # Other methods can simply be forwarded using the decorator pattern #-----------------------------------------------------------------------# def connect(self): return self.client.connect() def close(self): return self.client.close() #-----------------------------------------------------------------------# # magic methods #-----------------------------------------------------------------------# def __enter__(self): ''' Implement the client with enter block :returns: The current instance of the client ''' self.client.connect() return self def __exit__(self, klass, value, traceback): ''' Implement the client with exit block ''' self.client.close() #--------------------------------------------------------------------------# # main example runner #--------------------------------------------------------------------------# if __name__ == '__main__': # create our low level client host = '127.0.0.1' port = 502 protocol = LibmodbusLevel1Client.create_tcp_client(host, port) # operate with our high level client with LibmodbusClient(protocol) as client: registers = client.write_registers(0, [13, 12, 11]) print registers registers = client.read_holding_registers(0, 10) print registers.registers pymodbus/examples/contrib/bcd-payload.py0000644000175500017550000001525412607272152020474 0ustar debacledebacle''' Modbus BCD Payload Builder ----------------------------------------------------------- This is an example of building a custom payload builder that can be used in the pymodbus library. Below is a simple binary coded decimal builder and decoder. ''' from struct import pack, unpack from pymodbus.constants import Endian from pymodbus.interfaces import IPayloadBuilder from pymodbus.utilities import pack_bitstring from pymodbus.utilities import unpack_bitstring from pymodbus.exceptions import ParameterException def convert_to_bcd(decimal): ''' Converts a decimal value to a bcd value :param value: The decimal value to to pack into bcd :returns: The number in bcd form ''' place, bcd = 0, 0 while decimal > 0: nibble = decimal % 10 bcd += nibble << place decimal /= 10 place += 4 return bcd def convert_from_bcd(bcd): ''' Converts a bcd value to a decimal value :param value: The value to unpack from bcd :returns: The number in decimal form ''' place, decimal = 1, 0 while bcd > 0: nibble = bcd & 0xf decimal += nibble * place bcd >>= 4 place *= 10 return decimal def count_bcd_digits(bcd): ''' Count the number of digits in a bcd value :param bcd: The bcd number to count the digits of :returns: The number of digits in the bcd string ''' count = 0 while bcd > 0: count += 1 bcd >>= 4 return count class BcdPayloadBuilder(IPayloadBuilder): ''' A utility that helps build binary coded decimal payload messages to be written with the various modbus messages. example:: builder = BcdPayloadBuilder() builder.add_number(1) builder.add_number(int(2.234 * 1000)) payload = builder.build() ''' def __init__(self, payload=None, endian=Endian.Little): ''' Initialize a new instance of the payload builder :param payload: Raw payload data to initialize with :param endian: The endianess of the payload ''' self._payload = payload or [] self._endian = endian def __str__(self): ''' Return the payload buffer as a string :returns: The payload buffer as a string ''' return ''.join(self._payload) def reset(self): ''' Reset the payload buffer ''' self._payload = [] def build(self): ''' Return the payload buffer as a list This list is two bytes per element and can thus be treated as a list of registers. :returns: The payload buffer as a list ''' string = str(self) length = len(string) string = string + ('\x00' * (length % 2)) return [string[i:i+2] for i in xrange(0, length, 2)] def add_bits(self, values): ''' Adds a collection of bits to be encoded If these are less than a multiple of eight, they will be left padded with 0 bits to make it so. :param value: The value to add to the buffer ''' value = pack_bitstring(values) self._payload.append(value) def add_number(self, value, size=None): ''' Adds any 8bit numeric type to the buffer :param value: The value to add to the buffer ''' encoded = [] value = convert_to_bcd(value) size = size or count_bcd_digits(value) while size > 0: nibble = value & 0xf encoded.append(pack('B', nibble)) value >>= 4 size -= 1 self._payload.extend(encoded) def add_string(self, value): ''' Adds a string to the buffer :param value: The value to add to the buffer ''' self._payload.append(value) class BcdPayloadDecoder(object): ''' A utility that helps decode binary coded decimal payload messages from a modbus reponse message. What follows is a simple example:: decoder = BcdPayloadDecoder(payload) first = decoder.decode_int(2) second = decoder.decode_int(5) / 100 ''' def __init__(self, payload): ''' Initialize a new payload decoder :param payload: The payload to decode with ''' self._payload = payload self._pointer = 0x00 @staticmethod def fromRegisters(registers, endian=Endian.Little): ''' Initialize a payload decoder with the result of reading a collection of registers from a modbus device. The registers are treated as a list of 2 byte values. We have to do this because of how the data has already been decoded by the rest of the library. :param registers: The register results to initialize with :param endian: The endianess of the payload :returns: An initialized PayloadDecoder ''' if isinstance(registers, list): # repack into flat binary payload = ''.join(pack('>H', x) for x in registers) return BinaryPayloadDecoder(payload, endian) raise ParameterException('Invalid collection of registers supplied') @staticmethod def fromCoils(coils, endian=Endian.Little): ''' Initialize a payload decoder with the result of reading a collection of coils from a modbus device. The coils are treated as a list of bit(boolean) values. :param coils: The coil results to initialize with :param endian: The endianess of the payload :returns: An initialized PayloadDecoder ''' if isinstance(coils, list): payload = pack_bitstring(coils) return BinaryPayloadDecoder(payload, endian) raise ParameterException('Invalid collection of coils supplied') def reset(self): ''' Reset the decoder pointer back to the start ''' self._pointer = 0x00 def decode_int(self, size=1): ''' Decodes a int or long from the buffer ''' self._pointer += size handle = self._payload[self._pointer - size:self._pointer] return convert_from_bcd(handle) def decode_bits(self): ''' Decodes a byte worth of bits from the buffer ''' self._pointer += 1 handle = self._payload[self._pointer - 1:self._pointer] return unpack_bitstring(handle) def decode_string(self, size=1): ''' Decodes a string from the buffer :param size: The size of the string to decode ''' self._pointer += size return self._payload[self._pointer - size:self._pointer] #---------------------------------------------------------------------------# # Exported Identifiers #---------------------------------------------------------------------------# __all__ = ["BcdPayloadBuilder", "BcdPayloadDecoder"] pymodbus/examples/contrib/requirements.txt0000644000175500017550000000035012607272152021216 0ustar debacledebacle# ------------------------------------------------------------------- # if you want to use the custom data stores, uncomment these # ------------------------------------------------------------------- SQLAlchemy==0.7.9 redis==2.6.2 pymodbus/examples/contrib/database-datastore.py0000644000175500017550000001501012607272152022033 0ustar debacledebacleimport sqlalchemy import sqlalchemy.types as sqltypes from sqlalchemy.sql import and_ from sqlalchemy.schema import UniqueConstraint from sqlalchemy.sql.expression import bindparam from pymodbus.exceptions import NotImplementedException from pymodbus.interfaces import IModbusSlaveContext #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging; _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Context #---------------------------------------------------------------------------# class DatabaseSlaveContext(IModbusSlaveContext): ''' This creates a modbus data model with each data access stored in its own personal block ''' def __init__(self, *args, **kwargs): ''' Initializes the datastores :param kwargs: Each element is a ModbusDataBlock ''' self.table = kwargs.get('table', 'pymodbus') self.database = kwargs.get('database', 'sqlite:///pymodbus.db') self.__db_create(self.table, self.database) def __str__(self): ''' Returns a string representation of the context :returns: A string representation of the context ''' return "Modbus Slave Context" def reset(self): ''' Resets all the datastores to their default values ''' self._metadata.drop_all() self.__db_create(self.table, self.database) raise NotImplementedException() # TODO drop table? def validate(self, fx, address, count=1): ''' Validates the request to make sure it is in range :param fx: The function we are working with :param address: The starting address :param count: The number of values to test :returns: True if the request in within range, False otherwise ''' address = address + 1 # section 4.4 of specification _logger.debug("validate[%d] %d:%d" % (fx, address, count)) return self.__validate(self.decode(fx), address, count) def getValues(self, fx, address, count=1): ''' Validates the request to make sure it is in range :param fx: The function we are working with :param address: The starting address :param count: The number of values to retrieve :returns: The requested values from a:a+c ''' address = address + 1 # section 4.4 of specification _logger.debug("get-values[%d] %d:%d" % (fx, address, count)) return self.__get(self.decode(fx), address, count) def setValues(self, fx, address, values): ''' Sets the datastore with the supplied values :param fx: The function we are working with :param address: The starting address :param values: The new values to be set ''' address = address + 1 # section 4.4 of specification _logger.debug("set-values[%d] %d:%d" % (fx, address, len(values))) self.__set(self.decode(fx), address, values) #--------------------------------------------------------------------------# # Sqlite Helper Methods #--------------------------------------------------------------------------# def __db_create(self, table, database): ''' A helper method to initialize the database and handles :param table: The table name to create :param database: The database uri to use ''' self._engine = sqlalchemy.create_engine(database, echo=False) self._metadata = sqlalchemy.MetaData(self._engine) self._table = sqlalchemy.Table(table, self._metadata, sqlalchemy.Column('type', sqltypes.String(1)), sqlalchemy.Column('index', sqltypes.Integer), sqlalchemy.Column('value', sqltypes.Integer), UniqueConstraint('type', 'index', name='key')) self._table.create(checkfirst=True) self._connection = self._engine.connect() def __get(self, type, offset, count): ''' :param type: The key prefix to use :param offset: The address offset to start at :param count: The number of bits to read :returns: The resulting values ''' query = self._table.select(and_( self._table.c.type == type, self._table.c.index >= offset, self._table.c.index <= offset + count)) query = query.order_by(self._table.c.index.asc()) result = self._connection.execute(query).fetchall() return [row.value for row in result] def __build_set(self, type, offset, values, p=''): ''' A helper method to generate the sql update context :param type: The key prefix to use :param offset: The address offset to start at :param values: The values to set ''' result = [] for index, value in enumerate(values): result.append({ p + 'type' : type, p + 'index' : offset + index, 'value' : value }) return result def __set(self, type, offset, values): ''' :param key: The type prefix to use :param offset: The address offset to start at :param values: The values to set ''' context = self.__build_set(type, offset, values) query = self._table.insert() result = self._connection.execute(query, context) return result.rowcount == len(values) def __update(self, type, offset, values): ''' :param type: The type prefix to use :param offset: The address offset to start at :param values: The values to set ''' context = self.__build_set(type, offset, values, p='x_') query = self._table.update().values(name='value') query = query.where(and_( self._table.c.type == bindparam('x_type'), self._table.c.index == bindparam('x_index'))) result = self._connection.execute(query, context) return result.rowcount == len(values) def __validate(self, key, offset, count): ''' :param key: The key prefix to use :param offset: The address offset to start at :param count: The number of bits to read :returns: The result of the validation ''' query = self._table.select(and_( self._table.c.type == type, self._table.c.index >= offset, self._table.c.index <= offset + count)) result = self._connection.execute(query) return result.rowcount == count pymodbus/examples/contrib/modbus-simulator.py0000644000175500017550000001033512607272152021616 0ustar debacledebacle#!/usr/bin/env python ''' An example of creating a fully implemented modbus server with read/write data as well as user configurable base data ''' import pickle from optparse import OptionParser from twisted.internet import reactor from pymodbus.server.async import StartTcpServer from pymodbus.datastore import ModbusServerContext,ModbusSlaveContext #--------------------------------------------------------------------------# # Logging #--------------------------------------------------------------------------# import logging logging.basicConfig() server_log = logging.getLogger("pymodbus.server") protocol_log = logging.getLogger("pymodbus.protocol") #---------------------------------------------------------------------------# # Extra Global Functions #---------------------------------------------------------------------------# # These are extra helper functions that don't belong in a class #---------------------------------------------------------------------------# import getpass def root_test(): ''' Simple test to see if we are running as root ''' return True # removed for the time being as it isn't portable #return getpass.getuser() == "root" #--------------------------------------------------------------------------# # Helper Classes #--------------------------------------------------------------------------# class ConfigurationException(Exception): ''' Exception for configuration error ''' def __init__(self, string): ''' Initializes the ConfigurationException instance :param string: The message to append to the exception ''' Exception.__init__(self, string) self.string = string def __str__(self): ''' Builds a representation of the object :returns: A string representation of the object ''' return 'Configuration Error: %s' % self.string class Configuration: ''' Class used to parse configuration file and create and modbus datastore. The format of the configuration file is actually just a python pickle, which is a compressed memory dump from the scraper. ''' def __init__(self, config): ''' Trys to load a configuration file, lets the file not found exception fall through :param config: The pickled datastore ''' try: self.file = open(config, "r") except Exception: raise ConfigurationException("File not found %s" % config) def parse(self): ''' Parses the config file and creates a server context ''' handle = pickle.load(self.file) try: # test for existance, or bomb dsd = handle['di'] csd = handle['ci'] hsd = handle['hr'] isd = handle['ir'] except Exception: raise ConfigurationException("Invalid Configuration") slave = ModbusSlaveContext(d=dsd, c=csd, h=hsd, i=isd) return ModbusServerContext(slaves=slave) #--------------------------------------------------------------------------# # Main start point #--------------------------------------------------------------------------# def main(): ''' Server launcher ''' parser = OptionParser() parser.add_option("-c", "--conf", help="The configuration file to load", dest="file") parser.add_option("-D", "--debug", help="Turn on to enable tracing", action="store_true", dest="debug", default=False) (opt, arg) = parser.parse_args() # enable debugging information if opt.debug: try: server_log.setLevel(logging.DEBUG) protocol_log.setLevel(logging.DEBUG) except Exception, e: print "Logging is not supported on this system" # parse configuration file and run try: conf = Configuration(opt.file) StartTcpServer(context=conf.parse()) except ConfigurationException, err: print err parser.print_help() #---------------------------------------------------------------------------# # Main jumper #---------------------------------------------------------------------------# if __name__ == "__main__": if root_test(): main() else: print "This script must be run as root!" pymodbus/examples/contrib/modicon-payload.py0000644000175500017550000002172212607272152021371 0ustar debacledebacle''' Modbus Modicon Payload Builder ----------------------------------------------------------- This is an example of building a custom payload builder that can be used in the pymodbus library. Below is a simple modicon encoded builder and decoder. ''' from struct import pack, unpack from pymodbus.constants import Endian from pymodbus.interfaces import IPayloadBuilder from pymodbus.utilities import pack_bitstring from pymodbus.utilities import unpack_bitstring from pymodbus.exceptions import ParameterException class ModiconPayloadBuilder(IPayloadBuilder): ''' A utility that helps build modicon encoded payload messages to be written with the various modbus messages. example:: builder = ModiconPayloadBuilder() builder.add_8bit_uint(1) builder.add_16bit_uint(2) payload = builder.build() ''' def __init__(self, payload=None, endian=Endian.Little): ''' Initialize a new instance of the payload builder :param payload: Raw payload data to initialize with :param endian: The endianess of the payload ''' self._payload = payload or [] self._endian = endian def __str__(self): ''' Return the payload buffer as a string :returns: The payload buffer as a string ''' return ''.join(self._payload) def reset(self): ''' Reset the payload buffer ''' self._payload = [] def build(self): ''' Return the payload buffer as a list This list is two bytes per element and can thus be treated as a list of registers. :returns: The payload buffer as a list ''' string = str(self) length = len(string) string = string + ('\x00' * (length % 2)) return [string[i:i+2] for i in xrange(0, length, 2)] def add_bits(self, values): ''' Adds a collection of bits to be encoded If these are less than a multiple of eight, they will be left padded with 0 bits to make it so. :param value: The value to add to the buffer ''' value = pack_bitstring(values) self._payload.append(value) def add_8bit_uint(self, value): ''' Adds a 8 bit unsigned int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'B' self._payload.append(pack(fstring, value)) def add_16bit_uint(self, value): ''' Adds a 16 bit unsigned int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'H' self._payload.append(pack(fstring, value)) def add_32bit_uint(self, value): ''' Adds a 32 bit unsigned int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'I' handle = pack(fstring, value) handle = handle[2:] + handle[:2] self._payload.append(handle) def add_8bit_int(self, value): ''' Adds a 8 bit signed int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'b' self._payload.append(pack(fstring, value)) def add_16bit_int(self, value): ''' Adds a 16 bit signed int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'h' self._payload.append(pack(fstring, value)) def add_32bit_int(self, value): ''' Adds a 32 bit signed int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'i' handle = pack(fstring, value) handle = handle[2:] + handle[:2] self._payload.append(handle) def add_32bit_float(self, value): ''' Adds a 32 bit float to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'f' handle = pack(fstring, value) handle = handle[2:] + handle[:2] self._payload.append(handle) def add_string(self, value): ''' Adds a string to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 's' for c in value: self._payload.append(pack(fstring, c)) class ModiconPayloadDecoder(object): ''' A utility that helps decode modicon encoded payload messages from a modbus reponse message. What follows is a simple example:: decoder = ModiconPayloadDecoder(payload) first = decoder.decode_8bit_uint() second = decoder.decode_16bit_uint() ''' def __init__(self, payload): ''' Initialize a new payload decoder :param payload: The payload to decode with ''' self._payload = payload self._pointer = 0x00 @staticmethod def fromRegisters(registers, endian=Endian.Little): ''' Initialize a payload decoder with the result of reading a collection of registers from a modbus device. The registers are treated as a list of 2 byte values. We have to do this because of how the data has already been decoded by the rest of the library. :param registers: The register results to initialize with :param endian: The endianess of the payload :returns: An initialized PayloadDecoder ''' if isinstance(registers, list): # repack into flat binary payload = ''.join(pack('>H', x) for x in registers) return ModiconPayloadDecoder(payload, endian) raise ParameterException('Invalid collection of registers supplied') @staticmethod def fromCoils(coils, endian=Endian.Little): ''' Initialize a payload decoder with the result of reading a collection of coils from a modbus device. The coils are treated as a list of bit(boolean) values. :param coils: The coil results to initialize with :param endian: The endianess of the payload :returns: An initialized PayloadDecoder ''' if isinstance(coils, list): payload = pack_bitstring(coils) return ModiconPayloadDecoder(payload, endian) raise ParameterException('Invalid collection of coils supplied') def reset(self): ''' Reset the decoder pointer back to the start ''' self._pointer = 0x00 def decode_8bit_uint(self): ''' Decodes a 8 bit unsigned int from the buffer ''' self._pointer += 1 fstring = self._endian + 'B' handle = self._payload[self._pointer - 1:self._pointer] return unpack(fstring, handle)[0] def decode_16bit_uint(self): ''' Decodes a 16 bit unsigned int from the buffer ''' self._pointer += 2 fstring = self._endian + 'H' handle = self._payload[self._pointer - 2:self._pointer] return unpack(fstring, handle)[0] def decode_32bit_uint(self): ''' Decodes a 32 bit unsigned int from the buffer ''' self._pointer += 4 fstring = self._endian + 'I' handle = self._payload[self._pointer - 4:self._pointer] handle = handle[2:] + handle[:2] return unpack(fstring, handle)[0] def decode_8bit_int(self): ''' Decodes a 8 bit signed int from the buffer ''' self._pointer += 1 fstring = self._endian + 'b' handle = self._payload[self._pointer - 1:self._pointer] return unpack(fstring, handle)[0] def decode_16bit_int(self): ''' Decodes a 16 bit signed int from the buffer ''' self._pointer += 2 fstring = self._endian + 'h' handle = self._payload[self._pointer - 2:self._pointer] return unpack(fstring, handle)[0] def decode_32bit_int(self): ''' Decodes a 32 bit signed int from the buffer ''' self._pointer += 4 fstring = self._endian + 'i' handle = self._payload[self._pointer - 4:self._pointer] handle = handle[2:] + handle[:2] return unpack(fstring, handle)[0] def decode_32bit_float(self, size=1): ''' Decodes a float from the buffer ''' self._pointer += 4 fstring = self._endian + 'f' handle = self._payload[self._pointer - 4:self._pointer] handle = handle[2:] + handle[:2] return unpack(fstring, handle)[0] def decode_bits(self): ''' Decodes a byte worth of bits from the buffer ''' self._pointer += 1 handle = self._payload[self._pointer - 1:self._pointer] return unpack_bitstring(handle) def decode_string(self, size=1): ''' Decodes a string from the buffer :param size: The size of the string to decode ''' self._pointer += size return self._payload[self._pointer - size:self._pointer] #---------------------------------------------------------------------------# # Exported Identifiers #---------------------------------------------------------------------------# __all__ = ["BcdPayloadBuilder", "BcdPayloadDecoder"] pymodbus/examples/contrib/modbus_saver.py0000644000175500017550000001230712607272152021002 0ustar debacledebacle''' These are a collection of helper methods that can be used to save a modbus server context to file for backup, checkpointing, or any other purpose. There use is very simple:: context = server.context saver = JsonDatastoreSaver(context) saver.save() These can then be re-opened by the parsers in the modbus_mapping module. At the moment, the supported output formats are: * csv * json * xml To implement your own, simply subclass ModbusDatastoreSaver and supply the needed callbacks for your given format: * handle_store_start(self, store) * handle_store_end(self, store) * handle_slave_start(self, slave) * handle_slave_end(self, slave) * handle_save_start(self) * handle_save_end(self) ''' import csv import json import xml.etree.ElementTree as xml class ModbusDatastoreSaver(object): ''' An abstract base class that can be used to implement a persistance format for the modbus server context. In order to use it, just complete the neccessary callbacks (SAX style) that your persistance format needs. ''' def __init__(self, context, path=None): ''' Initialize a new instance of the saver. :param context: The modbus server context :param path: The output path to save to ''' self.context = context self.path = path or 'modbus-context-dump' def save(self): ''' The main runner method to save the context to file which calls the various callbacks which the sub classes will implement. ''' with open(self.path, 'w') as self.file_handle: self.handle_save_start() for slave_name, slave in self.context: self.handle_slave_start(slave_name) for store_name, store in slave.store.iteritems(): self.handle_store_start(store_name) self.handle_store_values(iter(store)) self.handle_store_end(store_name) self.handle_slave_end(slave_name) self.handle_save_end() #------------------------------------------------------------ # predefined state machine callbacks #------------------------------------------------------------ def handle_save_start(self): pass def handle_store_start(self, store): pass def handle_store_end(self, store): pass def handle_slave_start(self, slave): pass def handle_slave_end(self, slave): pass def handle_save_end(self): pass #---------------------------------------------------------------- # Implementations of the data store savers #---------------------------------------------------------------- class JsonDatastoreSaver(ModbusDatastoreSaver): ''' An implementation of the modbus datastore saver that persists the context as a json document. ''' STORE_NAMES = { 'i' : 'input-registers', 'd' : 'discretes', 'h' : 'holding-registers', 'c' : 'coils', } def handle_save_start(self): self._context = dict() def handle_slave_start(self, slave): self._context[hex(slave)] = self._slave = dict() def handle_store_start(self, store): self._store = self.STORE_NAMES[store] def handle_store_values(self, values): self._slave[self._store] = dict(values) def handle_save_end(self): json.dump(self._context, self.file_handle) class CsvDatastoreSaver(ModbusDatastoreSaver): ''' An implementation of the modbus datastore saver that persists the context as a csv document. ''' NEWLINE = '\r\n' HEADER = "slave,store,address,value" + NEWLINE STORE_NAMES = { 'i' : 'i', 'd' : 'd', 'h' : 'h', 'c' : 'c', } def handle_save_start(self): self.file_handle.write(self.HEADER) def handle_slave_start(self, slave): self._line = [str(slave)] def handle_store_start(self, store): self._line.append(self.STORE_NAMES[store]) def handle_store_values(self, values): self.file_handle.writelines(self.handle_store_value(values)) def handle_store_end(self, store): self._line.pop() def handle_store_value(self, values): for a, v in values: yield ','.join(self._line + [str(a), str(v)]) + self.NEWLINE class XmlDatastoreSaver(ModbusDatastoreSaver): ''' An implementation of the modbus datastore saver that persists the context as a XML document. ''' STORE_NAMES = { 'i' : 'input-registers', 'd' : 'discretes', 'h' : 'holding-registers', 'c' : 'coils', } def handle_save_start(self): self._context = xml.Element("context") self._root = xml.ElementTree(self._context) def handle_slave_start(self, slave): self._slave = xml.SubElement(self._context, "slave") self._slave.set("id", str(slave)) def handle_store_start(self, store): self._store = xml.SubElement(self._slave, "store") self._store.set("function", self.STORE_NAMES[store]) def handle_store_values(self, values): for address, value in values: entry = xml.SubElement(self._store, "entry") entry.text = str(value) entry.set("address", str(address)) def handle_save_end(self): self._root.write(self.file_handle) pymodbus/examples/contrib/serial-forwarder.py0000755000175500017550000000316312607272152021564 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Synchronous Serial Forwarder -------------------------------------------------------------------------- We basically set the context for the tcp serial server to be that of a serial client! This is just an example of how clever you can be with the data context (basically anything can become a modbus device). ''' #---------------------------------------------------------------------------# # import the various server implementations #---------------------------------------------------------------------------# from pymodbus.server.sync import StartTcpServer as StartServer from pymodbus.client.sync import ModbusSerialClient as ModbusClient from pymodbus.datastore.remote import RemoteSlaveContext from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext #---------------------------------------------------------------------------# # configure the service logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # initialize the datastore(serial client) #---------------------------------------------------------------------------# client = ModbusClient(method='ascii', port='/dev/pts/14') store = RemoteSlaveContext(client) context = ModbusServerContext(slaves=store, single=True) #---------------------------------------------------------------------------# # run the server you want #---------------------------------------------------------------------------# StartServer(context) pymodbus/examples/functional/0000755000175500017550000000000012607272152016436 5ustar debacledebaclepymodbus/examples/functional/synchronous-tcp-client.py0000644000175500017550000000146112607272152023444 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.client.sync import ModbusTcpClient as ModbusClient from base_runner import Runner class SynchronousTcpClient(Runner, unittest.TestCase): ''' These are the integration tests for the synchronous tcp client. ''' def setUp(self): ''' Initializes the test environment ''' self.initialize(["../tools/reference/diagslave", "-m", "tcp", "-p", "12345"]) self.client = ModbusClient(port=12345) def tearDown(self): ''' Cleans up the test environment ''' self.client.close() self.shutdown() #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/examples/functional/asynchronous-ascii-client.py0000644000175500017550000000142412607272152024106 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.client.async import ModbusSerialClient as ModbusClient from base_runner import Runner class AsynchronousAsciiClient(Runner, unittest.TestCase): ''' These are the integration tests for the asynchronous serial ascii client. ''' def setUp(self): ''' Initializes the test environment ''' super(Runner, self).setUp() self.client = ModbusClient(method='ascii') def tearDown(self): ''' Cleans up the test environment ''' self.client.close() self.shutdown() #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/examples/functional/remote-slave-context.py0000644000175500017550000000177512607272152023107 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.client.sync import ModbusTcpClient from pymodbus.datastore.remote import RemoteSlaveContext from base_context import ContextRunner class RemoteSlaveContextTest(ContextRunner, unittest.TestCase): ''' These are the integration tests for using the redis slave context. ''' def setUp(self): ''' Initializes the test environment ''' self.context = RemoteSlaveContext(client=None) # for the log statment self.initialize(["../tools/reference/diagslave", "-m", "tcp", "-p", "12345"]) self.client = ModbusTcpClient(port=12345) self.context = RemoteSlaveContext(client=self.client) def tearDown(self): ''' Cleans up the test environment ''' self.client.close() self.shutdown() #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/examples/functional/synchronous-ascii-client.py0000644000175500017550000000174412607272152023752 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.client.sync import ModbusSerialClient as ModbusClient from base_runner import Runner class SynchronousAsciiClient(Runner, unittest.TestCase): ''' These are the integration tests for the synchronous serial ascii client. ''' def setUp(self): ''' Initializes the test environment ''' super(Runner, self).setUp() # "../tools/nullmodem/linux/run", self.initialize(["../tools/reference/diagslave", "-m", "ascii", "/dev/pts/14"]) self.client = ModbusClient(method='ascii', timeout=0.2, port='/dev/pts/13') self.client.connect() def tearDown(self): ''' Cleans up the test environment ''' self.client.close() super(Runner, self).tearDown() #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/examples/functional/base_runner.py0000644000175500017550000000607712607272152021325 0ustar debacledebacleimport os import time from subprocess import Popen as execute from twisted.internet.defer import Deferred #---------------------------------------------------------------------------# # configure the client logging #---------------------------------------------------------------------------# import logging log = logging.getLogger(__name__) class Runner(object): ''' This is the base runner class for all the integration tests ''' def initialize(self, service): ''' Initializes the test environment ''' self.fnull = open(os.devnull, 'w') self.server = execute(service, stdout=self.fnull, stderr=self.fnull) log.debug("%s service started: %s", service, self.server.pid) time.sleep(0.2) def shutdown(self): ''' Cleans up the test environment ''' self.server.kill() self.fnull.close() log.debug("service stopped") def testReadWriteCoil(self): rq = self.client.write_coil(1, True) rr = self.client.read_coils(1,1) self.__validate(rq, lambda r: r.function_code < 0x80) self.__validate(rr, lambda r: r.bits[0] == True) def testReadWriteCoils(self): rq = self.client.write_coils(1, [True]*8) rr = self.client.read_coils(1,8) self.__validate(rq, lambda r: r.function_code < 0x80) self.__validate(rr, lambda r: r.bits == [True]*8) def testReadWriteDiscreteRegisters(self): rq = self.client.write_coils(1, [False]*8) rr = self.client.read_discrete_inputs(1,8) self.__validate(rq, lambda r: r.function_code < 0x80) self.__validate(rr, lambda r: r.bits == [False]*8) def testReadWriteHoldingRegisters(self): rq = self.client.write_register(1, 10) rr = self.client.read_holding_registers(1,1) self.__validate(rq, lambda r: r.function_code < 0x80) self.__validate(rr, lambda r: r.registers[0] == 10) def testReadWriteInputRegisters(self): rq = self.client.write_registers(1, [10]*8) rr = self.client.read_input_registers(1,8) self.__validate(rq, lambda r: r.function_code < 0x80) self.__validate(rr, lambda r: r.registers == [10]*8) def testReadWriteRegistersTogether(self): arguments = { 'read_address': 1, 'read_count': 8, 'write_address': 1, 'write_registers': [20]*8, } rq = self.client.readwrite_registers(**arguments) rr = self.client.read_input_registers(1,8) self.__validate(rq, lambda r: r.function_code < 0x80) self.__validate(rr, lambda r: r.registers == [20]*8) def __validate(self, result, test): ''' Validate the result whether it is a result or a deferred. :param result: The result to __validate :param callback: The test to __validate ''' if isinstance(result, Deferred): deferred.callback(lambda : self.assertTrue(test(result))) deferred.errback(lambda _: self.assertTrue(False)) else: self.assertTrue(test(result)) pymodbus/examples/functional/__init__.py0000644000175500017550000000000012607272152020535 0ustar debacledebaclepymodbus/examples/functional/README.rst0000644000175500017550000000177612607272152020140 0ustar debacledebacle============================================================================ Pymodbus Functional Tests ============================================================================ Modbus Clients --------------------------------------------------------------------------- The following can be run to validate the pymodbus clients against a running modbus instance. For these tests, the following are used as references:: * jamod * modpoll Modbus Servers --------------------------------------------------------------------------- The following can be used to create a null modem loopback for testing the serial implementations:: * tty0tty (linux) * com0com (windows) Specialized Datastores --------------------------------------------------------------------------- The following can be run to validate the pymodbus specializes datastores. For these tests, the following are used as references: * sqlite (for the database datastore) * redis (for the redis datastore) * modpoll (for the remote slave datastore) pymodbus/examples/functional/redis-slave-context.py0000644000175500017550000000161012607272152022706 0ustar debacledebacle#!/usr/bin/env python import unittest import os from subprocess import Popen as execute from pymodbus.datastore.modredis import RedisSlaveContext from base_context import ContextRunner class RedisSlaveContextTest(ContextRunner, unittest.TestCase): ''' These are the integration tests for using the redis slave context. ''' def setUp(self): ''' Initializes the test environment ''' self.context = RedisSlaveContext() # the redis client will block, so no wait needed self.initialize("redis-server") def tearDown(self): ''' Cleans up the test environment ''' self.server.kill() self.fnull.close() self.shutdown() #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/examples/functional/asynchronous-udp-client.py0000644000175500017550000000140612607272152023606 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.client.sync import ModbusUdpClient as ModbusClient from base_runner import Runner class AsynchronousUdpClient(Runner, unittest.TestCase): ''' These are the integration tests for the asynchronous udp client. ''' def setUp(self): ''' Initializes the test environment ''' super(Runner, self).setUp() self.client = ModbusClient() def tearDown(self): ''' Cleans up the test environment ''' self.client.close() super(Runner, self).tearDown() #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/examples/functional/memory-slave-context.py0000755000175500017550000000202112607272152023110 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.datastore.context import ModbusSlaveContext from pymodbus.datastore.store import ModbusSequentialDataBlock from base_context import ContextRunner class MemorySlaveContextTest(ContextRunner, unittest.TestCase): ''' These are the integration tests for using the in memory slave context. ''' def setUp(self): ''' Initializes the test environment ''' self.context = ModbusSlaveContext(**{ 'di' : ModbusSequentialDataBlock(0, [0]*100), 'co' : ModbusSequentialDataBlock(0, [0]*100), 'ir' : ModbusSequentialDataBlock(0, [0]*100), 'hr' : ModbusSequentialDataBlock(0, [0]*100)}) self.initialize() def tearDown(self): ''' Cleans up the test environment ''' self.shutdown() #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/examples/functional/database-slave-context.py0000644000175500017550000000170412607272152023350 0ustar debacledebacle#!/usr/bin/env python import unittest, os from pymodbus.datastore.database import DatabaseSlaveContext from base_context import ContextRunner class DatabaseSlaveContextTest(ContextRunner, unittest.TestCase): ''' These are the integration tests for using the redis slave context. ''' __database = 'sqlite:///pymodbus-test.db' def setUp(self): ''' Initializes the test environment ''' path = './' + self.__database.split('///')[1] if os.path.exists(path): os.remove(path) self.context = DatabaseSlaveContext(database=self.__database) self.initialize() def tearDown(self): ''' Cleans up the test environment ''' self.context._connection.close() self.shutdown() #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/examples/functional/asynchronous-tcp-client.py0000644000175500017550000000221612607272152023604 0ustar debacledebacle#!/usr/bin/env python import unittest from twisted.internet import reactor, protocol from pymodbus.constants import Defaults from pymodbus.client.async import ModbusClientProtocol from base_runner import Runner class AsynchronousTcpClient(Runner, unittest.TestCase): ''' These are the integration tests for the asynchronous tcp client. ''' def setUp(self): ''' Initializes the test environment ''' def _callback(client): self.client = client self.initialize(["../tools/reference/diagslave", "-m", "tcp", "-p", "12345"]) defer = protocol.ClientCreator(reactor, ModbusClientProtocol ).connectTCP("localhost", Defaults.Port) defer.addCallback(_callback) reactor.run() def tearDown(self): ''' Cleans up the test environment ''' reactor.callLater(1, client.transport.loseConnection) reactor.callLater(2, reactor.stop) reactor.shutdown() #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/examples/functional/synchronous-udp-client.py0000644000175500017550000000140412607272152023443 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.client.sync import ModbusUdpClient as ModbusClient from base_runner import Runner class SynchronousUdpClient(Runner, unittest.TestCase): ''' These are the integration tests for the synchronous udp client. ''' def setUp(self): ''' Initializes the test environment ''' super(Runner, self).setUp() self.client = ModbusClient() def tearDown(self): ''' Cleans up the test environment ''' self.client.close() super(Runner, self).tearDown() #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/examples/functional/base_context.py0000644000175500017550000000414212607272152021467 0ustar debacledebacleimport os import time from subprocess import Popen as execute from twisted.internet.defer import Deferred #---------------------------------------------------------------------------# # configure the client logging #---------------------------------------------------------------------------# import logging log = logging.getLogger(__name__) class ContextRunner(object): ''' This is the base runner class for all the integration tests ''' __bit_functions = [2,1] # redundant are removed for now __reg_functions = [4,3] # redundant are removed for now def initialize(self, service=None): ''' Initializes the test environment ''' if service: self.fnull = open(os.devnull, 'w') self.service = execute(service, stdout=self.fnull, stderr=self.fnull) log.debug("%s service started: %s", service, self.service.pid) time.sleep(0.2) else: self.service = None log.debug("%s context started", self.context) def shutdown(self): ''' Cleans up the test environment ''' try: if self.service: self.service.kill() self.fnull.close() self.context.reset() except: pass log.debug("%s context stopped" % self.context) def testDataContextRegisters(self): ''' Test that the context gets and sets registers ''' address = 10 values = [0x1234] * 32 for fx in self.__reg_functions: self.context.setValues(fx, address, values) result = self.context.getValues(fx, address, len(values)) self.assertEquals(len(result), len(values)) self.assertEquals(result, values) def testDataContextDiscretes(self): ''' Test that the context gets and sets discretes ''' address = 10 values = [True] * 32 for fx in self.__bit_functions: self.context.setValues(fx, address, values) result = self.context.getValues(fx, address, len(values)) self.assertEquals(len(result), len(values)) self.assertEquals(result, values) pymodbus/examples/functional/synchronous-rtu-client.py0000644000175500017550000000165712607272152023477 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.client.sync import ModbusSerialClient as ModbusClient from base_runner import Runner class SynchronousRtuClient(Runner, unittest.TestCase): ''' These are the integration tests for the synchronous serial rtu client. ''' def setUp(self): ''' Initializes the test environment ''' super(Runner, self).setUp() self.initialize(["../tools/reference/diagslave", "-m", "rtu", "/dev/pts/14"]) self.client = ModbusClient(method='rtu', timeout=0.2, port='/dev/pts/13') self.client.connect() def tearDown(self): ''' Cleans up the test environment ''' self.client.close() super(Runner, self).tearDown() #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/examples/functional/asynchronous-rtu-client.py0000644000175500017550000000143512607272152023632 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.client.async import ModbusSerialClient as ModbusClient from base_runner import Runner class AsynchronousRtuClient(Runner, unittest.TestCase): ''' These are the integration tests for the asynchronous serial rtu client. ''' def setUp(self): ''' Initializes the test environment ''' super(Runner, self).setUp() self.client = ModbusClient(method='rtu') def tearDown(self): ''' Cleans up the test environment ''' self.client.close() super(Runner, self).tearDown() #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/examples/common/0000755000175500017550000000000012607272152015564 5ustar debacledebaclepymodbus/examples/common/changing-framers.py0000755000175500017550000000512312607272152021355 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Client Framer Overload -------------------------------------------------------------------------- All of the modbus clients are designed to have pluggable framers so that the transport and protocol are decoupled. This allows a user to define or plug in their custom protocols into existing transports (like a binary framer over a serial connection). It should be noted that although you are not limited to trying whatever you would like, the library makes no gurantees that all framers with all transports will produce predictable or correct results (for example tcp transport with an RTU framer). However, please let us know of any success cases that are not documented! ''' #---------------------------------------------------------------------------# # import the modbus client and the framers #---------------------------------------------------------------------------# from pymodbus.client.sync import ModbusTcpClient as ModbusClient #---------------------------------------------------------------------------# # Import the modbus framer that you want #---------------------------------------------------------------------------# #---------------------------------------------------------------------------# #from pymodbus.transaction import ModbusSocketFramer as ModbusFramer from pymodbus.transaction import ModbusRtuFramer as ModbusFramer #from pymodbus.transaction import ModbusBinaryFramer as ModbusFramer #from pymodbus.transaction import ModbusAsciiFramer as ModbusFramer #---------------------------------------------------------------------------# # configure the client logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # Initialize the client #---------------------------------------------------------------------------# client = ModbusClient('localhost', port=5020, framer=ModbusFramer) client.connect() #---------------------------------------------------------------------------# # perform your requests #---------------------------------------------------------------------------# rq = client.write_coil(1, True) rr = client.read_coils(1,1) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.bits[0] == True) # test the expected value #---------------------------------------------------------------------------# # close the client #---------------------------------------------------------------------------# client.close() pymodbus/examples/common/asynchronous-server.py0000755000175500017550000001210112607272152022173 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Asynchronous Server Example -------------------------------------------------------------------------- The asynchronous server is a high performance implementation using the twisted library as its backend. This allows it to scale to many thousands of nodes which can be helpful for testing monitoring software. ''' #---------------------------------------------------------------------------# # import the various server implementations #---------------------------------------------------------------------------# from pymodbus.server.async import StartTcpServer from pymodbus.server.async import StartUdpServer from pymodbus.server.async import StartSerialServer from pymodbus.device import ModbusDeviceIdentification from pymodbus.datastore import ModbusSequentialDataBlock from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer #---------------------------------------------------------------------------# # configure the service logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # initialize your data store #---------------------------------------------------------------------------# # The datastores only respond to the addresses that they are initialized to. # Therefore, if you initialize a DataBlock to addresses of 0x00 to 0xFF, a # request to 0x100 will respond with an invalid address exception. This is # because many devices exhibit this kind of behavior (but not all):: # # block = ModbusSequentialDataBlock(0x00, [0]*0xff) # # Continuing, you can choose to use a sequential or a sparse DataBlock in # your data context. The difference is that the sequential has no gaps in # the data while the sparse can. Once again, there are devices that exhibit # both forms of behavior:: # # block = ModbusSparseDataBlock({0x00: 0, 0x05: 1}) # block = ModbusSequentialDataBlock(0x00, [0]*5) # # Alternately, you can use the factory methods to initialize the DataBlocks # or simply do not pass them to have them initialized to 0x00 on the full # address range:: # # store = ModbusSlaveContext(di = ModbusSequentialDataBlock.create()) # store = ModbusSlaveContext() # # Finally, you are allowed to use the same DataBlock reference for every # table or you you may use a seperate DataBlock for each table. This depends # if you would like functions to be able to access and modify the same data # or not:: # # block = ModbusSequentialDataBlock(0x00, [0]*0xff) # store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block) # # The server then makes use of a server context that allows the server to # respond with different slave contexts for different unit ids. By default # it will return the same context for every unit id supplied (broadcast # mode). However, this can be overloaded by setting the single flag to False # and then supplying a dictionary of unit id to context mapping:: # # slaves = { # 0x01: ModbusSlaveContext(...), # 0x02: ModbusSlaveContext(...), # 0x03: ModbusSlaveContext(...), # } # context = ModbusServerContext(slaves=slaves, single=False) # # The slave context can also be initialized in zero_mode which means that a # request to address(0-7) will map to the address (0-7). The default is # False which is based on section 4.4 of the specification, so address(0-7) # will map to (1-8):: # # store = ModbusSlaveContext(..., zero_mode=True) #---------------------------------------------------------------------------# store = ModbusSlaveContext( di = ModbusSequentialDataBlock(0, [17]*100), co = ModbusSequentialDataBlock(0, [17]*100), hr = ModbusSequentialDataBlock(0, [17]*100), ir = ModbusSequentialDataBlock(0, [17]*100)) context = ModbusServerContext(slaves=store, single=True) #---------------------------------------------------------------------------# # initialize the server information #---------------------------------------------------------------------------# # If you don't set this or any fields, they are defaulted to empty strings. #---------------------------------------------------------------------------# identity = ModbusDeviceIdentification() identity.VendorName = 'Pymodbus' identity.ProductCode = 'PM' identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' identity.ProductName = 'Pymodbus Server' identity.ModelName = 'Pymodbus Server' identity.MajorMinorRevision = '1.0' #---------------------------------------------------------------------------# # run the server you want #---------------------------------------------------------------------------# StartTcpServer(context, identity=identity, address=("localhost", 5020)) #StartUdpServer(context, identity=identity, address=("localhost", 502)) #StartSerialServer(context, identity=identity, port='/dev/pts/3', framer=ModbusRtuFramer) #StartSerialServer(context, identity=identity, port='/dev/pts/3', framer=ModbusAsciiFramer) pymodbus/examples/common/callback-server.py0000755000175500017550000001172312607272152021205 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Server With Callbacks -------------------------------------------------------------------------- This is an example of adding callbacks to a running modbus server when a value is written to it. In order for this to work, it needs a device-mapping file. ''' #---------------------------------------------------------------------------# # import the modbus libraries we need #---------------------------------------------------------------------------# from pymodbus.server.async import StartTcpServer from pymodbus.device import ModbusDeviceIdentification from pymodbus.datastore import ModbusSparseDataBlock from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer #---------------------------------------------------------------------------# # import the python libraries we need #---------------------------------------------------------------------------# from multiprocessing import Queue, Process #---------------------------------------------------------------------------# # configure the service logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # create your custom data block with callbacks #---------------------------------------------------------------------------# class CallbackDataBlock(ModbusSparseDataBlock): ''' A datablock that stores the new value in memory and passes the operation to a message queue for further processing. ''' def __init__(self, devices, queue): ''' ''' self.devices = devices self.queue = queue values = {k:0 for k in devices.iterkeys()} values[0xbeef] = len(values) # the number of devices super(CallbackDataBlock, self).__init__(values) def setValues(self, address, value): ''' Sets the requested values of the datastore :param address: The starting address :param values: The new values to be set ''' super(CallbackDataBlock, self).setValues(address, value) self.queue.put((self.devices.get(address, None), value)) #---------------------------------------------------------------------------# # define your callback process #---------------------------------------------------------------------------# def rescale_value(value): ''' Rescale the input value from the range of 0..100 to -3200..3200. :param value: The input value to scale :returns: The rescaled value ''' s = 1 if value >= 50 else -1 c = value if value < 50 else (value - 50) return s * (c * 64) def device_writer(queue): ''' A worker process that processes new messages from a queue to write to device outputs :param queue: The queue to get new messages from ''' while True: device, value = queue.get() scaled = rescale_value(value[0]) log.debug("Write(%s) = %s" % (device, value)) if not device: continue # do any logic here to update your devices #---------------------------------------------------------------------------# # initialize your device map #---------------------------------------------------------------------------# def read_device_map(path): ''' A helper method to read the device path to address mapping from file:: 0x0001,/dev/device1 0x0002,/dev/device2 :param path: The path to the input file :returns: The input mapping file ''' devices = {} with open(path, 'r') as stream: for line in stream: piece = line.strip().split(',') devices[int(piece[0], 16)] = piece[1] return devices #---------------------------------------------------------------------------# # initialize your data store #---------------------------------------------------------------------------# queue = Queue() devices = read_device_map("device-mapping") block = CallbackDataBlock(devices, queue) store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block) context = ModbusServerContext(slaves=store, single=True) #---------------------------------------------------------------------------# # initialize the server information #---------------------------------------------------------------------------# identity = ModbusDeviceIdentification() identity.VendorName = 'pymodbus' identity.ProductCode = 'PM' identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' identity.ProductName = 'pymodbus Server' identity.ModelName = 'pymodbus Server' identity.MajorMinorRevision = '1.0' #---------------------------------------------------------------------------# # run the server you want #---------------------------------------------------------------------------# p = Process(target=device_writer, args=(queue,)) p.start() StartTcpServer(context, identity=identity, address=("localhost", 5020)) pymodbus/examples/common/custom-datablock.py0000755000175500017550000000602612607272152021401 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Server With Custom Datablock Side Effect -------------------------------------------------------------------------- This is an example of performing custom logic after a value has been written to the datastore. ''' #---------------------------------------------------------------------------# # import the modbus libraries we need #---------------------------------------------------------------------------# from pymodbus.server.async import StartTcpServer from pymodbus.device import ModbusDeviceIdentification from pymodbus.datastore import ModbusSparseDataBlock from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer #---------------------------------------------------------------------------# # configure the service logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # create your custom data block here #---------------------------------------------------------------------------# class CustomDataBlock(ModbusSparseDataBlock): ''' A datablock that stores the new value in memory and performs a custom action after it has been stored. ''' def setValues(self, address, value): ''' Sets the requested values of the datastore :param address: The starting address :param values: The new values to be set ''' super(ModbusSparseDataBlock, self).setValues(address, value) # whatever you want to do with the written value is done here, # however make sure not to do too much work here or it will # block the server, espectially if the server is being written # to very quickly print "wrote {} to {}".format(value, address) #---------------------------------------------------------------------------# # initialize your data store #---------------------------------------------------------------------------# block = CustomDataBlock() store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block) context = ModbusServerContext(slaves=store, single=True) #---------------------------------------------------------------------------# # initialize the server information #---------------------------------------------------------------------------# identity = ModbusDeviceIdentification() identity.VendorName = 'pymodbus' identity.ProductCode = 'PM' identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' identity.ProductName = 'pymodbus Server' identity.ModelName = 'pymodbus Server' identity.MajorMinorRevision = '1.0' #---------------------------------------------------------------------------# # run the server you want #---------------------------------------------------------------------------# p = Process(target=device_writer, args=(queue,)) p.start() StartTcpServer(context, identity=identity, address=("localhost", 5020)) pymodbus/examples/common/synchronous-client.py0000755000175500017550000001264012607272152022012 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Synchronous Client Examples -------------------------------------------------------------------------- The following is an example of how to use the synchronous modbus client implementation from pymodbus. It should be noted that the client can also be used with the guard construct that is available in python 2.5 and up:: with ModbusClient('127.0.0.1') as client: result = client.read_coils(1,10) print result ''' #---------------------------------------------------------------------------# # import the various server implementations #---------------------------------------------------------------------------# from pymodbus.client.sync import ModbusTcpClient as ModbusClient #from pymodbus.client.sync import ModbusUdpClient as ModbusClient #from pymodbus.client.sync import ModbusSerialClient as ModbusClient #---------------------------------------------------------------------------# # configure the client logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # 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. # # If you use the UDP or TCP clients, you can override the framer being used # to use a custom implementation (say RTU over TCP). By default they use the # socket framer:: # # client = ModbusClient('localhost', port=5020, framer=ModbusRtuFramer) # # It should be noted that you can supply an ipv4 or an ipv6 host address for # both the UDP and TCP clients. # # There are also other options that can be set on the client that controls # how transactions are performed. The current ones are: # # * retries - Specify how many retries to allow per transaction (default = 3) # * retry_on_empty - Is an empty response a retry (default = False) # * source_address - Specifies the TCP source address to bind to # # Here is an example of using these options:: # # client = ModbusClient('localhost', retries=3, retry_on_empty=True) #---------------------------------------------------------------------------# client = ModbusClient('localhost', port=502) #client = ModbusClient(method='ascii', port='/dev/pts/2', timeout=1) #client = ModbusClient(method='rtu', port='/dev/pts/2', timeout=1) client.connect() #---------------------------------------------------------------------------# # specify slave to query #---------------------------------------------------------------------------# # The slave to query is specified in an optional parameter for each # individual request. This can be done by specifying the `unit` parameter # which defaults to `0x00` #---------------------------------------------------------------------------# rr = client.read_coils(1, 1, unit=0x02) #---------------------------------------------------------------------------# # example requests #---------------------------------------------------------------------------# # simply call the methods that you would like to use. An example session # is displayed below along with some assert checks. Note that some modbus # implementations differentiate holding/input discrete/coils and as such # you will not be able to write to these, therefore the starting values # are not known to these tests. Furthermore, some use the same memory # blocks for the two sets, so a change to one is a change to the other. # Keep both of these cases in mind when testing as the following will # _only_ pass with the supplied async modbus server (script supplied). #---------------------------------------------------------------------------# rq = client.write_coil(1, True) rr = client.read_coils(1,1) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.bits[0] == True) # test the expected value rq = client.write_coils(1, [True]*8) rr = client.read_coils(1,8) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.bits == [True]*8) # test the expected value rq = client.write_coils(1, [False]*8) rr = client.read_discrete_inputs(1,8) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.bits == [False]*8) # test the expected value rq = client.write_register(1, 10) rr = client.read_holding_registers(1,1) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.registers[0] == 10) # test the expected value rq = client.write_registers(1, [10]*8) rr = client.read_input_registers(1,8) assert(rq.function_code < 0x80) # test that we are not an error assert(rr.registers == [10]*8) # test the expected value arguments = { 'read_address': 1, 'read_count': 8, 'write_address': 1, 'write_registers': [20]*8, } rq = client.readwrite_registers(**arguments) rr = client.read_input_registers(1,8) assert(rq.function_code < 0x80) # test that we are not an error assert(rq.registers == [20]*8) # test the expected value assert(rr.registers == [20]*8) # test the expected value #---------------------------------------------------------------------------# # close the client #---------------------------------------------------------------------------# client.close() pymodbus/examples/common/README.rst0000644000175500017550000000737412607272152017266 0ustar debacledebacle============================================================ Modbus Implementations ============================================================ There are a few reference implementations that you can use to test modbus serial ------------------------------------------------------------ pymodbus ------------------------------------------------------------ You can use pymodbus as a testing server by simply modifying one of the run scripts supplied here. There is an asynchronous version and a synchronous version (that really differ in how mnay dependencies you are willing to have). Regardless of which one you choose, they can be started quite easily:: ./asynchronous-server.py ./synchronous-server.py Currently, each version has implementations of the following: - modbus tcp - modbus udp - modbus udp binary - modbus ascii serial - modbus ascii rtu ------------------------------------------------------------ Modbus Driver ------------------------------------------------------------ Included are reference implementations of a modbus client and server using the modbus driver library (as well as the relevant source code). Both programs have a wealth of options and can be used to test either side of your application:: tools/reference/diagslave -h # (server) tools/reference/modpoll -h # (client) ------------------------------------------------------------ jamod ------------------------------------------------------------ Jamod is a complete modbus implementation for the java jvm. Included are a few simple reference servers using the library, however, a great deal more can be produced using it. I have not tested it, however, it may even be possible to use this library in conjunction with jython to interop between your python code and this library: * http://jamod.sourceforge.net/ ------------------------------------------------------------ nmodbus ------------------------------------------------------------ Although there is not any code included in this package, nmodbus is a complete implementation of the modbus protocol for the .net clr. The site has a number of examples that can be tuned for your testing needs: * http://code.google.com/p/nmodbus/ ============================================================ Serial Loopback Testing ============================================================ In order to test the serial implementations, one needs to create a loopback connection (virtual serial port). This can be done in a number of ways. ------------------------------------------------------------ Linux ------------------------------------------------------------ For linux, there are three ways that are included with this distribution. One is to use the socat utility. The following will get one going quickly:: sudo apt-get install socat sudo socat PTY,link=/dev/pts/13, PTY,link=/dev/pts/14 # connect the master to /dev/pts/13 # connect the client to /dev/pts/14 Next, you can include the loopback kernel driver included in the tools/nullmodem/linux directory:: sudo ./run ------------------------------------------------------------ Windows ------------------------------------------------------------ For Windows, simply use the com2com application that is in the directory tools/nullmodem/windows. Instructions are included in the Readme.txt. ------------------------------------------------------------ Generic ------------------------------------------------------------ For most unix based systems, there is a simple virtual serial forwarding application in the tools/nullmodem/ directory:: make run # connect the master to the master output # connect the client to the client output Or for a tried and true method, simply connect a null modem cable between two of your serial ports and then simply reference those. pymodbus/examples/common/custom-message.py0000755000175500017550000000663112607272152021103 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Synchrnonous Client Examples -------------------------------------------------------------------------- The following is an example of how to use the synchronous modbus client implementation from pymodbus. It should be noted that the client can also be used with the guard construct that is available in python 2.5 and up:: with ModbusClient('127.0.0.1') as client: result = client.read_coils(1,10) print result ''' import struct #---------------------------------------------------------------------------# # import the various server implementations #---------------------------------------------------------------------------# from pymodbus.pdu import ModbusRequest, ModbusResponse from pymodbus.client.sync import ModbusTcpClient as ModbusClient #---------------------------------------------------------------------------# # configure the client logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # create your custom message #---------------------------------------------------------------------------# # The following is simply a read coil request that always reads 16 coils. # Since the function code is already registered with the decoder factory, # this will be decoded as a read coil response. If you implement a new # method that is not currently implemented, you must register the request # and response with a ClientDecoder factory. #---------------------------------------------------------------------------# class CustomModbusRequest(ModbusRequest): function_code = 1 def __init__(self, address): ModbusRequest.__init__(self) self.address = address self.count = 16 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(merror.IllegalValue) if not context.validate(self.function_code, self.address, self.count): return self.doException(merror.IllegalAddress) values = context.getValues(self.function_code, self.address, self.count) return CustomModbusResponse(values) #---------------------------------------------------------------------------# # This could also have been defined as #---------------------------------------------------------------------------# from pymodbus.bit_read_message import ReadCoilsRequest class Read16CoilsRequest(ReadCoilsRequest): def __init__(self, address): ''' Initializes a new instance :param address: The address to start reading from ''' ReadCoilsRequest.__init__(self, address, 16) #---------------------------------------------------------------------------# # execute the request with your client #---------------------------------------------------------------------------# # using the with context, the client will automatically be connected # and closed when it leaves the current scope. #---------------------------------------------------------------------------# with ModbusClient('127.0.0.1') as client: request = CustomModbusRequest(0) result = client.execute(request) print result pymodbus/examples/common/modbus-payload.py0000755000175500017550000000563012607272152021065 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Payload Building/Decoding Example -------------------------------------------------------------------------- ''' from pymodbus.constants import Endian from pymodbus.payload import BinaryPayloadDecoder from pymodbus.payload import BinaryPayloadBuilder from pymodbus.client.sync import ModbusTcpClient as ModbusClient #---------------------------------------------------------------------------# # configure the client logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.INFO) #---------------------------------------------------------------------------# # We are going to use a simple client to send our requests #---------------------------------------------------------------------------# client = ModbusClient('127.0.0.1') client.connect() #---------------------------------------------------------------------------# # If you need to build a complex message to send, you can use the payload # builder to simplify the packing logic. # # Here we demonstrate packing a random payload layout, unpacked it looks # like the following: # # - a 8 byte string 'abcdefgh' # - a 32 bit float 22.34 # - a 16 bit unsigned int 0x1234 # - an 8 bit int 0x12 # - an 8 bit bitstring [0,1,0,1,1,0,1,0] #---------------------------------------------------------------------------# builder = BinaryPayloadBuilder(endian=Endian.Little) builder.add_string('abcdefgh') builder.add_32bit_float(22.34) builder.add_16bit_uint(0x1234) builder.add_8bit_int(0x12) builder.add_bits([0,1,0,1,1,0,1,0]) payload = builder.build() address = 0x01 result = client.write_registers(address, payload, skip_encode=True) #---------------------------------------------------------------------------# # If you need to decode a collection of registers in a weird layout, the # payload decoder can help you as well. # # Here we demonstrate decoding a random register layout, unpacked it looks # like the following: # # - a 8 byte string 'abcdefgh' # - a 32 bit float 22.34 # - a 16 bit unsigned int 0x1234 # - an 8 bit int 0x12 # - an 8 bit bitstring [0,1,0,1,1,0,1,0] #---------------------------------------------------------------------------# address = 0x01 count = 8 result = client.read_input_registers(address, count) decoder = BinaryPayloadDecoder.fromRegisters(result.registers, endian=Endian.Little) decoded = { 'string': decoder.decode_string(8), 'float': decoder.decode_32bit_float(), '16uint': decoder.decode_16bit_uint(), '8int': decoder.decode_8bit_int(), 'bits': decoder.decode_bits(), } print "-" * 60 print "Decoded Data" print "-" * 60 for name, value in decoded.iteritems(): print ("%s\t" % name), value #---------------------------------------------------------------------------# # close the client #---------------------------------------------------------------------------# client.close() pymodbus/examples/common/modbus-logging.py0000755000175500017550000000344612607272152021065 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Logging Examples -------------------------------------------------------------------------- ''' import logging import logging.handlers as Handlers #---------------------------------------------------------------------------# # This will simply send everything logged to console #---------------------------------------------------------------------------# logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # This will send the error messages in the specified namespace to a file. # The available namespaces in pymodbus are as follows: #---------------------------------------------------------------------------# # * pymodbus.* - The root namespace # * pymodbus.server.* - all logging messages involving the modbus server # * pymodbus.client.* - all logging messages involving the client # * pymodbus.protocol.* - all logging messages inside the protocol layer #---------------------------------------------------------------------------# logging.basicConfig() log = logging.getLogger('pymodbus.server') log.setLevel(logging.ERROR) #---------------------------------------------------------------------------# # This will send the error messages to the specified handlers: # * docs.python.org/library/logging.html #---------------------------------------------------------------------------# log = logging.getLogger('pymodbus') log.setLevel(logging.ERROR) handlers = [ Handlers.RotatingFileHandler("logfile", maxBytes=1024*1024), Handlers.SMTPHandler("mx.host.com", "pymodbus@host.com", ["support@host.com"], "Pymodbus"), Handlers.SysLogHandler(facility="daemon"), Handlers.DatagramHandler('localhost', 12345), ] [log.addHandler(h) for h in handlers] pymodbus/examples/common/synchronous-client-ext.py0000755000175500017550000001671012607272152022612 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Synchronous Client Extended Examples -------------------------------------------------------------------------- The following is an example of how to use the synchronous modbus client implementation from pymodbus to perform the extended portions of the modbus protocol. ''' #---------------------------------------------------------------------------# # import the various server implementations #---------------------------------------------------------------------------# from pymodbus.client.sync import ModbusTcpClient as ModbusClient #from pymodbus.client.sync import ModbusUdpClient as ModbusClient #from pymodbus.client.sync import ModbusSerialClient as ModbusClient #---------------------------------------------------------------------------# # configure the client logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # 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('127.0.0.1') client.connect() #---------------------------------------------------------------------------# # import the extended messages to perform #---------------------------------------------------------------------------# from pymodbus.diag_message import * from pymodbus.file_message import * from pymodbus.other_message import * from pymodbus.mei_message import * #---------------------------------------------------------------------------# # 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 reference. #---------------------------------------------------------------------------# #---------------------------------------------------------------------------# # information requests #---------------------------------------------------------------------------# rq = ReadDeviceInformationRequest() 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] == 'proconX Pty Ltd') # test the vendor name assert(rr.information[1] == 'FT-MBSV') # test the product code assert(rr.information[2] == 'EXPERIMENTAL') # test the code revision rq = ReportSlaveIdRequest() 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() 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.status == 0x55) # test the status code rq = GetCommEventCounterRequest() 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.status == True) # test the status code #assert(rr.count == 0x00) # test the status code rq = GetCommEventLogRequest() 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.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 #---------------------------------------------------------------------------# rq = ReturnQueryDataRequest() rr = client.execute(rq) assert(rr == None) # not supported by reference #assert(rr.message[0] == 0x0000) # test the resulting message rq = RestartCommunicationsOptionRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference #assert(rr.message == 0x0000) # test the resulting message rq = ReturnDiagnosticRegisterRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference rq = ChangeAsciiInputDelimiterRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference rq = ForceListenOnlyModeRequest() client.execute(rq) # does not send a response rq = ClearCountersRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference rq = ReturnBusCommunicationErrorCountRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference rq = ReturnBusExceptionErrorCountRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference rq = ReturnSlaveMessageCountRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference rq = ReturnSlaveNoResponseCountRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference rq = ReturnSlaveNAKCountRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference rq = ReturnSlaveBusyCountRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference rq = ReturnSlaveBusCharacterOverrunCountRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference rq = ReturnIopOverrunCountRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference rq = ClearOverrunCountRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference rq = GetClearModbusPlusRequest() rr = client.execute(rq) #assert(rr == None) # not supported by reference #---------------------------------------------------------------------------# # close the client #---------------------------------------------------------------------------# client.close() pymodbus/examples/common/asynchronous-client.py0000755000175500017550000001343412607272152022155 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Asynchronous Client Examples -------------------------------------------------------------------------- The following is an example of how to use the asynchronous modbus client implementation from pymodbus. ''' #---------------------------------------------------------------------------# # import needed libraries #---------------------------------------------------------------------------# from twisted.internet import reactor, protocol from pymodbus.constants import Defaults #---------------------------------------------------------------------------# # choose the requested modbus protocol #---------------------------------------------------------------------------# from pymodbus.client.async import ModbusClientProtocol #from pymodbus.client.async import ModbusUdpClientProtocol #---------------------------------------------------------------------------# # configure the client logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # helper method to test deferred callbacks #---------------------------------------------------------------------------# def dassert(deferred, callback): def _assertor(value): assert(value) deferred.addCallback(lambda r: _assertor(callback(r))) deferred.addErrback(lambda _: _assertor(False)) #---------------------------------------------------------------------------# # specify slave to query #---------------------------------------------------------------------------# # The slave to query is specified in an optional parameter for each # individual request. This can be done by specifying the `unit` parameter # which defaults to `0x00` #---------------------------------------------------------------------------# def exampleRequests(client): rr = client.read_coils(1, 1, unit=0x02) #---------------------------------------------------------------------------# # example requests #---------------------------------------------------------------------------# # simply call the methods that you would like to use. An example session # is displayed below along with some assert checks. Note that unlike the # synchronous version of the client, the asynchronous version returns # deferreds which can be thought of as a handle to the callback to send # the result of the operation. We are handling the result using the # deferred assert helper(dassert). #---------------------------------------------------------------------------# def beginAsynchronousTest(client): rq = client.write_coil(1, True) rr = client.read_coils(1,1) dassert(rq, lambda r: r.function_code < 0x80) # test that we are not an error dassert(rr, lambda r: r.bits[0] == True) # test the expected value rq = client.write_coils(1, [True]*8) rr = client.read_coils(1,8) dassert(rq, lambda r: r.function_code < 0x80) # test that we are not an error dassert(rr, lambda r: r.bits == [True]*8) # test the expected value rq = client.write_coils(1, [False]*8) rr = client.read_discrete_inputs(1,8) dassert(rq, lambda r: r.function_code < 0x80) # test that we are not an error dassert(rr, lambda r: r.bits == [True]*8) # test the expected value rq = client.write_register(1, 10) rr = client.read_holding_registers(1,1) dassert(rq, lambda r: r.function_code < 0x80) # test that we are not an error dassert(rr, lambda r: r.registers[0] == 10) # test the expected value rq = client.write_registers(1, [10]*8) rr = client.read_input_registers(1,8) dassert(rq, lambda r: r.function_code < 0x80) # test that we are not an error dassert(rr, lambda r: r.registers == [17]*8) # test the expected value arguments = { 'read_address': 1, 'read_count': 8, 'write_address': 1, 'write_registers': [20]*8, } rq = client.readwrite_registers(**arguments) rr = client.read_input_registers(1,8) dassert(rq, lambda r: r.registers == [20]*8) # test the expected value dassert(rr, lambda r: r.registers == [17]*8) # test the expected value #-----------------------------------------------------------------------# # close the client at some time later #-----------------------------------------------------------------------# reactor.callLater(1, client.transport.loseConnection) reactor.callLater(2, reactor.stop) #---------------------------------------------------------------------------# # 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 # #---------------------------------------------------------------------------# #---------------------------------------------------------------------------# # 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. #---------------------------------------------------------------------------# defer = protocol.ClientCreator(reactor, ModbusClientProtocol ).connectTCP("localhost", Defaults.Port) defer.addCallback(beginAsynchronousTest) reactor.run() pymodbus/examples/common/performance.py0000755000175500017550000000643012607272152020445 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Performance Example -------------------------------------------------------------------------- The following is an quick performance check of the synchronous modbus client. ''' #---------------------------------------------------------------------------# # import the necessary modules #---------------------------------------------------------------------------# import logging, os from time import time from multiprocessing import log_to_stderr from pymodbus.client.sync import ModbusTcpClient #---------------------------------------------------------------------------# # choose between threads or processes #---------------------------------------------------------------------------# #from multiprocessing import Process as Worker from threading import Thread as Worker #---------------------------------------------------------------------------# # initialize the test #---------------------------------------------------------------------------# # Modify the parameters below to control how we are testing the client: # # * workers - the number of workers to use at once # * cycles - the total number of requests to send # * host - the host to send the requests to #---------------------------------------------------------------------------# workers = 1 cycles = 10000 host = '127.0.0.1' #---------------------------------------------------------------------------# # perform the test #---------------------------------------------------------------------------# # This test is written such that it can be used by many threads of processes # although it should be noted that there are performance penalties # associated with each strategy. #---------------------------------------------------------------------------# def single_client_test(host, cycles): ''' Performs a single threaded test of a synchronous client against the specified host :param host: The host to connect to :param cycles: The number of iterations to perform ''' logger = log_to_stderr() logger.setLevel(logging.DEBUG) logger.debug("starting worker: %d" % os.getpid()) try: count = 0 client = ModbusTcpClient(host) while count < cycles: result = client.read_holding_registers(10, 1).getRegister(0) count += 1 except: logger.exception("failed to run test successfully") logger.debug("finished worker: %d" % os.getpid()) #---------------------------------------------------------------------------# # run our test and check results #---------------------------------------------------------------------------# # We shard the total number of requests to perform between the number of # threads that was specified. We then start all the threads and block on # them to finish. This may need to switch to another mechanism to signal # finished as the process/thread start up/shut down may skew the test a bit. #---------------------------------------------------------------------------# args = (host, int(cycles * 1.0 / workers)) procs = [Worker(target=single_client_test, args=args) for _ in range(workers)] start = time() any(p.start() for p in procs) # start the workers any(p.join() for p in procs) # wait for the workers to finish stop = time() print "%d requests/second" % ((1.0 * cycles) / (stop - start)) pymodbus/examples/common/updating-server.py0000755000175500017550000000671312607272152021267 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Server With Updating Thread -------------------------------------------------------------------------- This is an example of having a background thread updating the context while the server is operating. This can also be done with a python thread:: from threading import Thread thread = Thread(target=updating_writer, args=(context,)) thread.start() ''' #---------------------------------------------------------------------------# # import the modbus libraries we need #---------------------------------------------------------------------------# from pymodbus.server.async import StartTcpServer from pymodbus.device import ModbusDeviceIdentification from pymodbus.datastore import ModbusSequentialDataBlock from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer #---------------------------------------------------------------------------# # import the twisted libraries we need #---------------------------------------------------------------------------# from twisted.internet.task import LoopingCall #---------------------------------------------------------------------------# # configure the service logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # define your callback process #---------------------------------------------------------------------------# def updating_writer(a): ''' A worker process that runs every so often and updates live values of the context. It should be noted that there is a race condition for the update. :param arguments: The input arguments to the call ''' log.debug("updating the context") context = a[0] register = 3 slave_id = 0x00 address = 0x10 values = context[slave_id].getValues(register, address, count=5) values = [v + 1 for v in values] log.debug("new values: " + str(values)) context[slave_id].setValues(register, address, values) #---------------------------------------------------------------------------# # initialize your data store #---------------------------------------------------------------------------# store = ModbusSlaveContext( di = ModbusSequentialDataBlock(0, [17]*100), co = ModbusSequentialDataBlock(0, [17]*100), hr = ModbusSequentialDataBlock(0, [17]*100), ir = ModbusSequentialDataBlock(0, [17]*100)) context = ModbusServerContext(slaves=store, single=True) #---------------------------------------------------------------------------# # initialize the server information #---------------------------------------------------------------------------# identity = ModbusDeviceIdentification() identity.VendorName = 'pymodbus' identity.ProductCode = 'PM' identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' identity.ProductName = 'pymodbus Server' identity.ModelName = 'pymodbus Server' identity.MajorMinorRevision = '1.0' #---------------------------------------------------------------------------# # run the server you want #---------------------------------------------------------------------------# time = 5 # 5 seconds delay loop = LoopingCall(f=updating_writer, a=(context,)) loop.start(time, now=False) # initially delay by time StartTcpServer(context, identity=identity, address=("localhost", 5020)) pymodbus/examples/common/asynchronous-processor.py0000755000175500017550000001611012607272152022710 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Asynchronous Processor Example -------------------------------------------------------------------------- The following is a full example of a continuous client processor. Feel free to use it as a skeleton guide in implementing your own. ''' #---------------------------------------------------------------------------# # import the neccessary modules #---------------------------------------------------------------------------# from twisted.internet import serialport, reactor from twisted.internet.protocol import ClientFactory from pymodbus.factory import ClientDecoder from pymodbus.client.async import ModbusClientProtocol #---------------------------------------------------------------------------# # Choose the framer you want to use #---------------------------------------------------------------------------# #from pymodbus.transaction import ModbusBinaryFramer as ModbusFramer #from pymodbus.transaction import ModbusAsciiFramer as ModbusFramer #from pymodbus.transaction import ModbusRtuFramer as ModbusFramer from pymodbus.transaction import ModbusSocketFramer as ModbusFramer #---------------------------------------------------------------------------# # configure the client logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger("pymodbus") log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # state a few constants #---------------------------------------------------------------------------# SERIAL_PORT = "/dev/ttyS0" STATUS_REGS = (1, 2) STATUS_COILS = (1, 3) CLIENT_DELAY = 1 #---------------------------------------------------------------------------# # an example custom protocol #---------------------------------------------------------------------------# # Here you can perform your main procesing loop utilizing defereds and timed # callbacks. #---------------------------------------------------------------------------# class ExampleProtocol(ModbusClientProtocol): def __init__(self, framer, endpoint): ''' Initializes our custom protocol :param framer: The decoder to use to process messages :param endpoint: The endpoint to send results to ''' ModbusClientProtocol.__init__(self, framer) self.endpoint = endpoint log.debug("Beginning the processing loop") reactor.callLater(CLIENT_DELAY, self.fetch_holding_registers) def fetch_holding_registers(self): ''' Defer fetching holding registers ''' log.debug("Starting the next cycle") d = self.read_holding_registers(*STATUS_REGS) d.addCallbacks(self.send_holding_registers, self.error_handler) def send_holding_registers(self, response): ''' Write values of holding registers, defer fetching coils :param response: The response to process ''' self.endpoint.write(response.getRegister(0)) self.endpoint.write(response.getRegister(1)) d = self.read_coils(*STATUS_COILS) d.addCallbacks(self.start_next_cycle, self.error_handler) def start_next_cycle(self, response): ''' Write values of coils, trigger next cycle :param response: The response to process ''' self.endpoint.write(response.getBit(0)) self.endpoint.write(response.getBit(1)) self.endpoint.write(response.getBit(2)) reactor.callLater(CLIENT_DELAY, self.fetch_holding_registers) def error_handler(self, failure): ''' Handle any twisted errors :param failure: The error to handle ''' log.error(failure) #---------------------------------------------------------------------------# # a factory for the example protocol #---------------------------------------------------------------------------# # This is used to build client protocol's if you tie into twisted's method # of processing. It basically produces client instances of the underlying # protocol:: # # Factory(Protocol) -> ProtocolInstance # # It also persists data between client instances (think protocol singelton). #---------------------------------------------------------------------------# class ExampleFactory(ClientFactory): protocol = ExampleProtocol def __init__(self, framer, endpoint): ''' Remember things necessary for building a protocols ''' self.framer = framer self.endpoint = endpoint def buildProtocol(self, _): ''' Create a protocol and start the reading cycle ''' proto = self.protocol(self.framer, self.endpoint) proto.factory = self return proto #---------------------------------------------------------------------------# # a custom client for our device #---------------------------------------------------------------------------# # Twisted provides a number of helper methods for creating and starting # clients: # - protocol.ClientCreator # - reactor.connectTCP # # How you start your client is really up to you. #---------------------------------------------------------------------------# class SerialModbusClient(serialport.SerialPort): def __init__(self, factory, *args, **kwargs): ''' Setup the client and start listening on the serial port :param factory: The factory to build clients with ''' protocol = factory.buildProtocol(None) self.decoder = ClientDecoder() serialport.SerialPort.__init__(self, protocol, *args, **kwargs) #---------------------------------------------------------------------------# # a custom endpoint for our results #---------------------------------------------------------------------------# # An example line reader, this can replace with: # - the TCP protocol # - a context recorder # - a database or file recorder #---------------------------------------------------------------------------# class LoggingLineReader(object): def write(self, response): ''' Handle the next modbus response :param response: The response to process ''' log.info("Read Data: %d" % response) #---------------------------------------------------------------------------# # start running the processor #---------------------------------------------------------------------------# # This initializes the client, the framer, the factory, and starts the # twisted event loop (the reactor). It should be noted that a number of # things could be chanegd as one sees fit: # - The ModbusRtuFramer could be replaced with a ModbusAsciiFramer # - The SerialModbusClient could be replaced with reactor.connectTCP # - The LineReader endpoint could be replaced with a database store #---------------------------------------------------------------------------# def main(): log.debug("Initializing the client") framer = ModbusFramer(ClientDecoder()) reader = LoggingLineReader() factory = ExampleFactory(framer, reader) SerialModbusClient(factory, SERIAL_PORT, reactor) #factory = reactor.connectTCP("localhost", 502, factory) log.debug("Starting the client") reactor.run() if __name__ == "__main__": main() pymodbus/examples/common/synchronous-server.py0000755000175500017550000001223112607272152022036 0ustar debacledebacle#!/usr/bin/env python ''' Pymodbus Synchronous Server Example -------------------------------------------------------------------------- The synchronous server is implemented in pure python without any third party libraries (unless you need to use the serial protocols which require pyserial). This is helpful in constrained or old environments where using twisted just is not feasable. What follows is an examle of its use: ''' #---------------------------------------------------------------------------# # import the various server implementations #---------------------------------------------------------------------------# from pymodbus.server.sync import StartTcpServer from pymodbus.server.sync import StartUdpServer from pymodbus.server.sync import StartSerialServer from pymodbus.device import ModbusDeviceIdentification from pymodbus.datastore import ModbusSequentialDataBlock from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext from pymodbus.transaction import ModbusRtuFramer #---------------------------------------------------------------------------# # configure the service logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # initialize your data store #---------------------------------------------------------------------------# # The datastores only respond to the addresses that they are initialized to. # Therefore, if you initialize a DataBlock to addresses of 0x00 to 0xFF, a # request to 0x100 will respond with an invalid address exception. This is # because many devices exhibit this kind of behavior (but not all):: # # block = ModbusSequentialDataBlock(0x00, [0]*0xff) # # Continuing, you can choose to use a sequential or a sparse DataBlock in # your data context. The difference is that the sequential has no gaps in # the data while the sparse can. Once again, there are devices that exhibit # both forms of behavior:: # # block = ModbusSparseDataBlock({0x00: 0, 0x05: 1}) # block = ModbusSequentialDataBlock(0x00, [0]*5) # # Alternately, you can use the factory methods to initialize the DataBlocks # or simply do not pass them to have them initialized to 0x00 on the full # address range:: # # store = ModbusSlaveContext(di = ModbusSequentialDataBlock.create()) # store = ModbusSlaveContext() # # Finally, you are allowed to use the same DataBlock reference for every # table or you you may use a seperate DataBlock for each table. This depends # if you would like functions to be able to access and modify the same data # or not:: # # block = ModbusSequentialDataBlock(0x00, [0]*0xff) # store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block) # # The server then makes use of a server context that allows the server to # respond with different slave contexts for different unit ids. By default # it will return the same context for every unit id supplied (broadcast # mode). However, this can be overloaded by setting the single flag to False # and then supplying a dictionary of unit id to context mapping:: # # slaves = { # 0x01: ModbusSlaveContext(...), # 0x02: ModbusSlaveContext(...), # 0x03: ModbusSlaveContext(...), # } # context = ModbusServerContext(slaves=slaves, single=False) # # The slave context can also be initialized in zero_mode which means that a # request to address(0-7) will map to the address (0-7). The default is # False which is based on section 4.4 of the specification, so address(0-7) # will map to (1-8):: # # store = ModbusSlaveContext(..., zero_mode=True) #---------------------------------------------------------------------------# store = ModbusSlaveContext( di = ModbusSequentialDataBlock(0, [17]*100), co = ModbusSequentialDataBlock(0, [17]*100), hr = ModbusSequentialDataBlock(0, [17]*100), ir = ModbusSequentialDataBlock(0, [17]*100)) context = ModbusServerContext(slaves=store, single=True) #---------------------------------------------------------------------------# # initialize the server information #---------------------------------------------------------------------------# # If you don't set this or any fields, they are defaulted to empty strings. #---------------------------------------------------------------------------# identity = ModbusDeviceIdentification() identity.VendorName = 'Pymodbus' identity.ProductCode = 'PM' identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' identity.ProductName = 'Pymodbus Server' identity.ModelName = 'Pymodbus Server' identity.MajorMinorRevision = '1.0' #---------------------------------------------------------------------------# # run the server you want #---------------------------------------------------------------------------# # Tcp: StartTcpServer(context, identity=identity, address=("localhost", 5020)) # Udp: #StartUdpServer(context, identity=identity, address=("localhost", 502)) # Ascii: #StartSerialServer(context, identity=identity, port='/dev/pts/3', timeout=1) # RTU: #StartSerialServer(context, framer=ModbusRtuFramer, identity=identity, port='/dev/pts/3', timeout=.005) pymodbus/examples/twisted/0000755000175500017550000000000012607272152015757 5ustar debacledebaclepymodbus/examples/twisted/modbus-udp.tac0000644000175500017550000000175012607272152020532 0ustar debacledebacle''' This service can be run with the following:: twistd -ny modbus-udp.tac ''' from twisted.application import service, internet from twisted.python.log import ILogObserver, FileLogObserver from twisted.python.logfile import DailyLogFile from pymodbus.constants import Defaults from pymodbus.server.async import ModbusUdpProtocol from pymodbus.transaction import ModbusSocketFramer from pymodbus.internal.ptwisted import InstallManagementConsole def BuildService(): ''' A helper method to build the service ''' context = None framer = ModbusSocketFramer server = ModbusUdpProtocol(context, framer) InstallManagementConsole({ 'server' : server }) application = internet.UDPServer(Defaults.Port, server) return application application = service.Application("Modbus UDP Server") logfile = DailyLogFile("pymodbus.log", "/tmp") application.setComponent(ILogObserver, FileLogObserver(logfile).emit) service = BuildService() service.setServiceParent(application) pymodbus/examples/twisted/modbus-tcp.tac0000644000175500017550000000175712607272152020537 0ustar debacledebacle''' This service can be run with the following:: twistd -ny modbus-tcp.tac ''' from twisted.application import service, internet from twisted.python.log import ILogObserver, FileLogObserver from twisted.python.logfile import DailyLogFile from pymodbus.constants import Defaults from pymodbus.server.async import ModbusServerFactory from pymodbus.transaction import ModbusSocketFramer from pymodbus.internal.ptwisted import InstallManagementConsole def BuildService(): ''' A helper method to build the service ''' context = None framer = ModbusSocketFramer factory = ModbusServerFactory(context, framer) InstallManagementConsole({ 'server' : factory }) application = internet.TCPServer(Defaults.Port, factory) return application application = service.Application("Modbus TCP Server") logfile = DailyLogFile("pymodbus.log", "/tmp") application.setComponent(ILogObserver, FileLogObserver(logfile).emit) service = BuildService() service.setServiceParent(application) pymodbus/examples/twisted/plugins/0000755000175500017550000000000012607272152017440 5ustar debacledebaclepymodbus/examples/twisted/plugins/pymodbus_plugin.py0000644000175500017550000000364112607272152023236 0ustar debacledebacle''' ''' from zope.interface import implements from twisted.python import usage from twisted.plugin import IPlugin from twisted.application.service import IServiceMaker from twisted.application import internet from pymodbus.constants import Defaults from pymodbus.server.async import ModbusServerFactory from pymodbus.transaction import ModbusSocketFramer from pymodbus.internal.ptwisted import InstallManagementConsole class Options(usage.Options): ''' The following are the options available to the pymodbus server. ''' optParameters = [ ["port", "p", Defaults.Port, "The port number to listen on."], ["type", "t", "tcp", "The type of server to host (tcp, udp, ascii, rtu)"], ["store", "s", "./datastore", "The pickled datastore to use"], ["console", "c", False, "Should the management console be started"], ] class ModbusServiceMaker(object): ''' A helper class used to build a twisted plugin ''' implements(IServiceMaker, IPlugin) tapname = "pymodbus" description = "A modbus server" options = Options def makeService(self, options): ''' Construct a service from the given options ''' if options["type"] == "tcp": server = internet.TCPServer else: server = internet.UDPServer framer = ModbusSocketFramer context = self._build_context(options['store']) factory = ModbusServerFactory(None, framer) if options['console']: InstallManagementConsole({ 'server' : factory }) return server(int(options["port"]), factory) def _build_context(self, path): ''' A helper method to unpickle a datastore, note, this should be a ModbusServerContext. ''' import pickle try: context = pickle.load(path) except Exception: context = None return context serviceMaker = ModbusServiceMaker() pymodbus/examples/gui/0000755000175500017550000000000012607272152015060 5ustar debacledebaclepymodbus/examples/gui/gtk/0000755000175500017550000000000012607272152015645 5ustar debacledebaclepymodbus/examples/gui/gtk/sims/0000755000175500017550000000000012607272152016620 5ustar debacledebaclepymodbus/examples/gui/gtk/sims/example.sim0000644000175500017550000000755212607272152020776 0ustar debacledebacle(dp0 S'hr' p1 ccopy_reg _reconstructor p2 (cpymodbus.datastore ModbusSparseDataBlock p3 c__builtin__ object p4 Ntp5 Rp6 (dp7 S'default_value' p8 I1 sS'values' p9 (dp10 I0 I1 sI1 I2 sI2 I3 sI3 I4 sI4 I5 sI5 I6 sI6 I7 sI7 I8 sI8 I9 sI9 I10 sI10 I11 sI11 I12 sI12 I13 sI13 I14 sI14 I15 sI15 I16 sI16 I17 sI17 I18 sI18 I19 sI19 I20 sI20 I21 sI21 I22 sI22 I23 sI23 I24 sI24 I25 sI25 I26 sI26 I27 sI27 I28 sI28 I29 sI29 I30 sI30 I31 sI31 I32 sI32 I33 sI33 I34 sI34 I35 sI35 I36 sI36 I37 sI37 I38 sI38 I39 sI39 I40 sI40 I41 sI41 I42 sI42 I43 sI43 I44 sI44 I45 sI45 I46 sI46 I47 sI47 I48 sI48 I49 sI49 I50 sI50 I51 sI51 I52 sI52 I53 sI53 I54 sI54 I55 sI55 I56 sI56 I57 sI57 I58 sI58 I59 sI59 I60 sI60 I61 sI61 I62 sI62 I63 sI63 I64 sI64 I65 sI65 I66 sI66 I67 sI67 I68 sI68 I69 sI69 I70 sI70 I71 sI71 I72 sI72 I73 sI73 I74 sI74 I75 sI75 I76 sI76 I77 sI77 I78 sI78 I79 sI79 I80 sI80 I81 sI81 I82 sI82 I83 sI83 I84 sI84 I85 sI85 I86 sI86 I87 sI87 I88 sI88 I89 sI89 I90 sI90 I91 sI91 I92 sI92 I93 sI93 I94 sI94 I95 sI95 I96 sI96 I97 sI97 I98 sI98 I99 ssS'address' p11 I0 sbsS'ci' p12 g2 (g3 g4 Ntp13 Rp14 (dp15 g8 I00 sg9 (dp16 I0 I00 sI1 I00 sI2 I00 sI3 I00 sI4 I00 sI5 I00 sI6 I00 sI7 I00 sI8 I00 sI9 I00 sI10 I00 sI11 I00 sI12 I00 sI13 I00 sI14 I00 sI15 I00 sI16 I00 sI17 I00 sI18 I00 sI19 I00 sI20 I00 sI21 I00 sI22 I00 sI23 I00 sI24 I00 sI25 I00 sI26 I00 sI27 I00 sI28 I00 sI29 I00 sI30 I00 sI31 I00 sI32 I00 sI33 I00 sI34 I00 sI35 I00 sI36 I00 sI37 I00 sI38 I00 sI39 I00 sI40 I00 sI41 I00 sI42 I00 sI43 I00 sI44 I00 sI45 I00 sI46 I00 sI47 I00 sI48 I00 sI49 I00 sI50 I00 sI51 I00 sI52 I00 sI53 I00 sI54 I00 sI55 I00 sI56 I00 sI57 I00 sI58 I00 sI59 I00 sI60 I00 sI61 I00 sI62 I00 sI63 I00 sI64 I00 sI65 I00 sI66 I00 sI67 I00 sI68 I00 sI69 I00 sI70 I00 sI71 I00 sI72 I00 sI73 I00 sI74 I00 sI75 I00 sI76 I00 sI77 I00 sI78 I00 sI79 I00 sI80 I00 sI81 I00 sI82 I00 sI83 I00 sI84 I00 sI85 I00 sI86 I00 sI87 I00 sI88 I00 sI89 I00 sI90 I00 sI91 I00 sI92 I00 sI93 I00 sI94 I00 sI95 I00 sI96 I00 sI97 I00 sI98 I00 ssg11 I0 sbsS'ir' p17 g2 (g3 g4 Ntp18 Rp19 (dp20 g8 I2 sg9 (dp21 I0 I2 sI1 I4 sI2 I6 sI3 I8 sI4 I10 sI5 I12 sI6 I14 sI7 I16 sI8 I18 sI9 I20 sI10 I22 sI11 I24 sI12 I26 sI13 I28 sI14 I30 sI15 I32 sI16 I34 sI17 I36 sI18 I38 sI19 I40 sI20 I42 sI21 I44 sI22 I46 sI23 I48 sI24 I50 sI25 I52 sI26 I54 sI27 I56 sI28 I58 sI29 I60 sI30 I62 sI31 I64 sI32 I66 sI33 I68 sI34 I70 sI35 I72 sI36 I74 sI37 I76 sI38 I78 sI39 I80 sI40 I82 sI41 I84 sI42 I86 sI43 I88 sI44 I90 sI45 I92 sI46 I94 sI47 I96 sI48 I98 sI49 I100 sI50 I102 sI51 I104 sI52 I106 sI53 I108 sI54 I110 sI55 I112 sI56 I114 sI57 I116 sI58 I118 sI59 I120 sI60 I122 sI61 I124 sI62 I126 sI63 I128 sI64 I130 sI65 I132 sI66 I134 sI67 I136 sI68 I138 sI69 I140 sI70 I142 sI71 I144 sI72 I146 sI73 I148 sI74 I150 sI75 I152 sI76 I154 sI77 I156 sI78 I158 sI79 I160 sI80 I162 sI81 I164 sI82 I166 sI83 I168 sI84 I170 sI85 I172 sI86 I174 sI87 I176 sI88 I178 sI89 I180 sI90 I182 sI91 I184 sI92 I186 sI93 I188 sI94 I190 sI95 I192 sI96 I194 sI97 I196 sI98 I198 ssg11 I0 sbsS'di' p22 g2 (g3 g4 Ntp23 Rp24 (dp25 g8 I01 sg9 (dp26 I0 I01 sI1 I01 sI2 I01 sI3 I01 sI4 I01 sI5 I01 sI6 I01 sI7 I01 sI8 I01 sI9 I01 sI10 I01 sI11 I01 sI12 I01 sI13 I01 sI14 I01 sI15 I01 sI16 I01 sI17 I01 sI18 I01 sI19 I01 sI20 I01 sI21 I01 sI22 I01 sI23 I01 sI24 I01 sI25 I01 sI26 I01 sI27 I01 sI28 I01 sI29 I01 sI30 I01 sI31 I01 sI32 I01 sI33 I01 sI34 I01 sI35 I01 sI36 I01 sI37 I01 sI38 I01 sI39 I01 sI40 I01 sI41 I01 sI42 I01 sI43 I01 sI44 I01 sI45 I01 sI46 I01 sI47 I01 sI48 I01 sI49 I01 sI50 I01 sI51 I01 sI52 I01 sI53 I01 sI54 I01 sI55 I01 sI56 I01 sI57 I01 sI58 I01 sI59 I01 sI60 I01 sI61 I01 sI62 I01 sI63 I01 sI64 I01 sI65 I01 sI66 I01 sI67 I01 sI68 I01 sI69 I01 sI70 I01 sI71 I01 sI72 I01 sI73 I01 sI74 I01 sI75 I01 sI76 I01 sI77 I01 sI78 I01 sI79 I01 sI80 I01 sI81 I01 sI82 I01 sI83 I01 sI84 I01 sI85 I01 sI86 I01 sI87 I01 sI88 I01 sI89 I01 sI90 I01 sI91 I01 sI92 I01 sI93 I01 sI94 I01 sI95 I01 sI96 I01 sI97 I01 sI98 I01 ssg11 I0 sbs.pymodbus/examples/gui/gtk/simulator.glade0000644000175500017550000002116612607272152020670 0ustar debacledebacle True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Modbus Simulator False GTK_WIN_POS_CENTER 400 200 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Device to Simulate True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 220 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False False 20 1 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Starting Address 230 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK False 20 1 1 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Number of Devices 230 True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 1 0 2000 1 10 0 False 20 1 2 True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_BUTTONBOX_SPREAD True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-help True 0 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-apply True 0 1 True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-stop True 0 2 3 pymodbus/examples/gui/gtk/simulator.py0000755000175500017550000003045112607272152020244 0ustar debacledebacle#!/usr/bin/env python #---------------------------------------------------------------------------# # System #---------------------------------------------------------------------------# import os import getpass import pickle from threading import Thread #---------------------------------------------------------------------------# # For Gui #---------------------------------------------------------------------------# from twisted.internet import gtk2reactor gtk2reactor.install() import gtk from gtk import glade #---------------------------------------------------------------------------# # SNMP Simulator #---------------------------------------------------------------------------# from twisted.internet import reactor from twisted.internet import error as twisted_error from pymodbus.server.async import ModbusServerFactory from pymodbus.datastore import ModbusServerContext,ModbusSlaveContext #--------------------------------------------------------------------------# # Logging #--------------------------------------------------------------------------# import logging log = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Application Error #---------------------------------------------------------------------------# class ConfigurationException(Exception): ''' Exception for configuration error ''' def __init__(self, string): Exception.__init__(self, string) self.string = string def __str__(self): return 'Configuration Error: %s' % self.string #---------------------------------------------------------------------------# # Extra Global Functions #---------------------------------------------------------------------------# # These are extra helper functions that don't belong in a class #---------------------------------------------------------------------------# def root_test(): ''' Simple test to see if we are running as root ''' return getpass.getuser() == "root" #---------------------------------------------------------------------------# # Simulator Class #---------------------------------------------------------------------------# class Simulator(object): ''' Class used to parse configuration file and create and modbus datastore. The format of the configuration file is actually just a python pickle, which is a compressed memory dump from the scraper. ''' def __init__(self, config): ''' Trys to load a configuration file, lets the file not found exception fall through @param config The pickled datastore ''' try: self.file = open(config, "r") except Exception: raise ConfigurationException("File not found %s" % config) def _parse(self): ''' Parses the config file and creates a server context ''' try: handle = pickle.load(self.file) dsd = handle['di'] csd = handle['ci'] hsd = handle['hr'] isd = handle['ir'] except KeyError: raise ConfigurationException("Invalid Configuration") slave = ModbusSlaveContext(d=dsd, c=csd, h=hsd, i=isd) return ModbusServerContext(slaves=slave) def _simulator(self): ''' Starts the snmp simulator ''' ports = [502]+range(20000,25000) for port in ports: try: reactor.listenTCP(port, ModbusServerFactory(self._parse())) print 'listening on port', port return port except twisted_error.CannotListenError: pass def run(self): ''' Used to run the simulator ''' reactor.callWhenRunning(self._simulator) #---------------------------------------------------------------------------# # Network reset thread #---------------------------------------------------------------------------# # This is linux only, maybe I should make a base class that can be filled # in for linux(debian/redhat)/windows/nix #---------------------------------------------------------------------------# class NetworkReset(Thread): ''' This class is simply a daemon that is spun off at the end of the program to call the network restart function (an easy way to remove all the virtual interfaces) ''' def __init__(self): Thread.__init__(self) self.setDaemon(True) def run(self): ''' Run the network reset ''' os.system("/etc/init.d/networking restart") #---------------------------------------------------------------------------# # Main Gui Class #---------------------------------------------------------------------------# # Note, if you are using gtk2 before 2.12, the file_set signal is not # introduced. To fix this, you need to apply the following patch #---------------------------------------------------------------------------# #Index: simulator.py #=================================================================== #--- simulator.py (revision 60) #+++ simulator.py (working copy) #@@ -158,7 +161,7 @@ # "on_helpBtn_clicked" : self.help_clicked, # "on_quitBtn_clicked" : self.close_clicked, # "on_startBtn_clicked" : self.start_clicked, #- "on_file_changed" : self.file_changed, #+ #"on_file_changed" : self.file_changed, # "on_window_destroy" : self.close_clicked # } # self.tree.signal_autoconnect(actions) #@@ -235,6 +238,7 @@ # return False # # # check input file #+ self.file_changed(self.tdevice) # if os.path.exists(self.file): # self.grey_out() # handle = Simulator(config=self.file) #---------------------------------------------------------------------------# class SimulatorApp(object): ''' This class implements the GUI for the flasher application ''' file = "none" subnet = 205 number = 1 restart = 0 def __init__(self, xml): ''' Sets up the gui, callback, and widget handles ''' #---------------------------------------------------------------------------# # Action Handles #---------------------------------------------------------------------------# self.tree = glade.XML(xml) self.bstart = self.tree.get_widget("startBtn") self.bhelp = self.tree.get_widget("helpBtn") self.bclose = self.tree.get_widget("quitBtn") self.window = self.tree.get_widget("window") self.tdevice = self.tree.get_widget("fileTxt") self.tsubnet = self.tree.get_widget("addressTxt") self.tnumber = self.tree.get_widget("deviceTxt") #---------------------------------------------------------------------------# # Actions #---------------------------------------------------------------------------# actions = { "on_helpBtn_clicked" : self.help_clicked, "on_quitBtn_clicked" : self.close_clicked, "on_startBtn_clicked" : self.start_clicked, "on_file_changed" : self.file_changed, "on_window_destroy" : self.close_clicked } self.tree.signal_autoconnect(actions) if not root_test(): self.error_dialog("This program must be run with root permissions!", True) #---------------------------------------------------------------------------# # Gui helpers #---------------------------------------------------------------------------# # Not callbacks, but used by them #---------------------------------------------------------------------------# def show_buttons(self, state=False, all=0): ''' Greys out the buttons ''' if all: self.window.set_sensitive(state) self.bstart.set_sensitive(state) self.tdevice.set_sensitive(state) self.tsubnet.set_sensitive(state) self.tnumber.set_sensitive(state) def destroy_interfaces(self): ''' This is used to reset the virtual interfaces ''' if self.restart: n = NetworkReset() n.start() def error_dialog(self, message, quit=False): ''' Quick pop-up for error messages ''' dialog = gtk.MessageDialog( parent = self.window, flags = gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL, type = gtk.MESSAGE_ERROR, buttons = gtk.BUTTONS_CLOSE, message_format = message) dialog.set_title('Error') if quit: dialog.connect("response", lambda w, r: gtk.main_quit()) else: dialog.connect("response", lambda w, r: w.destroy()) dialog.show() #---------------------------------------------------------------------------# # Button Actions #---------------------------------------------------------------------------# # These are all callbacks for the various buttons #---------------------------------------------------------------------------# def start_clicked(self, widget): ''' Starts the simulator ''' start = 1 base = "172.16" # check starting network net = self.tsubnet.get_text() octets = net.split('.') if len(octets) == 4: base = "%s.%s" % (octets[0], octets[1]) net = int(octets[2]) % 255 start = int(octets[3]) % 255 else: self.error_dialog("Invalid starting address!"); return False # check interface size size = int(self.tnumber.get_text()) if (size >= 1): for i in range(start, (size + start)): j = i % 255 cmd = "/sbin/ifconfig eth0:%d %s.%d.%d" % (i, base, net, j) os.system(cmd) if j == 254: net = net + 1 self.restart = 1 else: self.error_dialog("Invalid number of devices!"); return False # check input file if os.path.exists(self.file): self.show_buttons(state=False) try: handle = Simulator(config=self.file) handle.run() except ConfigurationException, ex: self.error_dialog("Error %s" % ex) self.show_buttons(state=True) else: self.error_dialog("Device to emulate does not exist!"); return False def help_clicked(self, widget): ''' Quick pop-up for about page ''' data = gtk.AboutDialog() data.set_version("0.1") data.set_name(('Modbus Simulator')) data.set_authors(["Galen Collins"]) data.set_comments(('First Select a device to simulate,\n' + 'then select the starting subnet of the new devices\n' + 'then select the number of device to simulate and click start')) data.set_website("http://code.google.com/p/pymodbus/") data.connect("response", lambda w,r: w.hide()) data.run() def close_clicked(self, widget): ''' Callback for close button ''' self.destroy_interfaces() reactor.stop() # quit twisted def file_changed(self, widget): ''' Callback for the filename change ''' self.file = widget.get_filename() #---------------------------------------------------------------------------# # Main handle function #---------------------------------------------------------------------------# # This is called when the application is run from a console # We simply start the gui and start the twisted event loop #---------------------------------------------------------------------------# def main(): ''' Main control function This either launches the gui or runs the command line application ''' debug = True if debug: try: log.setLevel(logging.DEBUG) logging.basicConfig() except Exception, e: print "Logging is not supported on this system" simulator = SimulatorApp('./simulator.glade') reactor.run() #---------------------------------------------------------------------------# # Library/Console Test #---------------------------------------------------------------------------# # If this is called from console, we start main #---------------------------------------------------------------------------# if __name__ == "__main__": main() pymodbus/examples/gui/gui-common.py0000755000175500017550000001015612607272152017512 0ustar debacledebacle#!/usr/bin/env python #---------------------------------------------------------------------------# # System #---------------------------------------------------------------------------# import os import getpass import pickle from threading import Thread #---------------------------------------------------------------------------# # SNMP Simulator #---------------------------------------------------------------------------# from twisted.internet import reactor from twisted.internet import error as twisted_error from pymodbus.server.async import ModbusServerFactory from pymodbus.datastore import ModbusServerContext,ModbusSlaveContext #--------------------------------------------------------------------------# # Logging #--------------------------------------------------------------------------# import logging log = logging.getLogger("pymodbus") #---------------------------------------------------------------------------# # Application Error #---------------------------------------------------------------------------# class ConfigurationException(Exception): ''' Exception for configuration error ''' pass #---------------------------------------------------------------------------# # Extra Global Functions #---------------------------------------------------------------------------# # These are extra helper functions that don't belong in a class #---------------------------------------------------------------------------# def root_test(): ''' Simple test to see if we are running as root ''' return getpass.getuser() == "root" #---------------------------------------------------------------------------# # Simulator Class #---------------------------------------------------------------------------# class Simulator(object): ''' Class used to parse configuration file and create and modbus datastore. The format of the configuration file is actually just a python pickle, which is a compressed memory dump from the scraper. ''' def __init__(self, config): ''' Trys to load a configuration file, lets the file not found exception fall through :param config: The pickled datastore ''' try: self.file = open(config, "r") except Exception: raise ConfigurationException("File not found %s" % config) def _parse(self): ''' Parses the config file and creates a server context ''' try: handle = pickle.load(self.file) dsd = handle['di'] csd = handle['ci'] hsd = handle['hr'] isd = handle['ir'] except KeyError: raise ConfigurationException("Invalid Configuration") slave = ModbusSlaveContext(d=dsd, c=csd, h=hsd, i=isd) return ModbusServerContext(slaves=slave) def _simulator(self): ''' Starts the snmp simulator ''' ports = [502]+range(20000,25000) for port in ports: try: reactor.listenTCP(port, ModbusServerFactory(self._parse())) log.debug('listening on port %d' % port) return port except twisted_error.CannotListenError: pass def run(self): ''' Used to run the simulator ''' log.debug('simulator started') reactor.callWhenRunning(self._simulator) #---------------------------------------------------------------------------# # Network reset thread #---------------------------------------------------------------------------# # This is linux only, maybe I should make a base class that can be filled # in for linux(debian/redhat)/windows/nix #---------------------------------------------------------------------------# class NetworkReset(Thread): ''' This class is simply a daemon that is spun off at the end of the program to call the network restart function (an easy way to remove all the virtual interfaces) ''' def __init__(self): ''' Initialize a new network reset thread ''' Thread.__init__(self) self.setDaemon(True) def run(self): ''' Run the network reset ''' os.system("/etc/init.d/networking restart") pymodbus/examples/gui/bottle/0000755000175500017550000000000012607273533016355 5ustar debacledebaclepymodbus/examples/gui/bottle/frontend.py0000644000175500017550000002540012607272152020543 0ustar debacledebacle''' Pymodbus Web Frontend ======================================= This is a simple web frontend using bottle as the web framework. This can be hosted using any wsgi adapter. ''' import json, inspect from bottle import route, request, Bottle from bottle import static_file from bottle import jinja2_template as template #---------------------------------------------------------------------------# # configure the client logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # REST API #---------------------------------------------------------------------------# class Response(object): ''' A collection of common responses for the frontend api ''' success = { 'status' : 200 } failure = { 'status' : 500 } class ModbusApiWebApp(object): ''' This is the web REST api interace into the pymodbus service. It can be consumed by any utility that can make web requests (javascript). ''' _namespace = '/api/v1' def __init__(self, server): ''' Initialize a new instance of the ModbusApi :param server: The current server instance ''' self._server = server #---------------------------------------------------------------------# # Device API #---------------------------------------------------------------------# def get_device(self): return { 'mode' : self._server.control.Mode, 'delimiter' : self._server.control.Delimiter, 'readonly' : self._server.control.ListenOnly, 'identity' : self._server.control.Identity.summary(), 'counters' : dict(self._server.control.Counter), 'diagnostic' : self._server.control.getDiagnosticRegister(), } def get_device_identity(self): return { 'identity' : dict(self._server.control.Identity) } def get_device_counters(self): return { 'counters' : dict(self._server.control.Counter) } def get_device_events(self): return { 'events' : self._server.control.Events } def get_device_plus(self): return { 'plus' : dict(self._server.control.Plus) } def delete_device_events(self): self._server.control.clearEvents() return Response.success def get_device_host(self): return { 'hosts' : list(self._server.access) } def post_device_host(self): value = request.forms.get('host') if value: self._server.access.add(value) return Response.success def delete_device_host(self): value = request.forms.get('host') if value: self._server.access.remove(value) return Response.success def post_device_delimiter(self): value = request.forms.get('delimiter') if value: self._server.control.Delimiter = value return Response.success def post_device_mode(self): value = request.forms.get('mode') if value: self._server.control.Mode = value return Response.success def post_device_reset(self): self._server.control.reset() return Response.success #---------------------------------------------------------------------# # Datastore Get API #---------------------------------------------------------------------# def __get_data(self, store, address, count, slave='00'): try: address, count = int(address), int(count) context = self._server.store[int(store)] values = context.getValues(store, address, count) values = dict(zip(range(address, address + count), values)) result = { 'data' : values } result.update(Response.success) return result except Exception, ex: log.error(ex) return Response.failure def get_coils(self, address='0', count='1'): return self.__get_data(1, address, count) def get_discretes(self, address='0', count='1'): return self.__get_data(2, address, count) def get_holdings(self, address='0', count='1'): return self.__get_data(3, address, count) def get_inputs(self, address='0', count='1'): return self.__get_data(4, address, count) #---------------------------------------------------------------------# # Datastore Update API #---------------------------------------------------------------------# def __set_data(self, store, address, values, slave='00'): try: address = int(address) values = json.loads(values) print values context = self._server.store[int(store)] context.setValues(store, address, values) return Response.success except Exception, ex: log.error(ex) return Response.failure def post_coils(self, address='0'): values = request.forms.get('data') return self.__set_data(1, address, values) def post_discretes(self, address='0'): values = request.forms.get('data') return self.__set_data(2, address, values) def post_holding(self, address='0'): values = request.forms.get('data') return self.__set_data(3, address, values) def post_inputs(self, address='0'): values = request.forms.get('data') return self.__set_data(4, address, values) #---------------------------------------------------------------------# # webpage routes #---------------------------------------------------------------------# def register_web_routes(application, register): ''' A helper method to register the default web routes of a single page application. :param application: The application instance to register :param register: The bottle instance to register the application with ''' def get_index_file(): return template('index.html') def get_static_file(filename): return static_file(filename, root='./media') register.route('/', method='GET', name='get_index_file')(get_index_file) register.route('/media/', method='GET', name='get_static_file')(get_static_file) #---------------------------------------------------------------------------# # Configurations #---------------------------------------------------------------------------# def register_api_routes(application, register): ''' A helper method to register the routes of an application based on convention. This is easier to manage than having to decorate each method with a static route name. :param application: The application instance to register :param register: The bottle instance to register the application with ''' log.info("installing application routes:") methods = inspect.getmembers(application) methods = filter(lambda n: not n[0].startswith('_'), methods) for method, func in dict(methods).iteritems(): pieces = method.split('_') verb, path = pieces[0], pieces[1:] args = inspect.getargspec(func).args[1:] args = ['<%s>' % arg for arg in args] args = '/'.join(args) args = '' if len(args) == 0 else '/' + args path.insert(0, application._namespace) path = '/'.join(path) + args log.info("%6s: %s" % (verb, path)) register.route(path, method=verb, name=method)(func) def build_application(server): ''' Helper method to create and initiailze a bottle application :param server: The modbus server to pull instance data from :returns: An initialied bottle application ''' log.info("building web application") api = ModbusApiWebApp(server) register = Bottle() register_api_routes(api, register) register_web_routes(api, register) return register #---------------------------------------------------------------------------# # Start Methods #---------------------------------------------------------------------------# def RunModbusFrontend(server, port=8080): ''' Helper method to host bottle in twisted :param server: The modbus server to pull instance data from :param port: The port to host the service on ''' from bottle import TwistedServer, run application = build_application(server) run(app=application, server=TwistedServer, port=port) def RunDebugModbusFrontend(server, port=8080): ''' Helper method to start the bottle server :param server: The modbus server to pull instance data from :param port: The port to host the service on ''' from bottle import run application = build_application(server) run(app=application, port=port) if __name__ == '__main__': # ------------------------------------------------------------ # an example server configuration # ------------------------------------------------------------ from pymodbus.server.async import ModbusServerFactory from pymodbus.constants import Defaults from pymodbus.device import ModbusDeviceIdentification from pymodbus.datastore import ModbusSequentialDataBlock from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext from twisted.internet import reactor # ------------------------------------------------------------ # initialize the identity # ------------------------------------------------------------ identity = ModbusDeviceIdentification() identity.VendorName = 'Pymodbus' identity.ProductCode = 'PM' identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' identity.ProductName = 'Pymodbus Server' identity.ModelName = 'Pymodbus Server' identity.MajorMinorRevision = '1.0' # ------------------------------------------------------------ # initialize the datastore # ------------------------------------------------------------ store = ModbusSlaveContext( di = ModbusSequentialDataBlock(0, [17]*100), co = ModbusSequentialDataBlock(0, [17]*100), hr = ModbusSequentialDataBlock(0, [17]*100), ir = ModbusSequentialDataBlock(0, [17]*100)) context = ModbusServerContext(slaves=store, single=True) # ------------------------------------------------------------ # initialize the factory # ------------------------------------------------------------ address = ("", Defaults.Port) factory = ModbusServerFactory(context, None, identity) # ------------------------------------------------------------ # start the servers # ------------------------------------------------------------ log.info("Starting Modbus TCP Server on %s:%s" % address) reactor.listenTCP(address[1], factory, interface=address[0]) RunDebugModbusFrontend(factory) pymodbus/examples/gui/bottle/requirements.txt0000644000175500017550000000031612607272152021635 0ustar debacledebacle# ------------------------------------------------------------------- # if you want to use this frontend uncomment these # ------------------------------------------------------------------- bottle==0.11.2 pymodbus/examples/gui/bottle/views/0000755000175500017550000000000012607272152017506 5ustar debacledebaclepymodbus/examples/gui/bottle/views/index.html0000644000175500017550000001043612607272152021507 0ustar debacledebacle {% block title %}pymodbus | server{% endblock %}

Device Identity

Device Counters

Device Settings

pymodbus/examples/gui/tk/0000755000175500017550000000000012607272152015476 5ustar debacledebaclepymodbus/examples/gui/tk/fileopen.gif0000644000175500017550000000216012607272152017765 0ustar debacledebacleGIF89aç¢>?>LMJLNJMOLNOLOQMQRNRTPSURTVRUWSWYV[]X[]Y^`Zegegh[gi`lnhprdqsdtvguwhxyrxzkxzsy{lz|l{|n|~o~€p€‚rƒs†‡w‹ŒzŒ{ŒŽ|Ž}„Ž‚ƒ‘€’„’“’“‚’“…’“†“”ƒ“•ƒ”•‡“•‹”–„•–„”–ˆ•—‰—˜Ž—™†˜™‡˜š‹š›‰š›Ššœ‡›œœœž‹œžŽžŸ‹ŸŸŽžŸŸ ž •Ÿ¡ ¡Ž  ™ ¢Ž¡£¡£’¢¤£¥“¤¦‘¤¦”¥¦”¥§”¦§“¥¨”§¨–¨ª”©ª—©«•©«–ª«™ª¬š«¬˜ª¬ž«®›¬® ­®¡­¯š­¯œ­¯¡®¯¤®°¯±œ¯±°±ž¯±¡°±¡°²Ÿ±²Ÿ±³±³ž±³¢²´ ²´¡³´¡´µ¡´µ¤´¶£¶·¢¶·¥·¹¤¸º§¸º¨¹»¦¹»§¹»©»¼¨»½©»¾«¼¾©½¿«¾¾¼½À­¾Á¬À¯Á®À²ÁîÁóÂÄ®ÂıÂÄ´Ãŵį´ÅDzÅǶÈÉ·ÈÉ»Ê̺ËÍ»ÍκÎϽÎнÏѾÑÓÀÓÔÂÔÔÂÓÔÆîîì÷÷öÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ!þCreated with GIMP!ù ÿ,þÿ H° Á *Tpð ‚2"F,ð!AzhÔ ÂÂÿa$A²d™“(Qš‰ˆ ?F ˆ’K¢rêÜ)JA–>|À ª¨Q£=¿$@øÈCœ…¢J•št©ÀŠ8l zôhÏ0VE2Ò€êÔ©_Y>²Pk×¢i™R˜  T̘"$Ø‹àÁ’O˜"9BdHŸ= 1): for i in range(start, (size + start)): j = i % 255 cmd = "/sbin/ifconfig eth0:%d %s.%d.%d" % (i, base, net, j) os.system(cmd) if j == 254: net = net + 1 self.restart = 1 else: self.error_dialog("Invalid number of devices!"); return False # check input file filename = self.tdevice_value.get() if os.path.exists(filename): self.show_buttons(state=False) try: handle = Simulator(config=filename) handle.run() except ConfigurationException, ex: self.error_dialog("Error %s" % ex) self.show_buttons(state=True) else: self.error_dialog("Device to emulate does not exist!"); return False def help_clicked(self): ''' Quick pop-up for about page ''' data = gtk.AboutDialog() data.set_version("0.1") data.set_name(('Modbus Simulator')) data.set_authors(["Galen Collins"]) data.set_comments(('First Select a device to simulate,\n' + 'then select the starting subnet of the new devices\n' + 'then select the number of device to simulate and click start')) data.set_website("http://code.google.com/p/pymodbus/") data.connect("response", lambda w,r: w.hide()) data.run() def close_clicked(self): ''' Callback for close button ''' #self.destroy_interfaces() reactor.stop() def file_clicked(self): ''' Callback for the filename change ''' file = OpenFilename() self.tdevice_value.set(file) class SimulatorApp(object): ''' The main wx application handle for our simulator ''' def __init__(self, master): ''' Called by wxWindows to initialize our application :param master: The master window to connect to ''' font = ('Helvetica', 12, 'normal') frame = SimulatorFrame(master, font) frame.pack() #---------------------------------------------------------------------------# # Main handle function #---------------------------------------------------------------------------# # This is called when the application is run from a console # We simply start the gui and start the twisted event loop #---------------------------------------------------------------------------# def main(): ''' Main control function This either launches the gui or runs the command line application ''' debug = True if debug: try: log.setLevel(logging.DEBUG) logging.basicConfig() except Exception, e: print "Logging is not supported on this system" simulator = SimulatorApp(root) root.title("Modbus Simulator") reactor.run() #---------------------------------------------------------------------------# # Library/Console Test #---------------------------------------------------------------------------# # If this is called from console, we start main #---------------------------------------------------------------------------# if __name__ == "__main__": main() pymodbus/examples/gui/wx/0000755000175500017550000000000012607272152015516 5ustar debacledebaclepymodbus/examples/gui/wx/simulator.py0000755000175500017550000002642512607272152020123 0ustar debacledebacle#!/usr/bin/env python ''' Note that this is not finished ''' #---------------------------------------------------------------------------# # System #---------------------------------------------------------------------------# import os import getpass import pickle from threading import Thread #---------------------------------------------------------------------------# # For Gui #---------------------------------------------------------------------------# import wx from twisted.internet import wxreactor wxreactor.install() #---------------------------------------------------------------------------# # SNMP Simulator #---------------------------------------------------------------------------# from twisted.internet import reactor from twisted.internet import error as twisted_error from pymodbus.server.async import ModbusServerFactory from pymodbus.datastore import ModbusServerContext,ModbusSlaveContext #--------------------------------------------------------------------------# # Logging #--------------------------------------------------------------------------# import logging log = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Application Error #---------------------------------------------------------------------------# class ConfigurationException(Exception): ''' Exception for configuration error ''' pass #---------------------------------------------------------------------------# # Extra Global Functions #---------------------------------------------------------------------------# # These are extra helper functions that don't belong in a class #---------------------------------------------------------------------------# def root_test(): ''' Simple test to see if we are running as root ''' return getpass.getuser() == "root" #---------------------------------------------------------------------------# # Simulator Class #---------------------------------------------------------------------------# class Simulator(object): ''' Class used to parse configuration file and create and modbus datastore. The format of the configuration file is actually just a python pickle, which is a compressed memory dump from the scraper. ''' def __init__(self, config): ''' Trys to load a configuration file, lets the file not found exception fall through @param config The pickled datastore ''' try: self.file = open(config, "r") except Exception: raise ConfigurationException("File not found %s" % config) def _parse(self): ''' Parses the config file and creates a server context ''' try: handle = pickle.load(self.file) dsd = handle['di'] csd = handle['ci'] hsd = handle['hr'] isd = handle['ir'] except KeyError: raise ConfigurationException("Invalid Configuration") slave = ModbusSlaveContext(d=dsd, c=csd, h=hsd, i=isd) return ModbusServerContext(slaves=slave) def _simulator(self): ''' Starts the snmp simulator ''' ports = [502]+range(20000,25000) for port in ports: try: reactor.listenTCP(port, ModbusServerFactory(self._parse())) print 'listening on port', port return port except twisted_error.CannotListenError: pass def run(self): ''' Used to run the simulator ''' reactor.callWhenRunning(self._simulator) #---------------------------------------------------------------------------# # Network reset thread #---------------------------------------------------------------------------# # This is linux only, maybe I should make a base class that can be filled # in for linux(debian/redhat)/windows/nix #---------------------------------------------------------------------------# class NetworkReset(Thread): ''' This class is simply a daemon that is spun off at the end of the program to call the network restart function (an easy way to remove all the virtual interfaces) ''' def __init__(self): ''' Initializes a new instance of the network reset thread ''' Thread.__init__(self) self.setDaemon(True) def run(self): ''' Run the network reset ''' os.system("/etc/init.d/networking restart") #---------------------------------------------------------------------------# # Main Gui Class #---------------------------------------------------------------------------# class SimulatorFrame(wx.Frame): ''' This class implements the GUI for the flasher application ''' subnet = 205 number = 1 restart = 0 def __init__(self, parent, id, title): ''' Sets up the gui, callback, and widget handles ''' wx.Frame.__init__(self, parent, id, title) wx.EVT_CLOSE(self, self.close_clicked) #---------------------------------------------------------------------------# # Add button row #---------------------------------------------------------------------------# panel = wx.Panel(self, -1) box = wx.BoxSizer(wx.HORIZONTAL) box.Add(wx.Button(panel, 1, 'Apply'), 1) box.Add(wx.Button(panel, 2, 'Help'), 1) box.Add(wx.Button(panel, 3, 'Close'), 1) panel.SetSizer(box) #---------------------------------------------------------------------------# # Add input boxes #---------------------------------------------------------------------------# #self.tdevice = self.tree.get_widget("fileTxt") #self.tsubnet = self.tree.get_widget("addressTxt") #self.tnumber = self.tree.get_widget("deviceTxt") #---------------------------------------------------------------------------# # Tie callbacks #---------------------------------------------------------------------------# self.Bind(wx.EVT_BUTTON, self.start_clicked, id=1) self.Bind(wx.EVT_BUTTON, self.help_clicked, id=2) self.Bind(wx.EVT_BUTTON, self.close_clicked, id=3) #if not root_test(): # self.error_dialog("This program must be run with root permissions!", True) #---------------------------------------------------------------------------# # Gui helpers #---------------------------------------------------------------------------# # Not callbacks, but used by them #---------------------------------------------------------------------------# def show_buttons(self, state=False, all=0): ''' Greys out the buttons ''' if all: self.window.set_sensitive(state) self.bstart.set_sensitive(state) self.tdevice.set_sensitive(state) self.tsubnet.set_sensitive(state) self.tnumber.set_sensitive(state) def destroy_interfaces(self): ''' This is used to reset the virtual interfaces ''' if self.restart: n = NetworkReset() n.start() def error_dialog(self, message, quit=False): ''' Quick pop-up for error messages ''' log.debug("error event called") dialog = wx.MessageDialog(self, message, 'Error', wx.OK | wx.ICON_ERROR) dialog.ShowModel() if quit: self.Destroy() dialog.Destroy() #---------------------------------------------------------------------------# # Button Actions #---------------------------------------------------------------------------# # These are all callbacks for the various buttons #---------------------------------------------------------------------------# def start_clicked(self, widget): ''' Starts the simulator ''' start = 1 base = "172.16" # check starting network net = self.tsubnet.get_text() octets = net.split('.') if len(octets) == 4: base = "%s.%s" % (octets[0], octets[1]) net = int(octets[2]) % 255 start = int(octets[3]) % 255 else: self.error_dialog("Invalid starting address!"); return False # check interface size size = int(self.tnumber.get_text()) if (size >= 1): for i in range(start, (size + start)): j = i % 255 cmd = "/sbin/ifconfig eth0:%d %s.%d.%d" % (i, base, net, j) os.system(cmd) if j == 254: net = net + 1 self.restart = 1 else: self.error_dialog("Invalid number of devices!"); return False # check input file if os.path.exists(self.file): self.show_buttons(state=False) try: handle = Simulator(config=self.file) handle.run() except ConfigurationException, ex: self.error_dialog("Error %s" % ex) self.show_buttons(state=True) else: self.error_dialog("Device to emulate does not exist!"); return False def help_clicked(self, widget): ''' Quick pop-up for about page ''' data = gtk.AboutDialog() data.set_version("0.1") data.set_name(('Modbus Simulator')) data.set_authors(["Galen Collins"]) data.set_comments(('First Select a device to simulate,\n' + 'then select the starting subnet of the new devices\n' + 'then select the number of device to simulate and click start')) data.set_website("http://code.google.com/p/pymodbus/") data.connect("response", lambda w,r: w.hide()) data.run() def close_clicked(self, event): ''' Callback for close button ''' log.debug("close event called") reactor.stop() def file_changed(self, event): ''' Callback for the filename change ''' self.file = widget.get_filename() class SimulatorApp(wx.App): ''' The main wx application handle for our simulator ''' def OnInit(self): ''' Called by wxWindows to initialize our application :returns: Always True ''' log.debug("application initialize event called") reactor.registerWxApp(self) frame = SimulatorFrame(None, -1, "Pymodbus Simulator") frame.CenterOnScreen() frame.Show(True) self.SetTopWindow(frame) return True #---------------------------------------------------------------------------# # Main handle function #---------------------------------------------------------------------------# # This is called when the application is run from a console # We simply start the gui and start the twisted event loop #---------------------------------------------------------------------------# def main(): ''' Main control function This either launches the gui or runs the command line application ''' debug = True if debug: try: log.setLevel(logging.DEBUG) logging.basicConfig() except Exception, e: print "Logging is not supported on this system" simulator = SimulatorApp(0) reactor.run() #---------------------------------------------------------------------------# # Library/Console Test #---------------------------------------------------------------------------# # If this is called from console, we start main #---------------------------------------------------------------------------# if __name__ == "__main__": main() pymodbus/.gitignore0000644000175500017550000000006612607272152014450 0ustar debacledebacle*.pyc *.swp build/ dist/ pymodbus.egg-info/ .coverage pymodbus/.pydevproject0000644000175500017550000000065112607272152015177 0ustar debacledebacle /pymodbus python 2.7 Pymodbus Environment pymodbus/README.rst0000644000175500017550000001231112607272152014143 0ustar debacledebacle============================================================ Summary ============================================================ Pymodbus is a full Modbus protocol implementation using twisted for its asynchronous communications core. It can also be used without any third party dependencies (aside from pyserial) if a more lightweight project is needed. Furthermore, it should work fine under any python version > 2.3 with a python 3.0 branch currently being maintained as well. ============================================================ Features ============================================================ ------------------------------------------------------------ Client Features ------------------------------------------------------------ * Full read/write protocol on discrete and register * Most of the extended protocol (diagnostic/file/pipe/setting/information) * TCP, UDP, Serial ASCII, Serial RTU, and Serial Binary * asynchronous(powered by twisted) and synchronous versions * Payload builder/decoder utilities ------------------------------------------------------------ Server Features ------------------------------------------------------------ * Can function as a fully implemented modbus server * TCP, UDP, Serial ASCII, Serial RTU, and Serial Binary * asynchronous(powered by twisted) and synchronous versions * Full server control context (device information, counters, etc) * A number of backing contexts (database, redis, a slave device) ============================================================ Use Cases ============================================================ Although most system administrators will find little need for a Modbus server on any modern hardware, they may find the need to query devices on their network for status (PDU, PDR, UPS, etc). Since the library is written in python, it allows for easy scripting and/or integration into their existing solutions. Continuing, most monitoring software needs to be stress tested against hundreds or even thousands of devices (why this was originally written), but getting access to that many is unwieldy at best. The pymodbus server will allow a user to test as many devices as their base operating system will allow (*allow* in this case means how many Virtual IP addresses are allowed). For more information please browse the project documentation: http://readthedocs.org/docs/pymodbus/en/latest/index.html ------------------------------------------------------------ Example Code ------------------------------------------------------------ For those of you that just want to get started fast, here you go:: from pymodbus.client.sync import ModbusTcpClient client = ModbusTcpClient('127.0.0.1') client.write_coil(1, True) result = client.read_coils(1,1) print result.bits[0] client.close() For more advanced examples, check out the examples included in the respository. If you have created any utilities that meet a specific need, feel free to submit them so others can benefit. Also, if you have questions, please ask them on the mailing list so that others can benefit from the results and so that I can trace them. I get a lot of email and sometimes these requests get lost in the noise: http://groups.google.com/group/pymodbus ------------------------------------------------------------ Installing ------------------------------------------------------------ You can install using pip or easy install by issuing the following commands in a terminal window (make sure you have correct permissions or a virtualenv currently running):: easy_install -U pymodbus pip install -U pymodbus Otherwise you can pull the trunk source and install from there:: git clone git://github.com/bashwork/pymodbus.git cd pymodbus python setup.py install Either method will install all the required dependencies (at their appropriate versions) for your current python distribution. If you would like to install pymodbus without the twisted dependency, simply edit the setup.py file before running easy_install and comment out all mentions of twisted. It should be noted that without twisted, one will only be able to run the synchronized version as the asynchronous versions uses twisted for its event loop. ------------------------------------------------------------ Current Work In Progress ------------------------------------------------------------ Since I don't have access to any live modbus devices anymore it is a bit hard to test on live hardware. However, if you would like your device tested, I accept devices via mail or by IP address. That said, the current work mainly involves polishing the library as I get time doing such tasks as: * Fixing bugs/feature requests * Architecture documentation * Functional testing against any reference I can find * The remaining edges of the protocol (that I think no one uses) ------------------------------------------------------------ License Information ------------------------------------------------------------ Pymodbus is built on top of code developed from/by: * Copyright (c) 2001-2005 S.W.A.C. GmbH, Germany. * Copyright (c) 2001-2005 S.W.A.C. Bohemia s.r.o., Czech Republic. * Hynek Petrak * Twisted Matrix Released under the BSD License pymodbus/test/0000755000175500017550000000000012607272226013437 5ustar debacledebaclepymodbus/test/test_file_message.py0000644000175500017550000003264012607272152017476 0ustar debacledebacle#!/usr/bin/env python ''' Bit Message Test Fixture -------------------------------- This fixture tests the functionality of all the bit based request/response messages: * Read/Write Discretes * Read Coils ''' import unittest from pymodbus.file_message import * from pymodbus.exceptions import * from pymodbus.pdu import ModbusExceptions from modbus_mocks import MockContext #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class ModbusBitMessageTests(unittest.TestCase): #-----------------------------------------------------------------------# # Setup/TearDown #-----------------------------------------------------------------------# def setUp(self): ''' Initializes the test environment and builds request/result encoding pairs ''' pass def tearDown(self): ''' Cleans up the test environment ''' pass #-----------------------------------------------------------------------# # Read Fifo Queue #-----------------------------------------------------------------------# def testReadFifoQueueRequestEncode(self): ''' Test basic bit message encoding/decoding ''' handle = ReadFifoQueueRequest(0x1234) result = handle.encode() self.assertEqual(result, '\x12\x34') def testReadFifoQueueRequestDecode(self): ''' Test basic bit message encoding/decoding ''' handle = ReadFifoQueueRequest(0x0000) handle.decode('\x12\x34') self.assertEqual(handle.address, 0x1234) def testReadFifoQueueRequest(self): ''' Test basic bit message encoding/decoding ''' context = MockContext() handle = ReadFifoQueueRequest(0x1234) result = handle.execute(context) self.assertTrue(isinstance(result, ReadFifoQueueResponse)) handle.address = -1 result = handle.execute(context) self.assertEqual(ModbusExceptions.IllegalValue, result.exception_code) handle.values = [0x00]*33 result = handle.execute(context) self.assertEqual(ModbusExceptions.IllegalValue, result.exception_code) def testReadFifoQueueRequestError(self): ''' Test basic bit message encoding/decoding ''' context = MockContext() handle = ReadFifoQueueRequest(0x1234) handle.values = [0x00]*32 result = handle.execute(context) self.assertEqual(result.function_code, 0x98) def testReadFifoQueueResponseEncode(self): ''' Test that the read fifo queue response can encode ''' message = '\x00\n\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04' handle = ReadFifoQueueResponse([1,2,3,4]) result = handle.encode() self.assertEqual(result, message) def testReadFifoQueueResponseDecode(self): ''' Test that the read fifo queue response can decode ''' message = '\x00\n\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04' handle = ReadFifoQueueResponse([1,2,3,4]) handle.decode(message) self.assertEqual(handle.values, [1,2,3,4]) def testRtuFrameSize(self): ''' Test that the read fifo queue response can decode ''' message = '\x00\n\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04' result = ReadFifoQueueResponse.calculateRtuFrameSize(message) self.assertEqual(result, 14) #-----------------------------------------------------------------------# # File Record #-----------------------------------------------------------------------# def testFileRecordLength(self): ''' Test file record length generation ''' record = FileRecord(file_number=0x01, record_number=0x02, record_data='\x00\x01\x02\x04') self.assertEqual(record.record_length, 0x02) self.assertEqual(record.response_length, 0x05) def testFileRecordComapre(self): ''' Test file record comparison operations ''' record1 = FileRecord(file_number=0x01, record_number=0x02, record_data='\x00\x01\x02\x04') record2 = FileRecord(file_number=0x01, record_number=0x02, record_data='\x00\x0a\x0e\x04') record3 = FileRecord(file_number=0x02, record_number=0x03, record_data='\x00\x01\x02\x04') record4 = FileRecord(file_number=0x01, record_number=0x02, record_data='\x00\x01\x02\x04') self.assertTrue(record1 == record4) self.assertTrue(record1 != record2) self.assertNotEqual(record1, record2) self.assertNotEqual(record1, record3) self.assertNotEqual(record2, record3) self.assertEqual(record1, record4) self.assertEqual(str(record1), "FileRecord(file=1, record=2, length=2)") self.assertEqual(str(record2), "FileRecord(file=1, record=2, length=2)") self.assertEqual(str(record3), "FileRecord(file=2, record=3, length=2)") #-----------------------------------------------------------------------# # Read File Record Request #-----------------------------------------------------------------------# def testReadFileRecordRequestEncode(self): ''' Test basic bit message encoding/decoding ''' records = [FileRecord(file_number=0x01, record_number=0x02)] handle = ReadFileRecordRequest(records) result = handle.encode() self.assertEqual(result, '\x07\x06\x00\x01\x00\x02\x00\x00') def testReadFileRecordRequestDecode(self): ''' Test basic bit message encoding/decoding ''' record = FileRecord(file_number=0x04, record_number=0x01, record_length=0x02) request = '\x0e\x06\x00\x04\x00\x01\x00\x02\x06\x00\x03\x00\x09\x00\x02' handle = ReadFileRecordRequest() handle.decode(request) self.assertEqual(handle.records[0], record) def testReadFileRecordRequestRtuFrameSize(self): ''' Test basic bit message encoding/decoding ''' request = '\x00\x00\x0e\x06\x00\x04\x00\x01\x00\x02\x06\x00\x03\x00\x09\x00\x02' handle = ReadFileRecordRequest() size = handle.calculateRtuFrameSize(request) self.assertEqual(size, 0x0e + 5) def testReadFileRecordRequestExecute(self): ''' Test basic bit message encoding/decoding ''' handle = ReadFileRecordRequest() result = handle.execute(None) self.assertTrue(isinstance(result, ReadFileRecordResponse)) #-----------------------------------------------------------------------# # Read File Record Response #-----------------------------------------------------------------------# def testReadFileRecordResponseEncode(self): ''' Test basic bit message encoding/decoding ''' records = [FileRecord(record_data='\x00\x01\x02\x03')] handle = ReadFileRecordResponse(records) result = handle.encode() self.assertEqual(result, '\x06\x06\x02\x00\x01\x02\x03') def testReadFileRecordResponseDecode(self): ''' Test basic bit message encoding/decoding ''' record = FileRecord(file_number=0x00, record_number=0x00, record_data='\x0d\xfe\x00\x20') request = '\x0c\x05\x06\x0d\xfe\x00\x20\x05\x05\x06\x33\xcd\x00\x40' handle = ReadFileRecordResponse() handle.decode(request) self.assertEqual(handle.records[0], record) def testReadFileRecordResponseRtuFrameSize(self): ''' Test basic bit message encoding/decoding ''' request = '\x00\x00\x0c\x05\x06\x0d\xfe\x00\x20\x05\x05\x06\x33\xcd\x00\x40' handle = ReadFileRecordResponse() size = handle.calculateRtuFrameSize(request) self.assertEqual(size, 0x0c + 5) #-----------------------------------------------------------------------# # Write File Record Request #-----------------------------------------------------------------------# def testWriteFileRecordRequestEncode(self): ''' Test basic bit message encoding/decoding ''' records = [FileRecord(file_number=0x01, record_number=0x02, record_data='\x00\x01\x02\x03')] handle = WriteFileRecordRequest(records) result = handle.encode() self.assertEqual(result, '\x0b\x06\x00\x01\x00\x02\x00\x02\x00\x01\x02\x03') def testWriteFileRecordRequestDecode(self): ''' Test basic bit message encoding/decoding ''' record = FileRecord(file_number=0x04, record_number=0x07, record_data='\x06\xaf\x04\xbe\x10\x0d') request = '\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\x0d' handle = WriteFileRecordRequest() handle.decode(request) self.assertEqual(handle.records[0], record) def testWriteFileRecordRequestRtuFrameSize(self): ''' Test write file record request rtu frame size calculation ''' request = '\x00\x00\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\x0d' handle = WriteFileRecordRequest() size = handle.calculateRtuFrameSize(request) self.assertEqual(size, 0x0d + 5) def testWriteFileRecordRequestExecute(self): ''' Test basic bit message encoding/decoding ''' handle = WriteFileRecordRequest() result = handle.execute(None) self.assertTrue(isinstance(result, WriteFileRecordResponse)) #-----------------------------------------------------------------------# # Write File Record Response #-----------------------------------------------------------------------# def testWriteFileRecordResponseEncode(self): ''' Test basic bit message encoding/decoding ''' records = [FileRecord(file_number=0x01, record_number=0x02, record_data='\x00\x01\x02\x03')] handle = WriteFileRecordResponse(records) result = handle.encode() self.assertEqual(result, '\x0b\x06\x00\x01\x00\x02\x00\x02\x00\x01\x02\x03') def testWriteFileRecordResponseDecode(self): ''' Test basic bit message encoding/decoding ''' record = FileRecord(file_number=0x04, record_number=0x07, record_data='\x06\xaf\x04\xbe\x10\x0d') request = '\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\x0d' handle = WriteFileRecordResponse() handle.decode(request) self.assertEqual(handle.records[0], record) def testWriteFileRecordResponseRtuFrameSize(self): ''' Test write file record response rtu frame size calculation ''' request = '\x00\x00\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\x0d' handle = WriteFileRecordResponse() size = handle.calculateRtuFrameSize(request) self.assertEqual(size, 0x0d + 5) #-----------------------------------------------------------------------# # Mask Write Register Request #-----------------------------------------------------------------------# def testMaskWriteRegisterRequestEncode(self): ''' Test basic bit message encoding/decoding ''' handle = MaskWriteRegisterRequest(0x0000, 0x0101, 0x1010) result = handle.encode() self.assertEqual(result, '\x00\x00\x01\x01\x10\x10') def testMaskWriteRegisterRequestDecode(self): ''' Test basic bit message encoding/decoding ''' request = '\x00\x04\x00\xf2\x00\x25' handle = MaskWriteRegisterRequest() handle.decode(request) self.assertEqual(handle.address, 0x0004) self.assertEqual(handle.and_mask, 0x00f2) self.assertEqual(handle.or_mask, 0x0025) def testMaskWriteRegisterRequestExecute(self): ''' Test write register request valid execution ''' context = MockContext(valid=True, default=0x0000) handle = MaskWriteRegisterRequest(0x0000, 0x0101, 0x1010) result = handle.execute(context) self.assertTrue(isinstance(result, MaskWriteRegisterResponse)) def testMaskWriteRegisterRequestInvalidExecute(self): ''' Test write register request execute with invalid data ''' context = MockContext(valid=False, default=0x0000) handle = MaskWriteRegisterRequest(0x0000, -1, 0x1010) result = handle.execute(context) self.assertEqual(ModbusExceptions.IllegalValue, result.exception_code) handle = MaskWriteRegisterRequest(0x0000, 0x0101, -1) result = handle.execute(context) self.assertEqual(ModbusExceptions.IllegalValue, result.exception_code) handle = MaskWriteRegisterRequest(0x0000, 0x0101, 0x1010) result = handle.execute(context) self.assertEqual(ModbusExceptions.IllegalAddress, result.exception_code) #-----------------------------------------------------------------------# # Mask Write Register Response #-----------------------------------------------------------------------# def testMaskWriteRegisterResponseEncode(self): ''' Test basic bit message encoding/decoding ''' handle = MaskWriteRegisterResponse(0x0000, 0x0101, 0x1010) result = handle.encode() self.assertEqual(result, '\x00\x00\x01\x01\x10\x10') def testMaskWriteRegisterResponseDecode(self): ''' Test basic bit message encoding/decoding ''' request = '\x00\x04\x00\xf2\x00\x25' handle = MaskWriteRegisterResponse() handle.decode(request) self.assertEqual(handle.address, 0x0004) self.assertEqual(handle.and_mask, 0x00f2) self.assertEqual(handle.or_mask, 0x0025) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_all_messages.py0000644000175500017550000000663212607272152017514 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.constants import Defaults from pymodbus.bit_read_message import * from pymodbus.bit_write_message import * from pymodbus.register_read_message import * from pymodbus.register_write_message import * #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class ModbusAllMessagesTests(unittest.TestCase): #-----------------------------------------------------------------------# # Setup/TearDown #-----------------------------------------------------------------------# def setUp(self): ''' Initializes the test environment and builds request/result encoding pairs ''' arguments = { 'read_address': 1, 'read_count': 1, 'write_address': 1, 'write_registers': 1 } self.requests = [ lambda unit: ReadCoilsRequest(1, 5, unit=unit), lambda unit: ReadDiscreteInputsRequest(1, 5, unit=unit), lambda unit: WriteSingleCoilRequest(1, 1, unit=unit), lambda unit: WriteMultipleCoilsRequest(1, [1], unit=unit), lambda unit: ReadHoldingRegistersRequest(1, 5, unit=unit), lambda unit: ReadInputRegistersRequest(1, 5, unit=unit), lambda unit: ReadWriteMultipleRegistersRequest(unit=unit, **arguments), lambda unit: WriteSingleRegisterRequest(1, 1, unit=unit), lambda unit: WriteMultipleRegistersRequest(1, [1], unit=unit), ] self.responses = [ lambda unit: ReadCoilsResponse([1], unit=unit), lambda unit: ReadDiscreteInputsResponse([1], unit=unit), lambda unit: WriteSingleCoilResponse(1, 1, unit=unit), lambda unit: WriteMultipleCoilsResponse(1, [1], unit=unit), lambda unit: ReadHoldingRegistersResponse([1], unit=unit), lambda unit: ReadInputRegistersResponse([1], unit=unit), lambda unit: ReadWriteMultipleRegistersResponse([1], unit=unit), lambda unit: WriteSingleRegisterResponse(1, 1, unit=unit), lambda unit: WriteMultipleRegistersResponse(1, 1, unit=unit), ] def tearDown(self): ''' Cleans up the test environment ''' pass def testInitializingSlaveAddressRequest(self): ''' Test that every request can initialize the unit id ''' unit_id = 0x12 for factory in self.requests: request = factory(unit_id) self.assertEqual(request.unit_id, unit_id) def testInitializingSlaveAddressResponse(self): ''' Test that every response can initialize the unit id ''' unit_id = 0x12 for factory in self.responses: response = factory(unit_id) self.assertEqual(response.unit_id, unit_id) def testForwardingKwargsToPdu(self): ''' Test that the kwargs are forwarded to the pdu correctly ''' request = ReadCoilsRequest(1,5, unit=0x12, transaction=0x12, protocol=0x12) self.assertEqual(request.unit_id, 0x12) self.assertEqual(request.transaction_id, 0x12) self.assertEqual(request.protocol_id, 0x12) request = ReadCoilsRequest(1,5) self.assertEqual(request.unit_id, Defaults.UnitId) self.assertEqual(request.transaction_id, Defaults.TransactionId) self.assertEqual(request.protocol_id, Defaults.ProtocolId) pymodbus/test/test_transaction.py0000644000175500017550000004045712607272152017405 0ustar debacledebacle#!/usr/bin/env python import unittest from binascii import a2b_hex from pymodbus.pdu import * from pymodbus.transaction import * from pymodbus.factory import ServerDecoder class ModbusTransactionTest(unittest.TestCase): ''' This is the unittest for the pymodbus.transaction module ''' #---------------------------------------------------------------------------# # Test Construction #---------------------------------------------------------------------------# def setUp(self): ''' Sets up the test environment ''' self.client = None self.decoder = ServerDecoder() self._tcp = ModbusSocketFramer(decoder=self.decoder) self._rtu = ModbusRtuFramer(decoder=self.decoder) self._ascii = ModbusAsciiFramer(decoder=self.decoder) self._binary = ModbusBinaryFramer(decoder=self.decoder) self._manager = DictTransactionManager(self.client) self._queue_manager = FifoTransactionManager(self.client) def tearDown(self): ''' Cleans up the test environment ''' del self._manager del self._tcp del self._rtu del self._ascii #---------------------------------------------------------------------------# # Dictionary based transaction manager #---------------------------------------------------------------------------# def testDictTransactionManagerTID(self): ''' Test the dict transaction manager TID ''' for tid in range(1, self._manager.getNextTID() + 10): self.assertEqual(tid+1, self._manager.getNextTID()) self._manager.reset() self.assertEqual(1, self._manager.getNextTID()) def testGetDictTransactionManagerTransaction(self): ''' Test the dict transaction manager ''' class Request: pass self._manager.reset() handle = Request() handle.transaction_id = self._manager.getNextTID() handle.message = "testing" self._manager.addTransaction(handle) result = self._manager.getTransaction(handle.transaction_id) self.assertEqual(handle.message, result.message) def testDeleteDictTransactionManagerTransaction(self): ''' Test the dict transaction manager ''' class Request: pass self._manager.reset() handle = Request() handle.transaction_id = self._manager.getNextTID() handle.message = "testing" self._manager.addTransaction(handle) self._manager.delTransaction(handle.transaction_id) self.assertEqual(None, self._manager.getTransaction(handle.transaction_id)) #---------------------------------------------------------------------------# # Queue based transaction manager #---------------------------------------------------------------------------# def testFifoTransactionManagerTID(self): ''' Test the fifo transaction manager TID ''' for tid in range(1, self._queue_manager.getNextTID() + 10): self.assertEqual(tid+1, self._queue_manager.getNextTID()) self._queue_manager.reset() self.assertEqual(1, self._queue_manager.getNextTID()) def testGetFifoTransactionManagerTransaction(self): ''' Test the fifo transaction manager ''' class Request: pass self._queue_manager.reset() handle = Request() handle.transaction_id = self._queue_manager.getNextTID() handle.message = "testing" self._queue_manager.addTransaction(handle) result = self._queue_manager.getTransaction(handle.transaction_id) self.assertEqual(handle.message, result.message) def testDeleteFifoTransactionManagerTransaction(self): ''' Test the fifo transaction manager ''' class Request: pass self._queue_manager.reset() handle = Request() handle.transaction_id = self._queue_manager.getNextTID() handle.message = "testing" self._queue_manager.addTransaction(handle) self._queue_manager.delTransaction(handle.transaction_id) self.assertEqual(None, self._queue_manager.getTransaction(handle.transaction_id)) #---------------------------------------------------------------------------# # TCP tests #---------------------------------------------------------------------------# def testTCPFramerTransactionReady(self): ''' Test a tcp frame transaction ''' msg = "\x00\x01\x12\x34\x00\x04\xff\x02\x12\x34" self.assertFalse(self._tcp.isFrameReady()) self.assertFalse(self._tcp.checkFrame()) self._tcp.addToFrame(msg) self.assertTrue(self._tcp.isFrameReady()) self.assertTrue(self._tcp.checkFrame()) self._tcp.advanceFrame() self.assertFalse(self._tcp.isFrameReady()) self.assertFalse(self._tcp.checkFrame()) self.assertEqual('', self._ascii.getFrame()) def testTCPFramerTransactionFull(self): ''' Test a full tcp frame transaction ''' msg = "\x00\x01\x12\x34\x00\x04\xff\x02\x12\x34" self._tcp.addToFrame(msg) self.assertTrue(self._tcp.checkFrame()) result = self._tcp.getFrame() self.assertEqual(msg[7:], result) self._tcp.advanceFrame() def testTCPFramerTransactionHalf(self): ''' Test a half completed tcp frame transaction ''' msg1 = "\x00\x01\x12\x34\x00" msg2 = "\x04\xff\x02\x12\x34" self._tcp.addToFrame(msg1) self.assertFalse(self._tcp.checkFrame()) result = self._tcp.getFrame() self.assertEqual('', result) self._tcp.addToFrame(msg2) self.assertTrue(self._tcp.checkFrame()) result = self._tcp.getFrame() self.assertEqual(msg2[2:], result) self._tcp.advanceFrame() def testTCPFramerTransactionHalf2(self): ''' Test a half completed tcp frame transaction ''' msg1 = "\x00\x01\x12\x34\x00\x04\xff" msg2 = "\x02\x12\x34" self._tcp.addToFrame(msg1) self.assertFalse(self._tcp.checkFrame()) result = self._tcp.getFrame() self.assertEqual('', result) self._tcp.addToFrame(msg2) self.assertTrue(self._tcp.checkFrame()) result = self._tcp.getFrame() self.assertEqual(msg2, result) self._tcp.advanceFrame() def testTCPFramerTransactionHalf3(self): ''' Test a half completed tcp frame transaction ''' msg1 = "\x00\x01\x12\x34\x00\x04\xff\x02\x12" msg2 = "\x34" self._tcp.addToFrame(msg1) self.assertFalse(self._tcp.checkFrame()) result = self._tcp.getFrame() self.assertEqual(msg1[7:], result) self._tcp.addToFrame(msg2) self.assertTrue(self._tcp.checkFrame()) result = self._tcp.getFrame() self.assertEqual(msg1[7:] + msg2, result) self._tcp.advanceFrame() def testTCPFramerTransactionShort(self): ''' Test that we can get back on track after an invalid message ''' msg1 = "\x99\x99\x99\x99\x00\x01\x00\x01" msg2 = "\x00\x01\x12\x34\x00\x04\xff\x02\x12\x34" self._tcp.addToFrame(msg1) self.assertFalse(self._tcp.checkFrame()) result = self._tcp.getFrame() self.assertEqual('', result) self._tcp.advanceFrame() self._tcp.addToFrame(msg2) self.assertEqual(10, len(self._tcp._ModbusSocketFramer__buffer)) self.assertTrue(self._tcp.checkFrame()) result = self._tcp.getFrame() self.assertEqual(msg2[7:], result) self._tcp.advanceFrame() def testTCPFramerPopulate(self): ''' Test a tcp frame packet build ''' expected = ModbusRequest() expected.transaction_id = 0x0001 expected.protocol_id = 0x1234 expected.unit_id = 0xff msg = "\x00\x01\x12\x34\x00\x04\xff\x02\x12\x34" self._tcp.addToFrame(msg) self.assertTrue(self._tcp.checkFrame()) actual = ModbusRequest() self._tcp.populateResult(actual) for name in ['transaction_id', 'protocol_id', 'unit_id']: self.assertEqual(getattr(expected, name), getattr(actual, name)) self._tcp.advanceFrame() def testTCPFramerPacket(self): ''' Test a tcp frame packet build ''' old_encode = ModbusRequest.encode ModbusRequest.encode = lambda self: '' message = ModbusRequest() message.transaction_id = 0x0001 message.protocol_id = 0x1234 message.unit_id = 0xff message.function_code = 0x01 expected = "\x00\x01\x12\x34\x00\x02\xff\x01" actual = self._tcp.buildPacket(message) self.assertEqual(expected, actual) ModbusRequest.encode = old_encode #---------------------------------------------------------------------------# # RTU tests #---------------------------------------------------------------------------# def testRTUFramerTransactionReady(self): ''' Test if the checks for a complete frame work ''' self.assertFalse(self._rtu.isFrameReady()) msg_parts = ["\x00\x01\x00", "\x00\x00\x01\xfc\x1b"] self._rtu.addToFrame(msg_parts[0]) self.assertTrue(self._rtu.isFrameReady()) self.assertFalse(self._rtu.checkFrame()) self._rtu.addToFrame(msg_parts[1]) self.assertTrue(self._rtu.isFrameReady()) self.assertTrue(self._rtu.checkFrame()) def testRTUFramerTransactionFull(self): ''' Test a full rtu frame transaction ''' msg = "\x00\x01\x00\x00\x00\x01\xfc\x1b" stripped_msg = msg[1:-2] self._rtu.addToFrame(msg) self.assertTrue(self._rtu.checkFrame()) result = self._rtu.getFrame() self.assertEqual(stripped_msg, result) self._rtu.advanceFrame() def testRTUFramerTransactionHalf(self): ''' Test a half completed rtu frame transaction ''' msg_parts = ["\x00\x01\x00", "\x00\x00\x01\xfc\x1b"] stripped_msg = "".join(msg_parts)[1:-2] self._rtu.addToFrame(msg_parts[0]) self.assertFalse(self._rtu.checkFrame()) self._rtu.addToFrame(msg_parts[1]) self.assertTrue(self._rtu.isFrameReady()) self.assertTrue(self._rtu.checkFrame()) result = self._rtu.getFrame() self.assertEqual(stripped_msg, result) self._rtu.advanceFrame() def testRTUFramerPopulate(self): ''' Test a rtu frame packet build ''' request = ModbusRequest() msg = "\x00\x01\x00\x00\x00\x01\xfc\x1b" self._rtu.addToFrame(msg) self._rtu.populateHeader() self._rtu.populateResult(request) header_dict = self._rtu._ModbusRtuFramer__header self.assertEqual(len(msg), header_dict['len']) self.assertEqual(ord(msg[0]), header_dict['uid']) self.assertEqual(msg[-2:], header_dict['crc']) self.assertEqual(0x00, request.unit_id) def testRTUFramerPacket(self): ''' Test a rtu frame packet build ''' old_encode = ModbusRequest.encode ModbusRequest.encode = lambda self: '' message = ModbusRequest() message.unit_id = 0xff message.function_code = 0x01 expected = "\xff\x01\x81\x80" # only header + CRC - no data actual = self._rtu.buildPacket(message) self.assertEqual(expected, actual) ModbusRequest.encode = old_encode def testRTUDecodeException(self): ''' Test that the RTU framer can decode errors ''' message = "\x00\x90\x02\x9c\x01" actual = self._rtu.addToFrame(message) result = self._rtu.checkFrame() self.assertTrue(result) #---------------------------------------------------------------------------# # ASCII tests #---------------------------------------------------------------------------# def testASCIIFramerTransactionReady(self): ''' Test a ascii frame transaction ''' msg = ':F7031389000A60\r\n' self.assertFalse(self._ascii.isFrameReady()) self.assertFalse(self._ascii.checkFrame()) self._ascii.addToFrame(msg) self.assertTrue(self._ascii.isFrameReady()) self.assertTrue(self._ascii.checkFrame()) self._ascii.advanceFrame() self.assertFalse(self._ascii.isFrameReady()) self.assertFalse(self._ascii.checkFrame()) self.assertEqual('', self._ascii.getFrame()) def testASCIIFramerTransactionFull(self): ''' Test a full ascii frame transaction ''' msg = 'sss:F7031389000A60\r\n' pack = a2b_hex(msg[6:-4]) self._ascii.addToFrame(msg) self.assertTrue(self._ascii.checkFrame()) result = self._ascii.getFrame() self.assertEqual(pack, result) self._ascii.advanceFrame() def testASCIIFramerTransactionHalf(self): ''' Test a half completed ascii frame transaction ''' msg1 = 'sss:F7031389' msg2 = '000A60\r\n' pack = a2b_hex(msg1[6:] + msg2[:-4]) self._ascii.addToFrame(msg1) self.assertFalse(self._ascii.checkFrame()) result = self._ascii.getFrame() self.assertEqual('', result) self._ascii.addToFrame(msg2) self.assertTrue(self._ascii.checkFrame()) result = self._ascii.getFrame() self.assertEqual(pack, result) self._ascii.advanceFrame() def testASCIIFramerPopulate(self): ''' Test a ascii frame packet build ''' request = ModbusRequest() self._ascii.populateResult(request) self.assertEqual(0x00, request.unit_id) def testASCIIFramerPacket(self): ''' Test a ascii frame packet build ''' old_encode = ModbusRequest.encode ModbusRequest.encode = lambda self: '' message = ModbusRequest() message.unit_id = 0xff message.function_code = 0x01 expected = ":FF0100\r\n" actual = self._ascii.buildPacket(message) self.assertEqual(expected, actual) ModbusRequest.encode = old_encode #---------------------------------------------------------------------------# # Binary tests #---------------------------------------------------------------------------# def testBinaryFramerTransactionReady(self): ''' Test a binary frame transaction ''' msg = '\x7b\x01\x03\x00\x00\x00\x05\x85\xC9\x7d' self.assertFalse(self._binary.isFrameReady()) self.assertFalse(self._binary.checkFrame()) self._binary.addToFrame(msg) self.assertTrue(self._binary.isFrameReady()) self.assertTrue(self._binary.checkFrame()) self._binary.advanceFrame() self.assertFalse(self._binary.isFrameReady()) self.assertFalse(self._binary.checkFrame()) self.assertEqual('', self._binary.getFrame()) def testBinaryFramerTransactionFull(self): ''' Test a full binary frame transaction ''' msg = '\x7b\x01\x03\x00\x00\x00\x05\x85\xC9\x7d' pack = msg[3:-3] self._binary.addToFrame(msg) self.assertTrue(self._binary.checkFrame()) result = self._binary.getFrame() self.assertEqual(pack, result) self._binary.advanceFrame() def testBinaryFramerTransactionHalf(self): ''' Test a half completed binary frame transaction ''' msg1 = '\x7b\x01\x03\x00' msg2 = '\x00\x00\x05\x85\xC9\x7d' pack = msg1[3:] + msg2[:-3] self._binary.addToFrame(msg1) self.assertFalse(self._binary.checkFrame()) result = self._binary.getFrame() self.assertEqual('', result) self._binary.addToFrame(msg2) self.assertTrue(self._binary.checkFrame()) result = self._binary.getFrame() self.assertEqual(pack, result) self._binary.advanceFrame() def testBinaryFramerPopulate(self): ''' Test a binary frame packet build ''' request = ModbusRequest() self._binary.populateResult(request) self.assertEqual(0x00, request.unit_id) def testBinaryFramerPacket(self): ''' Test a binary frame packet build ''' old_encode = ModbusRequest.encode ModbusRequest.encode = lambda self: '' message = ModbusRequest() message.unit_id = 0xff message.function_code = 0x01 expected = '\x7b\xff\x01\x81\x80\x7d' actual = self._binary.buildPacket(message) self.assertEqual(expected, actual) ModbusRequest.encode = old_encode #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_version.py0000644000175500017550000000137112607272152016535 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.version import Version class ModbusVersionTest(unittest.TestCase): ''' This is the unittest for the pymodbus._version code ''' def setUp(self): ''' Initializes the test environment ''' pass def tearDown(self): ''' Cleans up the test environment ''' pass def testVersionClass(self): version = Version('test', 1,2,3) self.assertEqual(version.short(), '1.2.3') self.assertEqual(str(version), '[test, version 1.2.3]') #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_device.py0000644000175500017550000002746612607272152016324 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.device import * from pymodbus.events import ModbusEvent, RemoteReceiveEvent from pymodbus.constants import DeviceInformation #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class SimpleDataStoreTest(unittest.TestCase): ''' This is the unittest for the pymodbus.device module ''' #-----------------------------------------------------------------------# # Setup/TearDown #-----------------------------------------------------------------------# def setUp(self): self.info = { 0x00: 'Bashwork', # VendorName 0x01: 'PTM', # ProductCode 0x02: '1.0', # MajorMinorRevision 0x03: 'http://internets.com', # VendorUrl 0x04: 'pymodbus', # ProductName 0x05: 'bashwork', # ModelName 0x06: 'unittest', # UserApplicationName 0x07: 'x', # reserved 0x08: 'x', # reserved 0x10: 'private' # private data } self.ident = ModbusDeviceIdentification(self.info) self.control = ModbusControlBlock() self.access = ModbusAccessControl() self.control.reset() def tearDown(self): ''' Cleans up the test environment ''' del self.ident del self.control del self.access def testUpdateIdentity(self): ''' Test device identification reading ''' self.control.Identity.update(self.ident) self.assertEqual(self.control.Identity.VendorName, 'Bashwork') self.assertEqual(self.control.Identity.ProductCode, 'PTM') self.assertEqual(self.control.Identity.MajorMinorRevision, '1.0') self.assertEqual(self.control.Identity.VendorUrl, 'http://internets.com') self.assertEqual(self.control.Identity.ProductName, 'pymodbus') self.assertEqual(self.control.Identity.ModelName, 'bashwork') self.assertEqual(self.control.Identity.UserApplicationName, 'unittest') def testDeviceInformationFactory(self): ''' Test device identification reading ''' self.control.Identity.update(self.ident) result = DeviceInformationFactory.get(self.control, DeviceInformation.Specific, 0x00) self.assertEqual(result[0x00], 'Bashwork') result = DeviceInformationFactory.get(self.control, DeviceInformation.Basic, 0x00) self.assertEqual(result[0x00], 'Bashwork') self.assertEqual(result[0x01], 'PTM') self.assertEqual(result[0x02], '1.0') result = DeviceInformationFactory.get(self.control, DeviceInformation.Regular, 0x00) self.assertEqual(result[0x00], 'Bashwork') self.assertEqual(result[0x01], 'PTM') self.assertEqual(result[0x02], '1.0') self.assertEqual(result[0x03], 'http://internets.com') self.assertEqual(result[0x04], 'pymodbus') self.assertEqual(result[0x05], 'bashwork') self.assertEqual(result[0x06], 'unittest') def testBasicCommands(self): ''' Test device identification reading ''' self.assertEqual(str(self.ident), "DeviceIdentity") self.assertEqual(str(self.control), "ModbusControl") def testModbusDeviceIdentificationGet(self): ''' Test device identification reading ''' self.assertEqual(self.ident[0x00], 'Bashwork') self.assertEqual(self.ident[0x01], 'PTM') self.assertEqual(self.ident[0x02], '1.0') self.assertEqual(self.ident[0x03], 'http://internets.com') self.assertEqual(self.ident[0x04], 'pymodbus') self.assertEqual(self.ident[0x05], 'bashwork') self.assertEqual(self.ident[0x06], 'unittest') self.assertNotEqual(self.ident[0x07], 'x') self.assertNotEqual(self.ident[0x08], 'x') self.assertEqual(self.ident[0x10], 'private') self.assertEqual(self.ident[0x54], '') def testModbusDeviceIdentificationSummary(self): ''' Test device identification summary creation ''' summary = sorted(self.ident.summary().values()) expected = sorted(self.info.values()[:-3]) # remove private self.assertEqual(summary, expected) def testModbusDeviceIdentificationSet(self): ''' Test a device identification writing ''' self.ident[0x07] = 'y' self.ident[0x08] = 'y' self.ident[0x10] = 'public' self.ident[0x54] = 'testing' self.assertNotEqual('y', self.ident[0x07]) self.assertNotEqual('y', self.ident[0x08]) self.assertEqual('public', self.ident[0x10]) self.assertEqual('testing', self.ident[0x54]) def testModbusControlBlockAsciiModes(self): ''' Test a server control block ascii mode ''' self.assertEqual(id(self.control), id(ModbusControlBlock())) self.control.Mode = 'RTU' self.assertEqual('RTU', self.control.Mode) self.control.Mode = 'FAKE' self.assertNotEqual('FAKE', self.control.Mode) def testModbusControlBlockCounters(self): ''' Tests the MCB counters methods ''' self.assertEqual(0x0, self.control.Counter.BusMessage) for _ in range(10): self.control.Counter.BusMessage += 1 self.control.Counter.SlaveMessage += 1 self.assertEqual(10, self.control.Counter.BusMessage) self.control.Counter.BusMessage = 0x00 self.assertEqual(0, self.control.Counter.BusMessage) self.assertEqual(10, self.control.Counter.SlaveMessage) self.control.Counter.reset() self.assertEqual(0, self.control.Counter.SlaveMessage) def testModbusControlBlockUpdate(self): ''' Tests the MCB counters upate methods ''' values = {'SlaveMessage':5, 'BusMessage':5} self.control.Counter.BusMessage += 1 self.control.Counter.SlaveMessage += 1 self.control.Counter.update(values) self.assertEqual(6, self.control.Counter.SlaveMessage) self.assertEqual(6, self.control.Counter.BusMessage) def testModbusControlBlockIterator(self): ''' Tests the MCB counters iterator ''' self.control.Counter.reset() for _,count in self.control: self.assertEqual(0, count) def testModbusCountersHandlerIterator(self): ''' Tests the MCB counters iterator ''' self.control.Counter.reset() for _,count in self.control.Counter: self.assertEqual(0, count) def testModbusControlBlockCounterSummary(self): ''' Tests retrieving the current counter summary ''' self.assertEqual(0x00, self.control.Counter.summary()) for _ in range(10): self.control.Counter.BusMessage += 1 self.control.Counter.SlaveMessage += 1 self.control.Counter.SlaveNAK += 1 self.control.Counter.BusCharacterOverrun += 1 self.assertEqual(0xa9, self.control.Counter.summary()) self.control.Counter.reset() self.assertEqual(0x00, self.control.Counter.summary()) def testModbusControlBlockListen(self): ''' Tests the MCB listen flag methods ''' self.control.ListenOnly = False self.assertEqual(self.control.ListenOnly, False) self.control.ListenOnly = not self.control.ListenOnly self.assertEqual(self.control.ListenOnly, True) def testModbusControlBlockDelimiter(self): ''' Tests the MCB delimiter setting methods ''' self.control.Delimiter = '\r' self.assertEqual(self.control.Delimiter, '\r') self.control.Delimiter = '=' self.assertEqual(self.control.Delimiter, '=') self.control.Delimiter = 61 self.assertEqual(self.control.Delimiter, '=') def testModbusControlBlockDiagnostic(self): ''' Tests the MCB delimiter setting methods ''' self.assertEqual([False] * 16, self.control.getDiagnosticRegister()) for i in [1,3,4,6]: self.control.setDiagnostic({i:True}); self.assertEqual(True, self.control.getDiagnostic(1)) self.assertEqual(False, self.control.getDiagnostic(2)) actual = [False, True, False, True, True, False, True] + [False] * 9 self.assertEqual(actual, self.control.getDiagnosticRegister()) for i in range(16): self.control.setDiagnostic({i:False}); def testModbusControlBlockInvalidDiagnostic(self): ''' Tests querying invalid MCB counters methods ''' self.assertEqual(None, self.control.getDiagnostic(-1)) self.assertEqual(None, self.control.getDiagnostic(17)) self.assertEqual(None, self.control.getDiagnostic(None)) self.assertEqual(None, self.control.getDiagnostic([1,2,3])) def testAddRemoveSingleClients(self): ''' Test adding and removing a host ''' self.assertFalse(self.access.check("192.168.1.1")) self.access.add("192.168.1.1") self.assertTrue(self.access.check("192.168.1.1")) self.access.add("192.168.1.1") self.access.remove("192.168.1.1") self.assertFalse(self.access.check("192.168.1.1")) def testAddRemoveMultipleClients(self): ''' Test adding and removing a host ''' clients = ["192.168.1.1", "192.168.1.2", "192.168.1.3"] self.access.add(clients) for host in clients: self.assertTrue(self.access.check(host)) self.access.remove(clients) def testNetworkAccessListIterator(self): ''' Test adding and removing a host ''' clients = ["127.0.0.1", "192.168.1.1", "192.168.1.2", "192.168.1.3"] self.access.add(clients) for host in self.access: self.assertTrue(host in clients) for host in clients: self.assertTrue(host in self.access) def testClearingControlEvents(self): ''' Test adding and clearing modbus events ''' self.assertEqual(self.control.Events, []) event = ModbusEvent() self.control.addEvent(event) self.assertEqual(self.control.Events, [event]) self.assertEqual(self.control.Counter.Event, 1) self.control.clearEvents() self.assertEqual(self.control.Events, []) self.assertEqual(self.control.Counter.Event, 1) def testRetrievingControlEvents(self): ''' Test adding and removing a host ''' self.assertEqual(self.control.Events, []) event = RemoteReceiveEvent() self.control.addEvent(event) self.assertEqual(self.control.Events, [event]) packet = self.control.getEvents() self.assertEqual(packet, '\x40') def testModbusPlusStatistics(self): ''' Test device identification reading ''' default = [0x0000] * 55 statistics = ModbusPlusStatistics() self.assertEqual(default, statistics.encode()) statistics.reset() self.assertEqual(default, statistics.encode()) self.assertEqual(default, self.control.Plus.encode()) def testModbusPlusStatisticsHelpers(self): ''' Test modbus plus statistics helper methods ''' statistics = ModbusPlusStatistics() summary = [ [0],[0],[0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0],[0,0],[0], [0,0,0,0,0,0,0,0],[0],[0],[0],[0],[0,0],[0],[0],[0],[0], [0],[0],[0],[0,0],[0],[0],[0],[0],[0,0,0,0,0,0,0,0],[0], [0,0,0,0,0,0,0,0],[0,0],[0],[0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0],[0],[0],[0,0],[0],[0],[0],[0],[0,0], [0],[0],[0],[0],[0],[0,0],[0],[0,0,0,0,0,0,0,0]] self.assertEqual(summary, statistics.summary()) self.assertEqual(0x00, sum(sum(value[1]) for value in statistics)) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_fixes.py0000644000175500017550000000152012607272152016162 0ustar debacledebacle#!/usr/bin/env python import unittest class ModbusFixesTest(unittest.TestCase): ''' This is the unittest for the pymodbus._version code ''' def testTrueFalseDefined(self): ''' Test that True and False are defined on all versions''' try: True,False except NameError: import pymodbus self.assertEqual(True, 1) self.assertEqual(False, 1) def testNullLoggerAttached(self): ''' Test that the null logger is attached''' import logging logger = logging.getLogger('pymodbus') self.assertEqual(len(logger.handlers), 1) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_mei_messages.py0000644000175500017550000001077212607272152017516 0ustar debacledebacle#!/usr/bin/env python ''' MEI Message Test Fixture -------------------------------- This fixture tests the functionality of all the mei based request/response messages: ''' import unittest from pymodbus.mei_message import * from pymodbus.constants import DeviceInformation, MoreData from pymodbus.pdu import ModbusExceptions from pymodbus.device import ModbusControlBlock #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class ModbusMeiMessageTest(unittest.TestCase): ''' This is the unittest for the pymodbus.mei_message module ''' #-----------------------------------------------------------------------# # Read Device Information #-----------------------------------------------------------------------# def testReadDeviceInformationRequestEncode(self): ''' Test basic bit message encoding/decoding ''' params = {'read_code':DeviceInformation.Basic, 'object_id':0x00 } handle = ReadDeviceInformationRequest(**params) result = handle.encode() self.assertEqual(result, '\x0e\x01\x00') self.assertEqual("ReadDeviceInformationRequest(1,0)", str(handle)) def testReadDeviceInformationRequestDecode(self): ''' Test basic bit message encoding/decoding ''' handle = ReadDeviceInformationRequest() handle.decode('\x0e\x01\x00') self.assertEqual(handle.read_code, DeviceInformation.Basic) self.assertEqual(handle.object_id, 0x00) def testReadDeviceInformationRequest(self): ''' Test basic bit message encoding/decoding ''' context = None control = ModbusControlBlock() control.Identity.VendorName = "Company" control.Identity.ProductCode = "Product" control.Identity.MajorMinorevision = "v2.1.12" handle = ReadDeviceInformationRequest() result = handle.execute(context) self.assertTrue(isinstance(result, ReadDeviceInformationResponse)) self.assertTrue(result.information[0x00], "Company") self.assertTrue(result.information[0x01], "Product") self.assertTrue(result.information[0x02], "v2.1.12") def testReadDeviceInformationRequestError(self): ''' Test basic bit message encoding/decoding ''' handle = ReadDeviceInformationRequest() handle.read_code = -1 self.assertEqual(handle.execute(None).function_code, 0xab) handle.read_code = 0x05 self.assertEqual(handle.execute(None).function_code, 0xab) handle.object_id = -1 self.assertEqual(handle.execute(None).function_code, 0xab) handle.object_id = 0x100 self.assertEqual(handle.execute(None).function_code, 0xab) def testReadDeviceInformationResponseEncode(self): ''' Test that the read fifo queue response can encode ''' message = '\x0e\x01\x83\x00\x00\x03' message += '\x00\x07Company\x01\x07Product\x02\x07v2.1.12' dataset = { 0x00: 'Company', 0x01: 'Product', 0x02: 'v2.1.12', } handle = ReadDeviceInformationResponse( read_code=DeviceInformation.Basic, information=dataset) result = handle.encode() self.assertEqual(result, message) self.assertEqual("ReadDeviceInformationResponse(1)", str(handle)) def testReadDeviceInformationResponseDecode(self): ''' Test that the read device information response can decode ''' message = '\x0e\x01\x01\x00\x00\x03' message += '\x00\x07Company\x01\x07Product\x02\x07v2.1.12' handle = ReadDeviceInformationResponse(read_code=0x00, information=[]) handle.decode(message) self.assertEqual(handle.read_code, DeviceInformation.Basic) self.assertEqual(handle.conformity, 0x01) self.assertEqual(handle.information[0x00], 'Company') self.assertEqual(handle.information[0x01], 'Product') self.assertEqual(handle.information[0x02], 'v2.1.12') def testRtuFrameSize(self): ''' Test that the read device information response can decode ''' message = '\x04\x2B\x0E\x01\x81\x00\x01\x01\x00\x06\x66\x6F\x6F\x62\x61\x72\xD7\x3B' result = ReadDeviceInformationResponse.calculateRtuFrameSize(message) self.assertEqual(result, 18) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_bit_write_messages.py0000644000175500017550000001055312607272152020731 0ustar debacledebacle#!/usr/bin/env python ''' Bit Message Test Fixture -------------------------------- This fixture tests the functionality of all the bit based request/response messages: * Read/Write Discretes * Read Coils ''' import unittest from pymodbus.bit_write_message import * from pymodbus.exceptions import * from pymodbus.pdu import ModbusExceptions from modbus_mocks import MockContext, FakeList #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class ModbusBitMessageTests(unittest.TestCase): #-----------------------------------------------------------------------# # Setup/TearDown #-----------------------------------------------------------------------# def setUp(self): ''' Initializes the test environment and builds request/result encoding pairs ''' pass def tearDown(self): ''' Cleans up the test environment ''' pass def testBitWriteBaseRequests(self): messages = { WriteSingleCoilRequest(1, 0xabcd) : '\x00\x01\xff\x00', WriteSingleCoilResponse(1, 0xabcd) : '\x00\x01\xff\x00', WriteMultipleCoilsRequest(1, [True]*5) : '\x00\x01\x00\x05\x01\x1f', WriteMultipleCoilsResponse(1, 5) : '\x00\x01\x00\x05', } for request, expected in messages.iteritems(): self.assertEqual(request.encode(), expected) def testWriteMultipleCoilsRequest(self): request = WriteMultipleCoilsRequest(1, [True]*5) request.decode('\x00\x01\x00\x05\x01\x1f') self.assertEqual(request.byte_count, 1) self.assertEqual(request.address, 1) self.assertEqual(request.values, [True]*5) def testInvalidWriteMultipleCoilsRequest(self): request = WriteMultipleCoilsRequest(1, None) self.assertEquals(request.values, []) def testWriteSingleCoilRequestEncode(self): request = WriteSingleCoilRequest(1, False) self.assertEquals(request.encode(), '\x00\x01\x00\x00') def testWriteSingleCoilExecute(self): context = MockContext(False, default=True) request = WriteSingleCoilRequest(2, True) result = request.execute(context) self.assertEqual(result.exception_code, ModbusExceptions.IllegalAddress) context.valid = True result = request.execute(context) self.assertEqual(result.encode(), '\x00\x02\xff\x00') context = MockContext(True, default=False) request = WriteSingleCoilRequest(2, False) result = request.execute(context) self.assertEqual(result.encode(), '\x00\x02\x00\x00') def testWriteMultipleCoilsExecute(self): context = MockContext(False) # too many values request = WriteMultipleCoilsRequest(2, FakeList(0x123456)) result = request.execute(context) self.assertEqual(result.exception_code, ModbusExceptions.IllegalValue) # bad byte count request = WriteMultipleCoilsRequest(2, [0x00]*4) request.byte_count = 0x00 result = request.execute(context) self.assertEqual(result.exception_code, ModbusExceptions.IllegalValue) # does not validate context.valid = False request = WriteMultipleCoilsRequest(2, [0x00]*4) result = request.execute(context) self.assertEqual(result.exception_code, ModbusExceptions.IllegalAddress) # validated request context.valid = True result = request.execute(context) self.assertEqual(result.encode(), '\x00\x02\x00\x04') def testWriteMultipleCoilsResponse(self): response = WriteMultipleCoilsResponse() response.decode('\x00\x80\x00\x08') self.assertEqual(response.address, 0x80) self.assertEqual(response.count, 0x08) def testSerializingToString(self): requests = [ WriteSingleCoilRequest(1, 0xabcd), WriteSingleCoilResponse(1, 0xabcd), WriteMultipleCoilsRequest(1, [True]*5), WriteMultipleCoilsResponse(1, 5), ] for request in requests: self.assertTrue(str(request) != None) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/__init__.py0000644000175500017550000000000012607272152015534 0ustar debacledebaclepymodbus/test/test_payload.py0000644000175500017550000001666412607272152016514 0ustar debacledebacle#!/usr/bin/env python ''' Payload Utilities Test Fixture -------------------------------- This fixture tests the functionality of the payload utilities. * PayloadBuilder * PayloadDecoder ''' import unittest from pymodbus.exceptions import ParameterException from pymodbus.constants import Endian from pymodbus.payload import BinaryPayloadBuilder, BinaryPayloadDecoder #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class ModbusPayloadUtilityTests(unittest.TestCase): #-----------------------------------------------------------------------# # Setup/TearDown #-----------------------------------------------------------------------# def setUp(self): ''' Initializes the test environment and builds request/result encoding pairs ''' self.little_endian_payload = \ '\x01\x02\x00\x03\x00\x00\x00\x04\x00\x00\x00\x00' \ '\x00\x00\x00\xff\xfe\xff\xfd\xff\xff\xff\xfc\xff' \ '\xff\xff\xff\xff\xff\xff\x00\x00\xa0\x3f\x00\x00' \ '\x00\x00\x00\x00\x19\x40\x74\x65\x73\x74\x11' self.big_endian_payload = \ '\x01\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00' \ '\x00\x00\x04\xff\xff\xfe\xff\xff\xff\xfd\xff\xff' \ '\xff\xff\xff\xff\xff\xfc\x3f\xa0\x00\x00\x40\x19' \ '\x00\x00\x00\x00\x00\x00\x74\x65\x73\x74\x11' self.bitstring = [True, False, False, False, True, False, False, False] def tearDown(self): ''' Cleans up the test environment ''' pass #-----------------------------------------------------------------------# # Payload Builder Tests #-----------------------------------------------------------------------# def testLittleEndianPayloadBuilder(self): ''' Test basic bit message encoding/decoding ''' builder = BinaryPayloadBuilder(endian=Endian.Little) builder.add_8bit_uint(1) builder.add_16bit_uint(2) builder.add_32bit_uint(3) builder.add_64bit_uint(4) builder.add_8bit_int(-1) builder.add_16bit_int(-2) builder.add_32bit_int(-3) builder.add_64bit_int(-4) builder.add_32bit_float(1.25) builder.add_64bit_float(6.25) builder.add_string('test') builder.add_bits(self.bitstring) self.assertEqual(self.little_endian_payload, str(builder)) def testBigEndianPayloadBuilder(self): ''' Test basic bit message encoding/decoding ''' builder = BinaryPayloadBuilder(endian=Endian.Big) builder.add_8bit_uint(1) builder.add_16bit_uint(2) builder.add_32bit_uint(3) builder.add_64bit_uint(4) builder.add_8bit_int(-1) builder.add_16bit_int(-2) builder.add_32bit_int(-3) builder.add_64bit_int(-4) builder.add_32bit_float(1.25) builder.add_64bit_float(6.25) builder.add_string('test') builder.add_bits(self.bitstring) self.assertEqual(self.big_endian_payload, str(builder)) def testPayloadBuilderReset(self): ''' Test basic bit message encoding/decoding ''' builder = BinaryPayloadBuilder() builder.add_8bit_uint(0x12) builder.add_8bit_uint(0x34) builder.add_8bit_uint(0x56) builder.add_8bit_uint(0x78) self.assertEqual('\x12\x34\x56\x78', str(builder)) self.assertEqual(['\x12\x34', '\x56\x78'], builder.build()) builder.reset() self.assertEqual('', str(builder)) self.assertEqual([], builder.build()) #-----------------------------------------------------------------------# # Payload Decoder Tests #-----------------------------------------------------------------------# def testLittleEndianPayloadDecoder(self): ''' Test basic bit message encoding/decoding ''' decoder = BinaryPayloadDecoder(self.little_endian_payload, endian=Endian.Little) self.assertEqual(1, decoder.decode_8bit_uint()) self.assertEqual(2, decoder.decode_16bit_uint()) self.assertEqual(3, decoder.decode_32bit_uint()) self.assertEqual(4, decoder.decode_64bit_uint()) self.assertEqual(-1, decoder.decode_8bit_int()) self.assertEqual(-2, decoder.decode_16bit_int()) self.assertEqual(-3, decoder.decode_32bit_int()) self.assertEqual(-4, decoder.decode_64bit_int()) self.assertEqual(1.25, decoder.decode_32bit_float()) self.assertEqual(6.25, decoder.decode_64bit_float()) self.assertEqual('test', decoder.decode_string(4)) self.assertEqual(self.bitstring, decoder.decode_bits()) def testBigEndianPayloadDecoder(self): ''' Test basic bit message encoding/decoding ''' decoder = BinaryPayloadDecoder(self.big_endian_payload, endian=Endian.Big) self.assertEqual(1, decoder.decode_8bit_uint()) self.assertEqual(2, decoder.decode_16bit_uint()) self.assertEqual(3, decoder.decode_32bit_uint()) self.assertEqual(4, decoder.decode_64bit_uint()) self.assertEqual(-1, decoder.decode_8bit_int()) self.assertEqual(-2, decoder.decode_16bit_int()) self.assertEqual(-3, decoder.decode_32bit_int()) self.assertEqual(-4, decoder.decode_64bit_int()) self.assertEqual(1.25, decoder.decode_32bit_float()) self.assertEqual(6.25, decoder.decode_64bit_float()) self.assertEqual('test', decoder.decode_string(4)) self.assertEqual(self.bitstring, decoder.decode_bits()) def testPayloadDecoderReset(self): ''' Test the payload decoder reset functionality ''' decoder = BinaryPayloadDecoder('\x12\x34') self.assertEqual(0x12, decoder.decode_8bit_uint()) self.assertEqual(0x34, decoder.decode_8bit_uint()) decoder.reset() self.assertEqual(0x3412, decoder.decode_16bit_uint()) def testPayloadDecoderRegisterFactory(self): ''' Test the payload decoder reset functionality ''' payload = [1,2,3,4] decoder = BinaryPayloadDecoder.fromRegisters(payload, endian=Endian.Little) encoded = '\x01\x00\x02\x00\x03\x00\x04\x00' self.assertEqual(encoded, decoder.decode_string(8)) decoder = BinaryPayloadDecoder.fromRegisters(payload, endian=Endian.Big) encoded = '\x00\x01\x00\x02\x00\x03\x00\x04' self.assertEqual(encoded, decoder.decode_string(8)) self.assertRaises(ParameterException, lambda: BinaryPayloadDecoder.fromRegisters('abcd')) def testPayloadDecoderCoilFactory(self): ''' Test the payload decoder reset functionality ''' payload = [1,0,0,0, 1,0,0,0, 0,0,0,1, 0,0,0,1] decoder = BinaryPayloadDecoder.fromCoils(payload, endian=Endian.Little) encoded = '\x11\x88' self.assertEqual(encoded, decoder.decode_string(2)) decoder = BinaryPayloadDecoder.fromCoils(payload, endian=Endian.Big) encoded = '\x11\x88' self.assertEqual(encoded, decoder.decode_string(2)) self.assertRaises(ParameterException, lambda: BinaryPayloadDecoder.fromCoils('abcd')) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_register_read_messages.py0000644000175500017550000001572012607272152021561 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.register_read_message import * from pymodbus.register_read_message import ReadRegistersRequestBase from pymodbus.register_read_message import ReadRegistersResponseBase from pymodbus.exceptions import * from pymodbus.pdu import ModbusExceptions from modbus_mocks import MockContext, FakeList #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class ReadRegisterMessagesTest(unittest.TestCase): ''' Register Message Test Fixture -------------------------------- This fixture tests the functionality of all the register based request/response messages: * Read/Write Input Registers * Read Holding Registers ''' def setUp(self): ''' Initializes the test environment and builds request/result encoding pairs ''' arguments = { 'read_address': 1, 'read_count': 5, 'write_address': 1, 'write_registers': [0x00]*5, } self.value = 0xabcd self.values = [0xa, 0xb, 0xc] self.request_read = { ReadRegistersRequestBase(1, 5) :'\x00\x01\x00\x05', ReadHoldingRegistersRequest(1, 5) :'\x00\x01\x00\x05', ReadInputRegistersRequest(1,5) :'\x00\x01\x00\x05', ReadWriteMultipleRegistersRequest(**arguments) :'\x00\x01\x00\x05\x00\x01\x00' '\x05\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', } self.response_read = { ReadRegistersResponseBase(self.values) :'\x06\x00\x0a\x00\x0b\x00\x0c', ReadHoldingRegistersResponse(self.values) :'\x06\x00\x0a\x00\x0b\x00\x0c', ReadInputRegistersResponse(self.values) :'\x06\x00\x0a\x00\x0b\x00\x0c', ReadWriteMultipleRegistersResponse(self.values) :'\x06\x00\x0a\x00\x0b\x00\x0c', } def tearDown(self): ''' Cleans up the test environment ''' del self.request_read del self.response_read def testReadRegisterResponseBase(self): response = ReadRegistersResponseBase(range(10)) for index in range(10): self.assertEqual(response.getRegister(index), index) def testRegisterReadRequests(self): for request, response in self.request_read.iteritems(): self.assertEqual(request.encode(), response) def testRegisterReadResponses(self): for request, response in self.response_read.iteritems(): self.assertEqual(request.encode(), response) def testRegisterReadResponseDecode(self): registers = [ [0x0a,0x0b,0x0c], [0x0a,0x0b,0x0c], [0x0a,0x0b,0x0c], [0x0a,0x0b,0x0c, 0x0a,0x0b,0x0c], ] values = sorted(self.response_read.iteritems()) for packet, register in zip(values, registers): request, response = packet request.decode(response) self.assertEqual(request.registers, register) def testRegisterReadRequestsCountErrors(self): ''' This tests that the register request messages will break on counts that are out of range ''' mock = FakeList(0x800) requests = [ ReadHoldingRegistersRequest(1, 0x800), ReadInputRegistersRequest(1,0x800), ReadWriteMultipleRegistersRequest(read_address=1, read_count=0x800, write_address=1, write_registers=5), ReadWriteMultipleRegistersRequest(read_address=1, read_count=5, write_address=1, write_registers=mock), ] for request in requests: result = request.execute(None) self.assertEqual(ModbusExceptions.IllegalValue, result.exception_code) def testRegisterReadRequestsValidateErrors(self): ''' This tests that the register request messages will break on counts that are out of range ''' context = MockContext() requests = [ ReadHoldingRegistersRequest(-1, 5), ReadInputRegistersRequest(-1,5), #ReadWriteMultipleRegistersRequest(-1,5,1,5), #ReadWriteMultipleRegistersRequest(1,5,-1,5), ] for request in requests: result = request.execute(context) self.assertEqual(ModbusExceptions.IllegalAddress, result.exception_code) def testRegisterReadRequestsExecute(self): ''' This tests that the register request messages will break on counts that are out of range ''' context = MockContext(True) requests = [ ReadHoldingRegistersRequest(-1, 5), ReadInputRegistersRequest(-1,5), ] for request in requests: response = request.execute(context) self.assertEqual(request.function_code, response.function_code) def testReadWriteMultipleRegistersRequest(self): context = MockContext(True) request = ReadWriteMultipleRegistersRequest(read_address=1, read_count=10, write_address=1, write_registers=[0x00]) response = request.execute(context) self.assertEqual(request.function_code, response.function_code) def testReadWriteMultipleRegistersValidate(self): context = MockContext() context.validate = lambda f,a,c: a == 1 request = ReadWriteMultipleRegistersRequest(read_address=1, read_count=10, write_address=2, write_registers=[0x00]) response = request.execute(context) self.assertEqual(response.exception_code, ModbusExceptions.IllegalAddress) context.validate = lambda f,a,c: a == 2 response = request.execute(context) self.assertEqual(response.exception_code, ModbusExceptions.IllegalAddress) request.write_byte_count = 0x100 response = request.execute(context) self.assertEqual(response.exception_code, ModbusExceptions.IllegalValue) def testReadWriteMultipleRegistersRequestDecode(self): request, response = sorted(self.request_read.items())[-1] request.decode(response) self.assertEqual(request.read_address, 0x01) self.assertEqual(request.write_address, 0x01) self.assertEqual(request.read_count, 0x05) self.assertEqual(request.write_count, 0x05) self.assertEqual(request.write_byte_count, 0x0a) self.assertEqual(request.write_registers, [0x00]*5) def testSerializingToString(self): for request in self.request_read.iterkeys(): self.assertTrue(str(request) != None) for request in self.response_read.iterkeys(): self.assertTrue(str(request) != None) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_pdu.py0000644000175500017550000000573212607272152015645 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.pdu import * from pymodbus.exceptions import * class SimplePduTest(unittest.TestCase): ''' This is the unittest for the pymod.pdu module ''' def setUp(self): ''' Initializes the test environment ''' self.badRequests = ( # ModbusPDU(), ModbusRequest(), ModbusResponse(), ) self.illegal = IllegalFunctionRequest(1) self.exception = ExceptionResponse(1,1) def tearDown(self): ''' Cleans up the test environment ''' del self.badRequests del self.illegal del self.exception def testNotImpelmented(self): ''' Test a base classes for not implemented funtions ''' for r in self.badRequests: self.assertRaises(NotImplementedException, r.encode) for r in self.badRequests: self.assertRaises(NotImplementedException, r.decode, None) def testErrorMethods(self): ''' Test all error methods ''' self.illegal.decode("12345") self.illegal.execute(None) result = self.exception.encode() self.exception.decode(result) self.assertEqual(result, '\x01') self.assertEqual(self.exception.exception_code, 1) def testRequestExceptionFactory(self): ''' Test all error methods ''' request = ModbusRequest() request.function_code = 1 errors = dict((ModbusExceptions.decode(c), c) for c in range(1,20)) for error, code in errors.iteritems(): result = request.doException(code) self.assertEqual(str(result), "Exception Response(129, 1, %s)" % error) def testCalculateRtuFrameSize(self): ''' Test the calculation of Modbus/RTU frame sizes ''' self.assertRaises(NotImplementedException, ModbusRequest.calculateRtuFrameSize, "") ModbusRequest._rtu_frame_size = 5 self.assertEqual(ModbusRequest.calculateRtuFrameSize(""), 5) del ModbusRequest._rtu_frame_size ModbusRequest._rtu_byte_count_pos = 2 self.assertEqual(ModbusRequest.calculateRtuFrameSize( "\x11\x01\x05\xcd\x6b\xb2\x0e\x1b\x45\xe6"), 0x05 + 5) del ModbusRequest._rtu_byte_count_pos self.assertRaises(NotImplementedException, ModbusResponse.calculateRtuFrameSize, "") ModbusResponse._rtu_frame_size = 12 self.assertEqual(ModbusResponse.calculateRtuFrameSize(""), 12) del ModbusResponse._rtu_frame_size ModbusResponse._rtu_byte_count_pos = 2 self.assertEqual(ModbusResponse.calculateRtuFrameSize( "\x11\x01\x05\xcd\x6b\xb2\x0e\x1b\x45\xe6"), 0x05 + 5) del ModbusResponse._rtu_byte_count_pos #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_datastore.py0000644000175500017550000001231412607272152017035 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.datastore import * from pymodbus.datastore.store import BaseModbusDataBlock from pymodbus.exceptions import NotImplementedException from pymodbus.exceptions import NoSuchSlaveException from pymodbus.exceptions import ParameterException from pymodbus.datastore.remote import RemoteSlaveContext class ModbusDataStoreTest(unittest.TestCase): ''' This is the unittest for the pymodbus.datastore module ''' def setUp(self): pass def tearDown(self): ''' Cleans up the test environment ''' pass def testModbusDataBlock(self): ''' Test a base data block store ''' block = BaseModbusDataBlock() block.default(10, True) self.assertNotEqual(str(block), None) self.assertEqual(block.default_value, True) self.assertEqual(block.values, [True]*10) block.default_value = False block.reset() self.assertEqual(block.values, [False]*10) def testModbusDataBlockIterate(self): ''' Test a base data block store ''' block = BaseModbusDataBlock() block.default(10, False) for idx,value in block: self.assertEqual(value, False) block.values = {0 : False, 2 : False, 3 : False } for idx,value in block: self.assertEqual(value, False) def testModbusDataBlockOther(self): ''' Test a base data block store ''' block = BaseModbusDataBlock() self.assertRaises(NotImplementedException, lambda: block.validate(1,1)) self.assertRaises(NotImplementedException, lambda: block.getValues(1,1)) self.assertRaises(NotImplementedException, lambda: block.setValues(1,1)) def testModbusSequentialDataBlock(self): ''' Test a sequential data block store ''' block = ModbusSequentialDataBlock(0x00, [False]*10) self.assertFalse(block.validate(-1, 0)) self.assertFalse(block.validate(0, 20)) self.assertFalse(block.validate(10, 1)) self.assertTrue(block.validate(0x00, 10)) block.setValues(0x00, True) self.assertEqual(block.getValues(0x00, 1), [True]) block.setValues(0x00, [True]*10) self.assertEqual(block.getValues(0x00, 10), [True]*10) def testModbusSequentialDataBlockFactory(self): ''' Test the sequential data block store factory ''' block = ModbusSequentialDataBlock.create() self.assertEqual(block.getValues(0x00, 65536), [False]*65536) block = ModbusSequentialDataBlock(0x00, 0x01) self.assertEqual(block.values, [0x01]) def testModbusSparseDataBlock(self): ''' Test a sparse data block store ''' values = dict(enumerate([True]*10)) block = ModbusSparseDataBlock(values) self.assertFalse(block.validate(-1, 0)) self.assertFalse(block.validate(0, 20)) self.assertFalse(block.validate(10, 1)) self.assertTrue(block.validate(0x00, 10)) self.assertTrue(block.validate(0x00, 10)) self.assertFalse(block.validate(0, 0)) self.assertFalse(block.validate(5, 0)) block.setValues(0x00, True) self.assertEqual(block.getValues(0x00, 1), [True]) block.setValues(0x00, [True]*10) self.assertEqual(block.getValues(0x00, 10), [True]*10) block.setValues(0x00, dict(enumerate([False]*10))) self.assertEqual(block.getValues(0x00, 10), [False]*10) def testModbusSparseDataBlockFactory(self): ''' Test the sparse data block store factory ''' block = ModbusSparseDataBlock.create() self.assertEqual(block.getValues(0x00, 65536), [False]*65536) def testModbusSparseDataBlockOther(self): block = ModbusSparseDataBlock([True]*10) self.assertEqual(block.getValues(0x00, 10), [True]*10) self.assertRaises(ParameterException, lambda: ModbusSparseDataBlock(True)) def testModbusSlaveContext(self): ''' Test a modbus slave context ''' store = { 'di' : ModbusSequentialDataBlock(0, [False]*10), 'co' : ModbusSequentialDataBlock(0, [False]*10), 'ir' : ModbusSequentialDataBlock(0, [False]*10), 'hr' : ModbusSequentialDataBlock(0, [False]*10), } context = ModbusSlaveContext(**store) self.assertNotEqual(str(context), None) for fx in [1,2,3,4]: context.setValues(fx, 0, [True]*10) self.assertTrue(context.validate(fx, 0,10)) self.assertEqual(context.getValues(fx, 0,10), [True]*10) context.reset() for fx in [1,2,3,4]: self.assertTrue(context.validate(fx, 0,10)) self.assertEqual(context.getValues(fx, 0,10), [False]*10) def testModbusServerContext(self): ''' Test a modbus server context ''' def _set(ctx): ctx[0xffff] = None context = ModbusServerContext(single=False) self.assertRaises(NoSuchSlaveException, lambda: _set(context)) self.assertRaises(NoSuchSlaveException, lambda: context[0xffff]) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_server_sync.py0000644000175500017550000003512512607272152017416 0ustar debacledebacle#!/usr/bin/env python import unittest from mock import patch, Mock import SocketServer import serial import socket from pymodbus.device import ModbusDeviceIdentification from pymodbus.server.sync import ModbusBaseRequestHandler from pymodbus.server.sync import ModbusSingleRequestHandler from pymodbus.server.sync import ModbusConnectedRequestHandler from pymodbus.server.sync import ModbusDisconnectedRequestHandler from pymodbus.server.sync import ModbusTcpServer, ModbusUdpServer, ModbusSerialServer from pymodbus.server.sync import StartTcpServer, StartUdpServer, StartSerialServer from pymodbus.exceptions import NotImplementedException from pymodbus.bit_read_message import ReadCoilsRequest, ReadCoilsResponse #---------------------------------------------------------------------------# # Mock Classes #---------------------------------------------------------------------------# class MockServer(object): def __init__(self): self.framer = lambda _: "framer" self.decoder = "decoder" self.threads = [] self.context = {} #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class SynchronousServerTest(unittest.TestCase): ''' This is the unittest for the pymodbus.server.sync module ''' #-----------------------------------------------------------------------# # Test Base Request Handler #-----------------------------------------------------------------------# def testBaseHandlerUndefinedMethods(self): ''' Test the base handler undefined methods''' handler = SocketServer.BaseRequestHandler(None, None, None) handler.__class__ = ModbusBaseRequestHandler self.assertRaises(NotImplementedException, lambda: handler.send(None)) self.assertRaises(NotImplementedException, lambda: handler.handle()) def testBaseHandlerMethods(self): ''' Test the base class for all the clients ''' request = ReadCoilsRequest(1, 1) address = ('server', 12345) server = MockServer() with patch.object(ModbusBaseRequestHandler, 'handle') as mock_handle: with patch.object(ModbusBaseRequestHandler, 'send') as mock_send: mock_handle.return_value = True mock_send.return_value = True handler = ModbusBaseRequestHandler(request, address, server) self.assertEqual(handler.running, True) self.assertEqual(handler.framer, 'framer') handler.execute(request) self.assertEqual(mock_send.call_count, 1) server.context[0x00] = object() handler.execute(request) self.assertEqual(mock_send.call_count, 2) #-----------------------------------------------------------------------# # Test Single Request Handler #-----------------------------------------------------------------------# def testModbusSingleRequestHandlerSend(self): handler = SocketServer.BaseRequestHandler(None, None, None) handler.__class__ = ModbusSingleRequestHandler handler.framer = Mock() handler.framer.buildPacket.return_value = "message" handler.request = Mock() request = ReadCoilsResponse([1]) handler.send(request) self.assertEqual(handler.request.send.call_count, 1) request.should_respond = False handler.send(request) self.assertEqual(handler.request.send.call_count, 1) def testModbusSingleRequestHandlerHandle(self): handler = SocketServer.BaseRequestHandler(None, None, None) handler.__class__ = ModbusSingleRequestHandler handler.framer = Mock() handler.framer.buildPacket.return_value = "message" handler.request = Mock() handler.request.recv.return_value = "\x12\x34" # exit if we are not running handler.running = False handler.handle() self.assertEqual(handler.framer.processIncomingPacket.call_count, 0) # run forever if we are running def _callback1(a, b): handler.running = False # stop infinite loop handler.framer.processIncomingPacket.side_effect = _callback1 handler.running = True handler.handle() self.assertEqual(handler.framer.processIncomingPacket.call_count, 1) # exceptions are simply ignored def _callback2(a, b): if handler.framer.processIncomingPacket.call_count == 2: raise Exception("example exception") else: handler.running = False # stop infinite loop handler.framer.processIncomingPacket.side_effect = _callback2 handler.running = True handler.handle() self.assertEqual(handler.framer.processIncomingPacket.call_count, 3) #-----------------------------------------------------------------------# # Test Connected Request Handler #-----------------------------------------------------------------------# def testModbusConnectedRequestHandlerSend(self): handler = SocketServer.BaseRequestHandler(None, None, None) handler.__class__ = ModbusConnectedRequestHandler handler.framer = Mock() handler.framer.buildPacket.return_value = "message" handler.request = Mock() request = ReadCoilsResponse([1]) handler.send(request) self.assertEqual(handler.request.send.call_count, 1) request.should_respond = False handler.send(request) self.assertEqual(handler.request.send.call_count, 1) def testModbusConnectedRequestHandlerHandle(self): handler = SocketServer.BaseRequestHandler(None, None, None) handler.__class__ = ModbusConnectedRequestHandler handler.framer = Mock() handler.framer.buildPacket.return_value = "message" handler.request = Mock() handler.request.recv.return_value = "\x12\x34" # exit if we are not running handler.running = False handler.handle() self.assertEqual(handler.framer.processIncomingPacket.call_count, 0) # run forever if we are running def _callback(a, b): handler.running = False # stop infinite loop handler.framer.processIncomingPacket.side_effect = _callback handler.running = True handler.handle() self.assertEqual(handler.framer.processIncomingPacket.call_count, 1) # socket errors cause the client to disconnect handler.framer.processIncomingPacket.side_effect = socket.error() handler.running = True handler.handle() self.assertEqual(handler.framer.processIncomingPacket.call_count, 2) # every other exception causes the client to disconnect handler.framer.processIncomingPacket.side_effect = Exception() handler.running = True handler.handle() self.assertEqual(handler.framer.processIncomingPacket.call_count, 3) # receiving no data causes the client to disconnect handler.request.recv.return_value = None handler.running = True handler.handle() self.assertEqual(handler.framer.processIncomingPacket.call_count, 3) #-----------------------------------------------------------------------# # Test Disconnected Request Handler #-----------------------------------------------------------------------# def testModbusDisconnectedRequestHandlerSend(self): handler = SocketServer.BaseRequestHandler(None, None, None) handler.__class__ = ModbusDisconnectedRequestHandler handler.framer = Mock() handler.framer.buildPacket.return_value = "message" handler.request = Mock() request = ReadCoilsResponse([1]) handler.send(request) self.assertEqual(handler.request.sendto.call_count, 1) request.should_respond = False handler.send(request) self.assertEqual(handler.request.sendto.call_count, 1) def testModbusDisconnectedRequestHandlerHandle(self): handler = SocketServer.BaseRequestHandler(None, None, None) handler.__class__ = ModbusDisconnectedRequestHandler handler.framer = Mock() handler.framer.buildPacket.return_value = "message" handler.request = ("\x12\x34", handler.request) # exit if we are not running handler.running = False handler.handle() self.assertEqual(handler.framer.processIncomingPacket.call_count, 0) # run forever if we are running def _callback(a, b): handler.running = False # stop infinite loop handler.framer.processIncomingPacket.side_effect = _callback handler.running = True handler.handle() self.assertEqual(handler.framer.processIncomingPacket.call_count, 1) # socket errors cause the client to disconnect handler.request = ("\x12\x34", handler.request) handler.framer.processIncomingPacket.side_effect = socket.error() handler.running = True handler.handle() self.assertEqual(handler.framer.processIncomingPacket.call_count, 2) # every other exception causes the client to disconnect handler.request = ("\x12\x34", handler.request) handler.framer.processIncomingPacket.side_effect = Exception() handler.running = True handler.handle() self.assertEqual(handler.framer.processIncomingPacket.call_count, 3) # receiving no data causes the client to disconnect handler.request = (None, handler.request) handler.running = True handler.handle() self.assertEqual(handler.framer.processIncomingPacket.call_count, 3) #-----------------------------------------------------------------------# # Test TCP Server #-----------------------------------------------------------------------# def testTcpServerClose(self): ''' test that the synchronous TCP server closes correctly ''' with patch.object(socket.socket, 'bind') as mock_socket: identity = ModbusDeviceIdentification(info={0x00: 'VendorName'}) server = ModbusTcpServer(context=None, identity=identity) server.threads.append(Mock(**{'running': True})) server.server_close() self.assertEqual(server.control.Identity.VendorName, 'VendorName') self.assertFalse(server.threads[0].running) def testTcpServerProcess(self): ''' test that the synchronous TCP server processes requests ''' with patch('SocketServer.ThreadingTCPServer') as mock_server: server = ModbusTcpServer(None) server.process_request('request', 'client') self.assertTrue(mock_server.process_request.called) #-----------------------------------------------------------------------# # Test UDP Server #-----------------------------------------------------------------------# def testUdpServerClose(self): ''' test that the synchronous UDP server closes correctly ''' with patch.object(socket.socket, 'bind') as mock_socket: identity = ModbusDeviceIdentification(info={0x00: 'VendorName'}) server = ModbusUdpServer(context=None, identity=identity) server.threads.append(Mock(**{'running': True})) server.server_close() self.assertEqual(server.control.Identity.VendorName, 'VendorName') self.assertFalse(server.threads[0].running) def testUdpServerProcess(self): ''' test that the synchronous UDP server processes requests ''' with patch('SocketServer.ThreadingUDPServer') as mock_server: server = ModbusUdpServer(None) request = ('data', 'socket') server.process_request(request, 'client') self.assertTrue(mock_server.process_request.called) #-----------------------------------------------------------------------# # Test Serial Server #-----------------------------------------------------------------------# def testSerialServerConnect(self): with patch.object(serial, 'Serial') as mock_serial: mock_serial.return_value = "socket" identity = ModbusDeviceIdentification(info={0x00: 'VendorName'}) server = ModbusSerialServer(context=None, identity=identity) self.assertEqual(server.socket, "socket") self.assertEqual(server.control.Identity.VendorName, 'VendorName') server._connect() self.assertEqual(server.socket, "socket") with patch.object(serial, 'Serial') as mock_serial: mock_serial.side_effect = serial.SerialException() server = ModbusSerialServer(None) self.assertEqual(server.socket, None) def testSerialServerServeForever(self): ''' test that the synchronous serial server closes correctly ''' with patch.object(serial, 'Serial') as mock_serial: with patch('pymodbus.server.sync.ModbusSingleRequestHandler') as mock_handler: server = ModbusSerialServer(None) instance = mock_handler.return_value instance.handle.side_effect = server.server_close server.serve_forever() instance.handle.assert_any_call() def testSerialServerClose(self): ''' test that the synchronous serial server closes correctly ''' with patch.object(serial, 'Serial') as mock_serial: instance = mock_serial.return_value server = ModbusSerialServer(None) server.server_close() instance.close.assert_any_call() #-----------------------------------------------------------------------# # Test Synchronous Factories #-----------------------------------------------------------------------# def testStartTcpServer(self): ''' Test the tcp server starting factory ''' with patch.object(ModbusTcpServer, 'serve_forever') as mock_server: with patch.object(SocketServer.TCPServer, 'server_bind') as mock_binder: StartTcpServer() def testStartUdpServer(self): ''' Test the udp server starting factory ''' with patch.object(ModbusUdpServer, 'serve_forever') as mock_server: with patch.object(SocketServer.UDPServer, 'server_bind') as mock_binder: StartUdpServer() def testStartSerialServer(self): ''' Test the serial server starting factory ''' with patch.object(ModbusSerialServer, 'serve_forever') as mock_server: StartSerialServer() #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_events.py0000644000175500017550000000476412607272152016365 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.events import * from pymodbus.exceptions import NotImplementedException from pymodbus.exceptions import ParameterException class ModbusEventsTest(unittest.TestCase): ''' This is the unittest for the pymodbus.device module ''' def setUp(self): ''' Sets up the test environment ''' pass def tearDown(self): ''' Cleans up the test environment ''' pass def testModbusEventBaseClass(self): event = ModbusEvent() self.assertRaises(NotImplementedException, event.encode) self.assertRaises(NotImplementedException, lambda: event.decode(None)) def testRemoteReceiveEvent(self): event = RemoteReceiveEvent() event.decode('\x70') self.assertTrue(event.overrun) self.assertTrue(event.listen) self.assertTrue(event.broadcast) def testRemoteSentEvent(self): event = RemoteSendEvent() result = event.encode() self.assertEqual(result, '\x40') event.decode('\x7f') self.assertTrue(event.read) self.assertTrue(event.slave_abort) self.assertTrue(event.slave_busy) self.assertTrue(event.slave_nak) self.assertTrue(event.write_timeout) self.assertTrue(event.listen) def testRemoteSentEventEncode(self): arguments = { 'read' : True, 'slave_abort' : True, 'slave_busy' : True, 'slave_nak' : True, 'write_timeout' : True, 'listen' : True, } event = RemoteSendEvent(**arguments) result = event.encode() self.assertEqual(result, '\x7f') def testEnteredListenModeEvent(self): event = EnteredListenModeEvent() result = event.encode() self.assertEqual(result, '\x04') event.decode('\x04') self.assertEqual(event.value, 0x04) self.assertRaises(ParameterException, lambda: event.decode('\x00')) def testCommunicationRestartEvent(self): event = CommunicationRestartEvent() result = event.encode() self.assertEqual(result, '\x00') event.decode('\x00') self.assertEqual(event.value, 0x00) self.assertRaises(ParameterException, lambda: event.decode('\x04')) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_remote_datastore.py0000644000175500017550000000504112607272152020407 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.exceptions import NotImplementedException from pymodbus.datastore.remote import RemoteSlaveContext from pymodbus.bit_read_message import * from pymodbus.bit_write_message import * from pymodbus.register_read_message import * from pymodbus.pdu import ExceptionResponse from modbus_mocks import mock class RemoteModbusDataStoreTest(unittest.TestCase): ''' This is the unittest for the pymodbus.datastore.remote module ''' def testRemoteSlaveContext(self): ''' Test a modbus remote slave context ''' context = RemoteSlaveContext(None) self.assertNotEqual(str(context), None) self.assertRaises(NotImplementedException, lambda: context.reset()) def testRemoteSlaveSetValues(self): ''' Test setting values against a remote slave context ''' client = mock() client.write_coils = lambda a,b: WriteMultipleCoilsResponse() context = RemoteSlaveContext(client) result = context.setValues(1, 0, [1]) self.assertTrue(True) def testRemoteSlaveGetValues(self): ''' Test getting values from a remote slave context ''' client = mock() client.read_coils = lambda a,b: ReadCoilsResponse([1]*10) client.read_input_registers = lambda a,b: ReadInputRegistersResponse([10]*10) client.read_holding_registers = lambda a,b: ExceptionResponse(0x15) context = RemoteSlaveContext(client) result = context.getValues(1, 0, 10) self.assertEqual(result, [1]*10) result = context.getValues(4, 0, 10) self.assertEqual(result, [10]*10) result = context.getValues(3, 0, 10) self.assertNotEqual(result, [10]*10) def testRemoteSlaveValidateValues(self): ''' Test validating against a remote slave context ''' client = mock() client.read_coils = lambda a,b: ReadCoilsResponse([1]*10) client.read_input_registers = lambda a,b: ReadInputRegistersResponse([10]*10) client.read_holding_registers = lambda a,b: ExceptionResponse(0x15) context = RemoteSlaveContext(client) result = context.validate(1, 0, 10) self.assertTrue(result) result = context.validate(4, 0, 10) self.assertTrue(result) result = context.validate(3, 0, 10) self.assertFalse(result) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_client_async.py0000644000175500017550000001622612607272152017530 0ustar debacledebacle#!/usr/bin/env python import unittest from mock import Mock from pymodbus.client.async import ModbusClientProtocol, ModbusUdpClientProtocol from pymodbus.client.async import ModbusClientFactory from pymodbus.exceptions import ConnectionException from pymodbus.exceptions import ParameterException from pymodbus.transaction import ModbusSocketFramer from pymodbus.bit_read_message import ReadCoilsRequest, ReadCoilsResponse #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class AsynchronousClientTest(unittest.TestCase): ''' This is the unittest for the pymodbus.client.async module ''' #-----------------------------------------------------------------------# # Test Client Protocol #-----------------------------------------------------------------------# def testClientProtocolInit(self): ''' Test the client protocol initialize ''' protocol = ModbusClientProtocol() self.assertEqual(0, len(list(protocol.transaction))) self.assertFalse(protocol._connected) self.assertTrue(isinstance(protocol.framer, ModbusSocketFramer)) framer = object() protocol = ModbusClientProtocol(framer=framer) self.assertEqual(0, len(list(protocol.transaction))) self.assertFalse(protocol._connected) self.assertTrue(framer is protocol.framer) def testClientProtocolConnect(self): ''' Test the client protocol connect ''' protocol = ModbusClientProtocol() self.assertFalse(protocol._connected) protocol.connectionMade() self.assertTrue(protocol._connected) def testClientProtocolDisconnect(self): ''' Test the client protocol disconnect ''' protocol = ModbusClientProtocol() protocol.connectionMade() def handle_failure(failure): self.assertTrue(isinstance(failure.value, ConnectionException)) d = protocol._buildResponse(0x00) d.addErrback(handle_failure) self.assertTrue(protocol._connected) protocol.connectionLost('because') self.assertFalse(protocol._connected) def testClientProtocolDataReceived(self): ''' Test the client protocol data received ''' protocol = ModbusClientProtocol() protocol.connectionMade() out = [] data = '\x00\x00\x12\x34\x00\x06\xff\x01\x01\x02\x00\x04' # setup existing request d = protocol._buildResponse(0x00) d.addCallback(lambda v: out.append(v)) protocol.dataReceived(data) self.assertTrue(isinstance(out[0], ReadCoilsResponse)) def testClientProtocolExecute(self): ''' Test the client protocol execute method ''' protocol = ModbusClientProtocol() protocol.connectionMade() protocol.transport = Mock() protocol.transport.write = Mock() request = ReadCoilsRequest(1, 1) d = protocol.execute(request) tid = request.transaction_id self.assertEqual(d, protocol.transaction.getTransaction(tid)) def testClientProtocolHandleResponse(self): ''' Test the client protocol handles responses ''' protocol = ModbusClientProtocol() protocol.connectionMade() out = [] reply = ReadCoilsRequest(1, 1) reply.transaction_id = 0x00 # handle skipped cases protocol._handleResponse(None) protocol._handleResponse(reply) # handle existing cases d = protocol._buildResponse(0x00) d.addCallback(lambda v: out.append(v)) protocol._handleResponse(reply) self.assertEqual(out[0], reply) def testClientProtocolBuildResponse(self): ''' Test the udp client protocol builds responses ''' protocol = ModbusClientProtocol() self.assertEqual(0, len(list(protocol.transaction))) def handle_failure(failure): self.assertTrue(isinstance(failure.value, ConnectionException)) d = protocol._buildResponse(0x00) d.addErrback(handle_failure) self.assertEqual(0, len(list(protocol.transaction))) protocol._connected = True d = protocol._buildResponse(0x00) self.assertEqual(1, len(list(protocol.transaction))) #-----------------------------------------------------------------------# # Test Udp Client Protocol #-----------------------------------------------------------------------# def testUdpClientProtocolInit(self): ''' Test the udp client protocol initialize ''' protocol = ModbusUdpClientProtocol() self.assertEqual(0, len(list(protocol.transaction))) self.assertTrue(isinstance(protocol.framer, ModbusSocketFramer)) framer = object() protocol = ModbusClientProtocol(framer=framer) self.assertTrue(framer is protocol.framer) def testUdpClientProtocolDataReceived(self): ''' Test the udp client protocol data received ''' protocol = ModbusUdpClientProtocol() out = [] data = '\x00\x00\x12\x34\x00\x06\xff\x01\x01\x02\x00\x04' server = ('127.0.0.1', 12345) # setup existing request d = protocol._buildResponse(0x00) d.addCallback(lambda v: out.append(v)) protocol.datagramReceived(data, server) self.assertTrue(isinstance(out[0], ReadCoilsResponse)) def testUdpClientProtocolExecute(self): ''' Test the udp client protocol execute method ''' protocol = ModbusUdpClientProtocol() protocol.transport = Mock() protocol.transport.write = Mock() request = ReadCoilsRequest(1, 1) d = protocol.execute(request) tid = request.transaction_id self.assertEqual(d, protocol.transaction.getTransaction(tid)) def testUdpClientProtocolHandleResponse(self): ''' Test the udp client protocol handles responses ''' protocol = ModbusUdpClientProtocol() out = [] reply = ReadCoilsRequest(1, 1) reply.transaction_id = 0x00 # handle skipped cases protocol._handleResponse(None) protocol._handleResponse(reply) # handle existing cases d = protocol._buildResponse(0x00) d.addCallback(lambda v: out.append(v)) protocol._handleResponse(reply) self.assertEqual(out[0], reply) def testUdpClientProtocolBuildResponse(self): ''' Test the udp client protocol builds responses ''' protocol = ModbusUdpClientProtocol() self.assertEqual(0, len(list(protocol.transaction))) d = protocol._buildResponse(0x00) self.assertEqual(1, len(list(protocol.transaction))) #-----------------------------------------------------------------------# # Test Client Factories #-----------------------------------------------------------------------# def testModbusClientFactory(self): ''' Test the base class for all the clients ''' factory = ModbusClientFactory() self.assertTrue(factory is not None) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_client_sync.py0000644000175500017550000002311012607272152017355 0ustar debacledebacle#!/usr/bin/env python import unittest import socket import serial from mock import patch, Mock from twisted.test import test_protocols from pymodbus.client.sync import ModbusTcpClient, ModbusUdpClient from pymodbus.client.sync import ModbusSerialClient, BaseModbusClient from pymodbus.exceptions import ConnectionException, NotImplementedException from pymodbus.exceptions import ParameterException from pymodbus.transaction import ModbusAsciiFramer, ModbusRtuFramer from pymodbus.transaction import ModbusBinaryFramer #---------------------------------------------------------------------------# # Mock Classes #---------------------------------------------------------------------------# class mockSocket(object): def close(self): return True def recv(self, size): return '\x00'*size def read(self, size): return '\x00'*size def send(self, msg): return len(msg) def write(self, msg): return len(msg) def recvfrom(self, size): return ['\x00'*size] def sendto(self, msg, *args): return len(msg) #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class SynchronousClientTest(unittest.TestCase): ''' This is the unittest for the pymodbus.client.sync module ''' #-----------------------------------------------------------------------# # Test Base Client #-----------------------------------------------------------------------# def testBaseModbusClient(self): ''' Test the base class for all the clients ''' client = BaseModbusClient(None) client.transaction = None self.assertRaises(NotImplementedException, lambda: client.connect()) self.assertRaises(NotImplementedException, lambda: client._send(None)) self.assertRaises(NotImplementedException, lambda: client._recv(None)) self.assertRaises(NotImplementedException, lambda: client.__enter__()) self.assertRaises(NotImplementedException, lambda: client.execute()) self.assertEquals("Null Transport", str(client)) client.close() client.__exit__(0,0,0) # a successful execute client.connect = lambda: True client.transaction = Mock(**{'execute.return_value': True}) self.assertEqual(client, client.__enter__()) self.assertTrue(client.execute()) # a unsuccessful connect client.connect = lambda: False self.assertRaises(ConnectionException, lambda: client.__enter__()) self.assertRaises(ConnectionException, lambda: client.execute()) #-----------------------------------------------------------------------# # Test UDP Client #-----------------------------------------------------------------------# def testSyncUdpClientInstantiation(self): client = ModbusUdpClient() self.assertNotEqual(client, None) def testBasicSyncUdpClient(self): ''' Test the basic methods for the udp sync client''' # receive/send client = ModbusUdpClient() client.socket = mockSocket() self.assertEqual(0, client._send(None)) self.assertEqual(1, client._send('\x00')) self.assertEqual('\x00', client._recv(1)) # connect/disconnect self.assertTrue(client.connect()) client.close() # already closed socket client.socket = False client.close() self.assertEqual("127.0.0.1:502", str(client)) def testUdpClientAddressFamily(self): ''' Test the Udp client get address family method''' client = ModbusUdpClient() self.assertEqual(socket.AF_INET, client._get_address_family('127.0.0.1')) self.assertEqual(socket.AF_INET6, client._get_address_family('::1')) def testUdpClientConnect(self): ''' Test the Udp client connection method''' with patch.object(socket, 'socket') as mock_method: class DummySocket(object): def settimeout(self, *a, **kwa): pass mock_method.return_value = DummySocket() client = ModbusUdpClient() self.assertTrue(client.connect()) with patch.object(socket, 'socket') as mock_method: mock_method.side_effect = socket.error() client = ModbusUdpClient() self.assertFalse(client.connect()) def testUdpClientSend(self): ''' Test the udp client send method''' client = ModbusUdpClient() self.assertRaises(ConnectionException, lambda: client._send(None)) client.socket = mockSocket() self.assertEqual(0, client._send(None)) self.assertEqual(4, client._send('1234')) def testUdpClientRecv(self): ''' Test the udp client receive method''' client = ModbusUdpClient() self.assertRaises(ConnectionException, lambda: client._recv(1024)) client.socket = mockSocket() self.assertEqual('', client._recv(0)) self.assertEqual('\x00'*4, client._recv(4)) #-----------------------------------------------------------------------# # Test TCP Client #-----------------------------------------------------------------------# def testSyncTcpClientInstantiation(self): client = ModbusTcpClient() self.assertNotEqual(client, None) def testBasicSyncTcpClient(self): ''' Test the basic methods for the tcp sync client''' # receive/send client = ModbusTcpClient() client.socket = mockSocket() self.assertEqual(0, client._send(None)) self.assertEqual(1, client._send('\x00')) self.assertEqual('\x00', client._recv(1)) # connect/disconnect self.assertTrue(client.connect()) client.close() # already closed socket client.socket = False client.close() self.assertEqual("127.0.0.1:502", str(client)) def testTcpClientConnect(self): ''' Test the tcp client connection method''' with patch.object(socket, 'create_connection') as mock_method: mock_method.return_value = object() client = ModbusTcpClient() self.assertTrue(client.connect()) with patch.object(socket, 'create_connection') as mock_method: mock_method.side_effect = socket.error() client = ModbusTcpClient() self.assertFalse(client.connect()) def testTcpClientSend(self): ''' Test the tcp client send method''' client = ModbusTcpClient() self.assertRaises(ConnectionException, lambda: client._send(None)) client.socket = mockSocket() self.assertEqual(0, client._send(None)) self.assertEqual(4, client._send('1234')) def testTcpClientRecv(self): ''' Test the tcp client receive method''' client = ModbusTcpClient() self.assertRaises(ConnectionException, lambda: client._recv(1024)) client.socket = mockSocket() self.assertEqual('', client._recv(0)) self.assertEqual('\x00'*4, client._recv(4)) #-----------------------------------------------------------------------# # Test Serial Client #-----------------------------------------------------------------------# def testSyncSerialClientInstantiation(self): client = ModbusSerialClient() self.assertNotEqual(client, None) self.assertTrue(isinstance(ModbusSerialClient(method='ascii').framer, ModbusAsciiFramer)) self.assertTrue(isinstance(ModbusSerialClient(method='rtu').framer, ModbusRtuFramer)) self.assertTrue(isinstance(ModbusSerialClient(method='binary').framer, ModbusBinaryFramer)) self.assertRaises(ParameterException, lambda: ModbusSerialClient(method='something')) def testBasicSyncSerialClient(self): ''' Test the basic methods for the serial sync client''' # receive/send client = ModbusSerialClient() client.socket = mockSocket() self.assertEqual(0, client._send(None)) self.assertEqual(1, client._send('\x00')) self.assertEqual('\x00', client._recv(1)) # connect/disconnect self.assertTrue(client.connect()) client.close() # already closed socket client.socket = False client.close() self.assertEqual('ascii baud[19200]', str(client)) def testSerialClientConnect(self): ''' Test the serial client connection method''' with patch.object(serial, 'Serial') as mock_method: mock_method.return_value = object() client = ModbusSerialClient() self.assertTrue(client.connect()) with patch.object(serial, 'Serial') as mock_method: mock_method.side_effect = serial.SerialException() client = ModbusSerialClient() self.assertFalse(client.connect()) def testSerialClientSend(self): ''' Test the serial client send method''' client = ModbusSerialClient() self.assertRaises(ConnectionException, lambda: client._send(None)) client.socket = mockSocket() self.assertEqual(0, client._send(None)) self.assertEqual(4, client._send('1234')) def testSerialClientRecv(self): ''' Test the serial client receive method''' client = ModbusSerialClient() self.assertRaises(ConnectionException, lambda: client._recv(1024)) client.socket = mockSocket() self.assertEqual('', client._recv(0)) self.assertEqual('\x00'*4, client._recv(4)) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/modbus_mocks.py0000644000175500017550000000146712607272152016504 0ustar debacledebaclefrom pymodbus.interfaces import IModbusSlaveContext #---------------------------------------------------------------------------# # Mocks #---------------------------------------------------------------------------# class mock(object): pass class MockContext(IModbusSlaveContext): def __init__(self, valid=False, default=True): self.valid = valid self.default = default def validate(self, fx, address, count): return self.valid def getValues(self, fx, address, count): return [self.default] * count def setValues(self, fx, address, count): pass class FakeList(object): ''' todo, replace with magic mock ''' def __init__(self, size): self.size = size def __len__(self): return self.size def __iter__(self): return [] pymodbus/test/test_server_context.py0000644000175500017550000000674312607272152020132 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.datastore import * from pymodbus.exceptions import * class ModbusServerSingleContextTest(unittest.TestCase): ''' This is the unittest for the pymodbus.datastore.ModbusServerContext using a single slave context. ''' def setUp(self): ''' Sets up the test environment ''' self.slave = ModbusSlaveContext() self.context = ModbusServerContext(slaves=self.slave, single=True) def tearDown(self): ''' Cleans up the test environment ''' del self.context def testSingleContextGets(self): ''' Test getting on a single context ''' for id in xrange(0, 0xff): self.assertEqual(self.slave, self.context[id]) def testSingleContextDeletes(self): ''' Test removing on multiple context ''' def _test(): del self.context[0x00] self.assertRaises(NoSuchSlaveException, _test) def testSingleContextIter(self): ''' Test iterating over a single context ''' expected = (0, self.slave) for slave in self.context: self.assertEqual(slave, expected) def testSingleContextDefault(self): ''' Test that the single context default values work ''' self.context = ModbusServerContext() slave = self.context[0x00] self.assertEqual(slave, {}) def testSingleContextSet(self): ''' Test a setting a single slave context ''' slave = ModbusSlaveContext() self.context[0x00] = slave actual = self.context[0x00] self.assertEqual(slave, actual) class ModbusServerMultipleContextTest(unittest.TestCase): ''' This is the unittest for the pymodbus.datastore.ModbusServerContext using multiple slave contexts. ''' def setUp(self): ''' Sets up the test environment ''' self.slaves = dict((id, ModbusSlaveContext()) for id in xrange(10)) self.context = ModbusServerContext(slaves=self.slaves, single=False) def tearDown(self): ''' Cleans up the test environment ''' del self.context def testMultipleContextGets(self): ''' Test getting on multiple context ''' for id in xrange(0, 10): self.assertEqual(self.slaves[id], self.context[id]) def testMultipleContextDeletes(self): ''' Test removing on multiple context ''' del self.context[0x00] self.assertRaises(NoSuchSlaveException, lambda: self.context[0x00]) def testMultipleContextIter(self): ''' Test iterating over multiple context ''' for id, slave in self.context: self.assertEqual(slave, self.slaves[id]) self.assertTrue(id in self.context) def testMultipleContextDefault(self): ''' Test that the multiple context default values work ''' self.context = ModbusServerContext(single=False) self.assertRaises(NoSuchSlaveException, lambda: self.context[0x00]) def testMultipleContextSet(self): ''' Test a setting multiple slave contexts ''' slaves = dict((id, ModbusSlaveContext()) for id in xrange(10)) for id, slave in slaves.iteritems(): self.context[id] = slave for id, slave in slaves.iteritems(): actual = self.context[id] self.assertEqual(slave, actual) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_diag_messages.py0000644000175500017550000001711712607272152017650 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.exceptions import * from pymodbus.constants import ModbusPlusOperation from pymodbus.diag_message import * from pymodbus.diag_message import DiagnosticStatusRequest from pymodbus.diag_message import DiagnosticStatusResponse from pymodbus.diag_message import DiagnosticStatusSimpleRequest from pymodbus.diag_message import DiagnosticStatusSimpleResponse class SimpleDataStoreTest(unittest.TestCase): ''' This is the unittest for the pymodbus.diag_message module ''' def setUp(self): self.requests = [ #(DiagnosticStatusRequest, '\x00\x00\x00\x00'), #(DiagnosticStatusSimpleRequest, '\x00\x00\x00\x00'), (RestartCommunicationsOptionRequest, '\x00\x01\x00\x00', '\x00\x01\xff\x00'), (ReturnDiagnosticRegisterRequest, '\x00\x02\x00\x00', '\x00\x02\x00\x00'), (ChangeAsciiInputDelimiterRequest, '\x00\x03\x00\x00', '\x00\x03\x00\x00'), (ForceListenOnlyModeRequest, '\x00\x04\x00\x00', '\x00\x04'), (ReturnQueryDataRequest, '\x00\x00\x00\x00', '\x00\x00\x00\x00'), (ClearCountersRequest, '\x00\x0a\x00\x00', '\x00\x0a\x00\x00'), (ReturnBusMessageCountRequest, '\x00\x0b\x00\x00', '\x00\x0b\x00\x00'), (ReturnBusCommunicationErrorCountRequest, '\x00\x0c\x00\x00', '\x00\x0c\x00\x00'), (ReturnBusExceptionErrorCountRequest, '\x00\x0d\x00\x00', '\x00\x0d\x00\x00'), (ReturnSlaveMessageCountRequest, '\x00\x0e\x00\x00', '\x00\x0e\x00\x00'), (ReturnSlaveNoResponseCountRequest, '\x00\x0f\x00\x00', '\x00\x0f\x00\x00'), (ReturnSlaveNAKCountRequest, '\x00\x10\x00\x00', '\x00\x10\x00\x00'), (ReturnSlaveBusyCountRequest, '\x00\x11\x00\x00', '\x00\x11\x00\x00'), (ReturnSlaveBusCharacterOverrunCountRequest, '\x00\x12\x00\x00', '\x00\x12\x00\x00'), (ReturnIopOverrunCountRequest, '\x00\x13\x00\x00', '\x00\x13\x00\x00'), (ClearOverrunCountRequest, '\x00\x14\x00\x00', '\x00\x14\x00\x00'), (GetClearModbusPlusRequest, '\x00\x15\x00\x00', '\x00\x15' + '\x00\x00' * 55), ] self.responses = [ #(DiagnosticStatusResponse, '\x00\x00\x00\x00'), #(DiagnosticStatusSimpleResponse, '\x00\x00\x00\x00'), (ReturnQueryDataResponse, '\x00\x00\x00\x00'), (RestartCommunicationsOptionResponse, '\x00\x01\x00\x00'), (ReturnDiagnosticRegisterResponse, '\x00\x02\x00\x00'), (ChangeAsciiInputDelimiterResponse, '\x00\x03\x00\x00'), (ForceListenOnlyModeResponse, '\x00\x04'), (ReturnQueryDataResponse, '\x00\x00\x00\x00'), (ClearCountersResponse, '\x00\x0a\x00\x00'), (ReturnBusMessageCountResponse, '\x00\x0b\x00\x00'), (ReturnBusCommunicationErrorCountResponse, '\x00\x0c\x00\x00'), (ReturnBusExceptionErrorCountResponse, '\x00\x0d\x00\x00'), (ReturnSlaveMessageCountResponse, '\x00\x0e\x00\x00'), (ReturnSlaveNoReponseCountResponse, '\x00\x0f\x00\x00'), (ReturnSlaveNAKCountResponse, '\x00\x10\x00\x00'), (ReturnSlaveBusyCountResponse, '\x00\x11\x00\x00'), (ReturnSlaveBusCharacterOverrunCountResponse, '\x00\x12\x00\x00'), (ReturnIopOverrunCountResponse, '\x00\x13\x00\x00'), (ClearOverrunCountResponse, '\x00\x14\x00\x00'), (GetClearModbusPlusResponse, '\x00\x15' + '\x00\x00' * 55), ] def tearDown(self): ''' Cleans up the test environment ''' del self.requests del self.responses def testDiagnosticRequestsDecode(self): ''' Testing diagnostic request messages encoding ''' for msg,enc,exe in self.requests: handle = DiagnosticStatusRequest() handle.decode(enc) self.assertEqual(handle.sub_function_code, msg.sub_function_code) def testDiagnosticSimpleRequests(self): ''' Testing diagnostic request messages encoding ''' request = DiagnosticStatusSimpleRequest('\x12\x34') request.sub_function_code = 0x1234 self.assertRaises(NotImplementedException, lambda: request.execute()) self.assertEqual(request.encode(), '\x12\x34\x12\x34') response = DiagnosticStatusSimpleResponse(None) def testDiagnosticResponseDecode(self): ''' Testing diagnostic request messages encoding ''' for msg,enc,exe in self.requests: handle = DiagnosticStatusResponse() handle.decode(enc) self.assertEqual(handle.sub_function_code, msg.sub_function_code) def testDiagnosticRequestsEncode(self): ''' Testing diagnostic request messages encoding ''' for msg,enc,exe in self.requests: self.assertEqual(msg().encode(), enc) #def testDiagnosticResponse(self): # ''' Testing diagnostic request messages ''' # for msg,enc in self.responses: # self.assertEqual(msg().encode(), enc) def testDiagnosticExecute(self): ''' Testing diagnostic message execution ''' for msg,enc,exe in self.requests: self.assertEqual(msg().execute().encode(), exe) def testReturnQueryDataRequest(self): ''' Testing diagnostic message execution ''' message = ReturnQueryDataRequest([0x0000]*2) self.assertEqual(message.encode(), '\x00\x00\x00\x00\x00\x00'); message = ReturnQueryDataRequest(0x0000) self.assertEqual(message.encode(), '\x00\x00\x00\x00'); def testReturnQueryDataResponse(self): ''' Testing diagnostic message execution ''' message = ReturnQueryDataResponse([0x0000]*2) self.assertEqual(message.encode(), '\x00\x00\x00\x00\x00\x00'); message = ReturnQueryDataResponse(0x0000) self.assertEqual(message.encode(), '\x00\x00\x00\x00'); def testRestartCommunicationsOption(self): ''' Testing diagnostic message execution ''' request = RestartCommunicationsOptionRequest(True); self.assertEqual(request.encode(), '\x00\x01\xff\x00') request = RestartCommunicationsOptionRequest(False); self.assertEqual(request.encode(), '\x00\x01\x00\x00') response = RestartCommunicationsOptionResponse(True); self.assertEqual(response.encode(), '\x00\x01\xff\x00') response = RestartCommunicationsOptionResponse(False); self.assertEqual(response.encode(), '\x00\x01\x00\x00') def testGetClearModbusPlusRequestExecute(self): ''' Testing diagnostic message execution ''' request = GetClearModbusPlusRequest(ModbusPlusOperation.ClearStatistics); response = request.execute() self.assertEqual(response.message, None) request = GetClearModbusPlusRequest(ModbusPlusOperation.GetStatistics); response = request.execute() self.assertEqual(response.message, [0x00] * 55) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_factory.py0000644000175500017550000002113212607272152016514 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.factory import ServerDecoder, ClientDecoder from pymodbus.exceptions import ModbusException def _raise_exception(_): raise ModbusException('something') class SimpleFactoryTest(unittest.TestCase): ''' This is the unittest for the pymod.exceptions module ''' def setUp(self): ''' Initializes the test environment ''' self.client = ClientDecoder() self.server = ServerDecoder() self.request = ( (0x01, '\x01\x00\x01\x00\x01'), # read coils (0x02, '\x02\x00\x01\x00\x01'), # read discrete inputs (0x03, '\x03\x00\x01\x00\x01'), # read holding registers (0x04, '\x04\x00\x01\x00\x01'), # read input registers (0x05, '\x05\x00\x01\x00\x01'), # write single coil (0x06, '\x06\x00\x01\x00\x01'), # write single register (0x07, '\x07'), # read exception status (0x08, '\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, '\x0b'), # get comm event counters (0x0c, '\x0c'), # get comm event log (0x0f, '\x0f\x00\x01\x00\x08\x01\x00\xff'), # write multiple coils (0x10, '\x10\x00\x01\x00\x02\x04\0xff\xff'), # write multiple registers (0x11, '\x11'), # report slave id (0x14, '\x14\x0e\x06\x00\x04\x00\x01\x00\x02' \ '\x06\x00\x03\x00\x09\x00\x02'), # read file record (0x15, '\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ '\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, '\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, '\x17\x00\x01\x00\x01\x00\x01\x00\x01\x02\x12\x34'),# read/write multiple registers (0x18, '\x18\x00\x01'), # read fifo queue (0x2b, '\x2b\x0e\x01\x00'), # read device identification ) self.response = ( (0x01, '\x01\x01\x01'), # read coils (0x02, '\x02\x01\x01'), # read discrete inputs (0x03, '\x03\x02\x01\x01'), # read holding registers (0x04, '\x04\x02\x01\x01'), # read input registers (0x05, '\x05\x00\x01\x00\x01'), # write single coil (0x06, '\x06\x00\x01\x00\x01'), # write single register (0x07, '\x07\x00'), # read exception status (0x08, '\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, '\x0b\x00\x00\x00\x00'), # get comm event counters (0x0c, '\x0c\x08\x00\x00\x01\x08\x01\x21\x20\x00'), # get comm event log (0x0f, '\x0f\x00\x01\x00\x08'), # write multiple coils (0x10, '\x10\x00\x01\x00\x02'), # write multiple registers (0x11, '\x11\x03\x05\x01\x54'), # report slave id (device specific) (0x14, '\x14\x0c\x05\x06\x0d\xfe\x00\x20\x05' \ '\x06\x33\xcd\x00\x40'), # read file record (0x15, '\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ '\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, '\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, '\x17\x02\x12\x34'), # read/write multiple registers (0x18, '\x18\x00\x01\x00\x01\x00\x00'), # read fifo queue (0x2b, '\x2b\x0e\x01\x01\x00\x00\x01\x00\x01\x77'), # read device identification ) self.exception = ( (0x81, '\x81\x01\xd0\x50'), # illegal function exception (0x82, '\x82\x02\x90\xa1'), # illegal data address exception (0x83, '\x83\x03\x50\xf1'), # illegal data value exception (0x84, '\x84\x04\x13\x03'), # skave device failure exception (0x85, '\x85\x05\xd3\x53'), # acknowledge exception (0x86, '\x86\x06\x93\xa2'), # slave device busy exception (0x87, '\x87\x08\x53\xf2'), # memory parity exception (0x88, '\x88\x0a\x16\x06'), # gateway path unavailable exception (0x89, '\x89\x0b\xd6\x56'), # gateway target failed exception ) self.bad = ( (0x80, '\x80\x00\x00\x00'), # Unknown Function (0x81, '\x81\x00\x00\x00'), # error message ) def tearDown(self): ''' Cleans up the test environment ''' del self.bad del self.request del self.response def testExceptionLookup(self): ''' Test that we can look up exception messages ''' for func, _ in self.exception: response = self.client.lookupPduClass(func) self.assertNotEqual(response, None) for func, _ in self.exception: response = self.server.lookupPduClass(func) self.assertNotEqual(response, None) def testResponseLookup(self): ''' Test a working response factory lookup ''' for func, _ in self.response: response = self.client.lookupPduClass(func) self.assertNotEqual(response, None) def testRequestLookup(self): ''' Test a working request factory lookup ''' for func, _ in self.request: request = self.client.lookupPduClass(func) self.assertNotEqual(request, None) def testResponseWorking(self): ''' Test a working response factory decoders ''' for func, msg in self.response: try: self.client.decode(msg) except ModbusException: self.fail("Failed to Decode Response Message", func) def testResponseErrors(self): ''' Test a response factory decoder exceptions ''' self.assertRaises(ModbusException, self.client._helper, self.bad[0][1]) self.assertEqual(self.client.decode(self.bad[1][1]).function_code, self.bad[1][0], "Failed to decode error PDU") def testRequestsWorking(self): ''' Test a working request factory decoders ''' for func, msg in self.request: try: self.server.decode(msg) except ModbusException: self.fail("Failed to Decode Request Message", func) def testClientFactoryFails(self): ''' Tests that a client factory will fail to decode a bad message ''' self.client._helper = _raise_exception actual = self.client.decode(None) self.assertEquals(actual, None) def testServerFactoryFails(self): ''' Tests that a server factory will fail to decode a bad message ''' self.server._helper = _raise_exception actual = self.server.decode(None) self.assertEquals(actual, None) #---------------------------------------------------------------------------# # I don't actually know what is supposed to be returned here, I assume that # since the high bit is set, it will simply echo the resulting message #---------------------------------------------------------------------------# def testRequestErrors(self): ''' Test a request factory decoder exceptions ''' for func, msg in self.bad: result = self.server.decode(msg) self.assertEqual(result.ErrorCode, 1, "Failed to decode invalid requests") self.assertEqual(result.execute(None).function_code, func, "Failed to create correct response message") #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_interfaces.py0000644000175500017550000000525112607272152017174 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.interfaces import * from pymodbus.exceptions import NotImplementedException class _SingleInstance(Singleton): pass class ModbusInterfaceTestsTest(unittest.TestCase): ''' This is the unittest for the pymodbus.interfaces module ''' def setUp(self): ''' Initializes the test environment ''' pass def tearDown(self): ''' Cleans up the test environment ''' pass def testSingletonInterface(self): ''' Test that the singleton interface works ''' first = _SingleInstance() second = _SingleInstance() self.assertEquals(first, second) def testModbusDecoderInterface(self): ''' Test that the base class isn't implemented ''' x = None instance = IModbusDecoder() self.assertRaises(NotImplementedException, lambda: instance.decode(x)) self.assertRaises(NotImplementedException, lambda: instance.lookupPduClass(x)) def testModbusFramerInterface(self): ''' Test that the base class isn't implemented ''' x = None instance = IModbusFramer() self.assertRaises(NotImplementedException, instance.checkFrame) self.assertRaises(NotImplementedException, instance.advanceFrame) self.assertRaises(NotImplementedException, instance.isFrameReady) self.assertRaises(NotImplementedException, instance.getFrame) self.assertRaises(NotImplementedException, lambda: instance.addToFrame(x)) self.assertRaises(NotImplementedException, lambda: instance.populateResult(x)) self.assertRaises(NotImplementedException, lambda: instance.processIncomingPacket(x,x)) self.assertRaises(NotImplementedException, lambda: instance.buildPacket(x)) def testModbusSlaveContextInterface(self): ''' Test that the base class isn't implemented ''' x = None instance = IModbusSlaveContext() self.assertRaises(NotImplementedException, instance.reset) self.assertRaises(NotImplementedException, lambda: instance.validate(x,x,x)) self.assertRaises(NotImplementedException, lambda: instance.getValues(x,x,x)) self.assertRaises(NotImplementedException, lambda: instance.setValues(x,x,x)) def testModbusPayloadBuilderInterface(self): ''' Test that the base class isn't implemented ''' x = None instance = IPayloadBuilder() self.assertRaises(NotImplementedException, lambda: instance.build()) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_other_messages.py0000644000175500017550000000767212607272152020072 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.other_message import * class ModbusOtherMessageTest(unittest.TestCase): ''' This is the unittest for the pymodbus.other_message module ''' def setUp(self): self.requests = [ ReadExceptionStatusRequest, GetCommEventCounterRequest, GetCommEventLogRequest, ReportSlaveIdRequest, ] self.responses = [ lambda: ReadExceptionStatusResponse(0x12), lambda: GetCommEventCounterResponse(0x12), GetCommEventLogResponse, lambda: ReportSlaveIdResponse(0x12), ] def tearDown(self): ''' Cleans up the test environment ''' del self.requests del self.responses def testOtherMessagesToString(self): for message in self.requests: self.assertNotEqual(str(message()), None) for message in self.responses: self.assertNotEqual(str(message()), None) def testReadExceptionStatus(self): request = ReadExceptionStatusRequest() request.decode('\x12') self.assertEqual(request.encode(), '') self.assertEqual(request.execute(None).function_code, 0x07) response = ReadExceptionStatusResponse(0x12) self.assertEqual(response.encode(), '\x12') response.decode('\x12') self.assertEqual(response.status, 0x12) def testGetCommEventCounter(self): request = GetCommEventCounterRequest() request.decode('\x12') self.assertEqual(request.encode(), '') self.assertEqual(request.execute(None).function_code, 0x0b) response = GetCommEventCounterResponse(0x12) self.assertEqual(response.encode(), '\x00\x00\x00\x12') response.decode('\x00\x00\x00\x12') self.assertEqual(response.status, True) self.assertEqual(response.count, 0x12) response.status = False self.assertEqual(response.encode(), '\xFF\xFF\x00\x12') def testGetCommEventLog(self): request = GetCommEventLogRequest() request.decode('\x12') self.assertEqual(request.encode(), '') self.assertEqual(request.execute(None).function_code, 0x0c) response = GetCommEventLogResponse() self.assertEqual(response.encode(), '\x06\x00\x00\x00\x00\x00\x00') response.decode('\x06\x00\x00\x00\x12\x00\x12') self.assertEqual(response.status, True) self.assertEqual(response.message_count, 0x12) self.assertEqual(response.event_count, 0x12) self.assertEqual(response.events, []) response.status = False self.assertEqual(response.encode(), '\x06\xff\xff\x00\x12\x00\x12') def testGetCommEventLogWithEvents(self): response = GetCommEventLogResponse(events=[0x12,0x34,0x56]) self.assertEqual(response.encode(), '\x09\x00\x00\x00\x00\x00\x00\x12\x34\x56') response.decode('\x09\x00\x00\x00\x12\x00\x12\x12\x34\x56') self.assertEqual(response.status, True) self.assertEqual(response.message_count, 0x12) self.assertEqual(response.event_count, 0x12) self.assertEqual(response.events, [0x12,0x34,0x56]) def testReportSlaveId(self): request = ReportSlaveIdRequest() request.decode('\x12') self.assertEqual(request.encode(), '') self.assertEqual(request.execute(None).function_code, 0x11) response = ReportSlaveIdResponse(request.execute(None).identifier, True) self.assertEqual(response.encode(), '\x0apymodbus\xff') response.decode('\x03\x12\x00') self.assertEqual(response.status, False) self.assertEqual(response.identifier, '\x12\x00') response.status = False self.assertEqual(response.encode(), '\x04\x12\x00\x00') #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_server_async.py0000644000175500017550000001013112607272152017545 0ustar debacledebacle#!/usr/bin/env python import unittest from mock import patch, Mock from pymodbus.device import ModbusDeviceIdentification from pymodbus.server.async import ModbusTcpProtocol, ModbusUdpProtocol from pymodbus.server.async import ModbusServerFactory from pymodbus.server.async import StartTcpServer, StartUdpServer, StartSerialServer from pymodbus.exceptions import ConnectionException, NotImplementedException from pymodbus.exceptions import ParameterException from pymodbus.bit_read_message import ReadCoilsRequest, ReadCoilsResponse #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class AsynchronousServerTest(unittest.TestCase): ''' This is the unittest for the pymodbus.server.async module ''' #-----------------------------------------------------------------------# # Setup/TearDown #-----------------------------------------------------------------------# def setUp(self): ''' Initializes the test environment ''' values = dict((i, '') for i in range(10)) identity = ModbusDeviceIdentification(info=values) def tearDown(self): ''' Cleans up the test environment ''' pass #-----------------------------------------------------------------------# # Test Modbus Server Factory #-----------------------------------------------------------------------# def testModbusServerFactory(self): ''' Test the base class for all the clients ''' factory = ModbusServerFactory(store=None) self.assertEqual(factory.control.Identity.VendorName, '') identity = ModbusDeviceIdentification(info={0x00: 'VendorName'}) factory = ModbusServerFactory(store=None, identity=identity) self.assertEqual(factory.control.Identity.VendorName, 'VendorName') #-----------------------------------------------------------------------# # Test Modbus TCP Server #-----------------------------------------------------------------------# def testTCPServerDisconnect(self): protocol = ModbusTcpProtocol() protocol.connectionLost('because of an error') #-----------------------------------------------------------------------# # Test Modbus UDP Server #-----------------------------------------------------------------------# def testUdpServerInitialize(self): protocol = ModbusUdpProtocol(store=None) self.assertEqual(protocol.control.Identity.VendorName, '') identity = ModbusDeviceIdentification(info={0x00: 'VendorName'}) protocol = ModbusUdpProtocol(store=None, identity=identity) self.assertEqual(protocol.control.Identity.VendorName, 'VendorName') #-----------------------------------------------------------------------# # Test Modbus Server Startups #-----------------------------------------------------------------------# def testTcpServerStartup(self): ''' Test that the modbus tcp async server starts correctly ''' with patch('twisted.internet.reactor') as mock_reactor: StartTcpServer(context=None, console=True) self.assertEqual(mock_reactor.listenTCP.call_count, 2) self.assertEqual(mock_reactor.run.call_count, 1) def testUdpServerStartup(self): ''' Test that the modbus udp async server starts correctly ''' with patch('twisted.internet.reactor') as mock_reactor: StartUdpServer(context=None) self.assertEqual(mock_reactor.listenUDP.call_count, 1) self.assertEqual(mock_reactor.run.call_count, 1) def testSerialServerStartup(self): ''' Test that the modbus serial async server starts correctly ''' with patch('twisted.internet.reactor') as mock_reactor: StartSerialServer(context=None, port='/dev/ptmx') self.assertEqual(mock_reactor.run.call_count, 1) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_ptwisted.py0000644000175500017550000000147612607272152016721 0ustar debacledebacle#!/usr/bin/env python import unittest #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class TwistedInternalCodeTest(unittest.TestCase): ''' This is the unittest for the pymodbus.internal.ptwisted code ''' #-----------------------------------------------------------------------# # Setup/TearDown #-----------------------------------------------------------------------# def testInstallConch(self): ''' Test that we can install the conch backend ''' pass #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_client_common.py0000644000175500017550000000513212607272226017677 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.client.common import ModbusClientMixin from pymodbus.bit_read_message import * from pymodbus.bit_write_message import * from pymodbus.file_message import * from pymodbus.register_read_message import * from pymodbus.register_write_message import * #---------------------------------------------------------------------------# # Mocks #---------------------------------------------------------------------------# class MockClient(ModbusClientMixin): def execute(self, request): return request #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class ModbusCommonClientTests(unittest.TestCase): #-----------------------------------------------------------------------# # Setup/TearDown #-----------------------------------------------------------------------# def setUp(self): ''' Initializes the test environment and builds request/result encoding pairs ''' self.client = MockClient() def tearDown(self): ''' Cleans up the test environment ''' del self.client #-----------------------------------------------------------------------# # Tests #-----------------------------------------------------------------------# def testModbusClientMixinMethods(self): ''' This tests that the mixing returns the correct request object ''' arguments = { 'read_address': 1, 'read_count': 1, 'write_address': 1, 'write_registers': 1 } self.assertTrue(isinstance(self.client.read_coils(1,1), ReadCoilsRequest)) self.assertTrue(isinstance(self.client.read_discrete_inputs(1,1), ReadDiscreteInputsRequest)) self.assertTrue(isinstance(self.client.write_coil(1,True), WriteSingleCoilRequest)) self.assertTrue(isinstance(self.client.write_coils(1,[True]), WriteMultipleCoilsRequest)) self.assertTrue(isinstance(self.client.write_register(1,0x00), WriteSingleRegisterRequest)) self.assertTrue(isinstance(self.client.write_registers(1,[0x00]), WriteMultipleRegistersRequest)) self.assertTrue(isinstance(self.client.read_holding_registers(1,1), ReadHoldingRegistersRequest)) self.assertTrue(isinstance(self.client.read_input_registers(1,1), ReadInputRegistersRequest)) self.assertTrue(isinstance(self.client.readwrite_registers(**arguments), ReadWriteMultipleRegistersRequest)) self.assertTrue(isinstance(self.client.mask_write_register(1,0,0), MaskWriteRegisterRequest)) pymodbus/test/test_utilities.py0000644000175500017550000000573112607272152017067 0ustar debacledebacle#!/usr/bin/env python import unittest import struct from pymodbus.utilities import pack_bitstring, unpack_bitstring from pymodbus.utilities import checkCRC, checkLRC from pymodbus.utilities import dict_property, default _test_master = {4 : 'd'} class DictPropertyTester(object): def __init__(self): self.test = {1 : 'a'} self._test = {2 : 'b'} self.__test = {3 : 'c'} l1 = dict_property(lambda s: s.test, 1) l2 = dict_property(lambda s: s._test, 2) l3 = dict_property(lambda s: s.__test, 3) s1 = dict_property('test', 1) s2 = dict_property('_test', 2) g1 = dict_property(_test_master, 4) class SimpleUtilityTest(unittest.TestCase): ''' This is the unittest for the pymod.utilities module ''' def setUp(self): ''' Initializes the test environment ''' self.data = struct.pack('>HHHH', 0x1234, 0x2345, 0x3456, 0x4567) self.string = "test the computation" self.bits = [True, False, True, False, True, False, True, False] def tearDown(self): ''' Cleans up the test environment ''' del self.bits del self.string def testDictProperty(self): ''' Test all string <=> bit packing functions ''' d = DictPropertyTester() self.assertEqual(d.l1, 'a') self.assertEqual(d.l2, 'b') self.assertEqual(d.l3, 'c') self.assertEqual(d.s1, 'a') self.assertEqual(d.s2, 'b') self.assertEqual(d.g1, 'd') for store in 'l1 l2 l3 s1 s2 g1'.split(' '): setattr(d, store, 'x') self.assertEqual(d.l1, 'x') self.assertEqual(d.l2, 'x') self.assertEqual(d.l3, 'x') self.assertEqual(d.s1, 'x') self.assertEqual(d.s2, 'x') self.assertEqual(d.g1, 'x') def testDefaultValue(self): ''' Test all string <=> bit packing functions ''' self.assertEqual(default(1), 0) self.assertEqual(default(1.1), 0.0) self.assertEqual(default(1+1j), 0j) self.assertEqual(default('string'), '') self.assertEqual(default([1,2,3]), []) self.assertEqual(default({1:1}), {}) self.assertEqual(default(True), False) def testBitPacking(self): ''' Test all string <=> bit packing functions ''' self.assertEqual(unpack_bitstring('\x55'), self.bits) self.assertEqual(pack_bitstring(self.bits), '\x55') def testLongitudinalRedundancyCheck(self): ''' Test the longitudinal redundancy check code ''' self.assertTrue(checkLRC(self.data, 0x1c)) self.assertTrue(checkLRC(self.string, 0x0c)) def testCyclicRedundancyCheck(self): ''' Test the cyclic redundancy check code ''' self.assertTrue(checkCRC(self.data, 0xe2db)) self.assertTrue(checkCRC(self.string, 0x889e)) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_bit_read_messages.py0000644000175500017550000001045512607272152020513 0ustar debacledebacle#!/usr/bin/env python ''' Bit Message Test Fixture -------------------------------- This fixture tests the functionality of all the bit based request/response messages: * Read/Write Discretes * Read Coils ''' import unittest, struct from pymodbus.bit_read_message import * from pymodbus.bit_read_message import ReadBitsRequestBase from pymodbus.bit_read_message import ReadBitsResponseBase from pymodbus.exceptions import * from pymodbus.pdu import ModbusExceptions from modbus_mocks import MockContext #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class ModbusBitMessageTests(unittest.TestCase): #-----------------------------------------------------------------------# # Setup/TearDown #-----------------------------------------------------------------------# def setUp(self): ''' Initializes the test environment and builds request/result encoding pairs ''' pass def tearDown(self): ''' Cleans up the test environment ''' pass def testReadBitBaseClassMethods(self): ''' Test basic bit message encoding/decoding ''' handle = ReadBitsRequestBase(1, 1) msg = "ReadBitRequest(1,1)" self.assertEqual(msg, str(handle)) handle = ReadBitsResponseBase([1,1]) msg = "ReadBitResponse(2)" self.assertEqual(msg, str(handle)) def testBitReadBaseRequestEncoding(self): ''' Test basic bit message encoding/decoding ''' for i in xrange(20): handle = ReadBitsRequestBase(i, i) result = struct.pack('>HH',i, i) self.assertEqual(handle.encode(), result) handle.decode(result) self.assertEqual((handle.address, handle.count), (i,i)) def testBitReadBaseResponseEncoding(self): ''' Test basic bit message encoding/decoding ''' for i in xrange(20): input = [True] * i handle = ReadBitsResponseBase(input) result = handle.encode() handle.decode(result) self.assertEqual(handle.bits[:i], input) def testBitReadBaseResponseHelperMethods(self): ''' Test the extra methods on a ReadBitsResponseBase ''' input = [False] * 8 handle = ReadBitsResponseBase(input) for i in [1,3,5]: handle.setBit(i, True) for i in [1,3,5]: handle.resetBit(i) for i in xrange(8): self.assertEqual(handle.getBit(i), False) def testBitReadBaseRequests(self): ''' Test bit read request encoding ''' messages = { ReadBitsRequestBase(12, 14) : '\x00\x0c\x00\x0e', ReadBitsResponseBase([1,0,1,1,0]) : '\x01\x0d', } for request, expected in messages.iteritems(): self.assertEqual(request.encode(), expected) def testBitReadMessageExecuteValueErrors(self): ''' Test bit read request encoding ''' context = MockContext() requests = [ ReadCoilsRequest(1,0x800), ReadDiscreteInputsRequest(1,0x800), ] for request in requests: result = request.execute(context) self.assertEqual(ModbusExceptions.IllegalValue, result.exception_code) def testBitReadMessageExecuteAddressErrors(self): ''' Test bit read request encoding ''' context = MockContext() requests = [ ReadCoilsRequest(1,5), ReadDiscreteInputsRequest(1,5), ] for request in requests: result = request.execute(context) self.assertEqual(ModbusExceptions.IllegalAddress, result.exception_code) def testBitReadMessageExecuteSuccess(self): ''' Test bit read request encoding ''' context = MockContext() context.validate = lambda a,b,c: True requests = [ ReadCoilsRequest(1,5), ReadDiscreteInputsRequest(1,5), ] for request in requests: result = request.execute(context) self.assertEqual(result.bits, [True] * 5) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_register_write_messages.py0000644000175500017550000000721712607272152022002 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.register_write_message import * from pymodbus.exceptions import ParameterException from pymodbus.pdu import ModbusExceptions from modbus_mocks import MockContext #---------------------------------------------------------------------------# # Fixture #---------------------------------------------------------------------------# class WriteRegisterMessagesTest(unittest.TestCase): ''' Register Message Test Fixture -------------------------------- This fixture tests the functionality of all the register based request/response messages: * Read/Write Input Registers * Read Holding Registers ''' def setUp(self): ''' Initializes the test environment and builds request/result encoding pairs ''' self.value = 0xabcd self.values = [0xa, 0xb, 0xc] self.write = { WriteSingleRegisterRequest(1, self.value) : '\x00\x01\xab\xcd', WriteSingleRegisterResponse(1, self.value) : '\x00\x01\xab\xcd', WriteMultipleRegistersRequest(1, self.values) : '\x00\x01\x00\x03\x06\x00\n\x00\x0b\x00\x0c', WriteMultipleRegistersResponse(1, 5) : '\x00\x01\x00\x05', } def tearDown(self): ''' Cleans up the test environment ''' del self.write def testRegisterWriteRequestsEncode(self): for request, response in self.write.iteritems(): self.assertEqual(request.encode(), response) def testRegisterWriteRequestsDecode(self): addresses = [1,1,1,1] values = sorted(self.write.items()) for packet, address in zip(values, addresses): request, response = packet request.decode(response) self.assertEqual(request.address, address) def testInvalidWriteMultipleRegistersRequest(self): request = WriteMultipleRegistersRequest(0, None) self.assertEquals(request.values, []) def testSerializingToString(self): for request in self.write.iterkeys(): self.assertTrue(str(request) != None) def testWriteSingleRegisterRequest(self): context = MockContext() request = WriteSingleRegisterRequest(0x00, 0xf0000) result = request.execute(context) self.assertEqual(result.exception_code, ModbusExceptions.IllegalValue) request.value = 0x00ff result = request.execute(context) self.assertEqual(result.exception_code, ModbusExceptions.IllegalAddress) context.valid = True result = request.execute(context) self.assertEqual(result.function_code, request.function_code) def testWriteMultipleRegisterRequest(self): context = MockContext() request = WriteMultipleRegistersRequest(0x00, [0x00]*10) result = request.execute(context) self.assertEqual(result.exception_code, ModbusExceptions.IllegalAddress) request.count = 0x05 # bytecode != code * 2 result = request.execute(context) self.assertEqual(result.exception_code, ModbusExceptions.IllegalValue) request.count = 0x800 # outside of range result = request.execute(context) self.assertEqual(result.exception_code, ModbusExceptions.IllegalValue) context.valid = True request = WriteMultipleRegistersRequest(0x00, [0x00]*10) result = request.execute(context) self.assertEqual(result.function_code, request.function_code) #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/test/test_exceptions.py0000644000175500017550000000225112607272152017227 0ustar debacledebacle#!/usr/bin/env python import unittest from pymodbus.exceptions import * class SimpleExceptionsTest(unittest.TestCase): ''' This is the unittest for the pymodbus.exceptions module ''' def setUp(self): ''' Initializes the test environment ''' self.exceptions = [ ModbusException("bad base"), ModbusIOException("bad register"), ParameterException("bad paramater"), NotImplementedException("bad function"), ConnectionException("bad connection"), ] def tearDown(self): ''' Cleans up the test environment ''' pass def testExceptions(self): ''' Test all module exceptions ''' for ex in self.exceptions: try: raise ex except ModbusException, ex: self.assertTrue("Modbus Error:" in str(ex)) pass else: self.fail("Excepted a ModbusExceptions") #---------------------------------------------------------------------------# # Main #---------------------------------------------------------------------------# if __name__ == "__main__": unittest.main() pymodbus/.project0000644000175500017550000000055212607272152014127 0ustar debacledebacle pymodbus org.python.pydev.PyDevBuilder org.python.pydev.pythonNature pymodbus/CHANGELOG.rst0000644000175500017550000000272412607272152014504 0ustar debacledebacleVersion 1.2.0 ------------------------------------------------------------ * Added ability to ignore missing slaves * Added ability to revert to ZeroMode * Passed a number of extra options through the stack * Fixed documenation and added a number of examples Version 1.2.0 ------------------------------------------------------------ * Reworking the transaction managers to be more explicit and to handle modbus RTU over TCP. * Adding examples for a number of unique requested use cases * Allow RTU framers to fail fast instead of staying at fault * Working on datastore saving and loading Version 1.1.0 ------------------------------------------------------------ * Fixing memory leak in clients and servers (removed __del__) * Adding the ability to override the client framers * Working on web page api and GUI * Moving examples and extra code to contrib sections * Adding more documentation Version 1.0.0 ------------------------------------------------------------ * Adding support for payload builders to form complex encoding and decoding of messages. * Adding BCD and binary payload builders * Adding support for pydev * Cleaning up the build tools * Adding a message encoding generator for testing. * Now passing kwargs to base of PDU so arguments can be used correctly at all levels of the protocol. * A number of bug fixes (see bug tracker and commit messages) Version 0.9.0 ------------------------------------------------------------ Please view the git commit log pymodbus/setup.cfg0000644000175500017550000000067612607272152014310 0ustar debacledebacle[aliases] upload_docs = build_sphinx upload_docs package = build_apidocs build_sphinx sdist [egg_info] #tag_build = dev tag_svn_revision = false [nosetests] verbosity=0 detailed-errors=1 with-coverage=1 cover-html=1 cover-html-dir=build/coverage/ cover-package=pymodbus #debug=nose.loader #pdb=1 #pdb-failures=1 [build-sphinx] source-dir = doc/sphinx/ build-dir = doc/sphinx/build all_files = 1 [upload_docs] upload-dir = build/sphinx/html pymodbus/setup.py0000644000175500017550000000526112607272152014174 0ustar debacledebacle#!/usr/bin/env python ''' Installs pymodbus using distutils Run: python setup.py install to install the package from the source archive. For information about setuptools http://peak.telecommunity.com/DevCenter/setuptools#new-and-changed-setup-keywords ''' #---------------------------------------------------------------------------# # initialization #---------------------------------------------------------------------------# try: # if not installed, install and proceed from setuptools import setup, find_packages except ImportError: from ez_setup import use_setuptools use_setuptools() from setuptools import setup, find_packages try: from setup_commands import command_classes except ImportError: command_classes = {} from pymodbus import __version__, __author__ #---------------------------------------------------------------------------# # configuration #---------------------------------------------------------------------------# setup(name = 'pymodbus', version = __version__, description = 'A fully featured modbus protocol stack in python', long_description=''' Pymodbus aims to be a fully implemented modbus protocol stack implemented using twisted. Its orignal goal was to allow simulation of thousands of modbus devices on a single machine for monitoring software testing. ''', classifiers = [ 'Development Status :: 4 - Beta', 'Environment :: Console', 'Environment :: X11 Applications :: GTK', 'Framework :: Twisted', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: POSIX :: Linux', 'Operating System :: Unix', 'Programming Language :: Python', 'Topic :: System :: Networking', 'Topic :: Utilities' ], keywords = 'modbus, twisted, scada', author = __author__, author_email = 'bashwork@gmail.com', maintainer = __author__, maintainer_email = 'bashwork@gmail.com', url='http://code.google.com/p/pymodbus/', license = 'BSD', packages = find_packages(exclude=['examples', 'test']), exclude_package_data = {'' : ['examples', 'test', 'tools', 'doc']}, py_modules = ['ez_setup'], platforms = ['Linux', 'Mac OS X', 'Win'], include_package_data = True, zip_safe = True, install_requires = [ 'twisted >= 12.2.0', 'pyserial >= 2.6' ], extras_require = { 'quality' : [ 'coverage >= 3.5.3', 'nose >= 1.2.1', 'mock >= 1.0.0', 'pep8 >= 1.3.3' ], 'documents' : [ 'sphinx >= 1.1.3' ], 'twisted' : [ 'pyasn1 >= 0.1.4', 'pycrypto >= 2.6' ], }, test_suite = 'nose.collector', cmdclass = command_classes, ) pymodbus/pymodbus/0000755000175500017550000000000012607272207014321 5ustar debacledebaclepymodbus/pymodbus/mei_message.py0000644000175500017550000001322012607272152017146 0ustar debacledebacle''' Encapsulated Interface (MEI) Transport Messages ----------------------------------------------- ''' import struct from pymodbus.constants import DeviceInformation, MoreData from pymodbus.pdu import ModbusRequest from pymodbus.pdu import ModbusResponse from pymodbus.device import ModbusControlBlock from pymodbus.device import DeviceInformationFactory from pymodbus.pdu import ModbusExceptions as merror _MCB = ModbusControlBlock() #---------------------------------------------------------------------------# # Read Device Information #---------------------------------------------------------------------------# class ReadDeviceInformationRequest(ModbusRequest): ''' This function code allows reading the identification and additional information relative to the physical and functional description of a remote device, only. The Read Device Identification interface is modeled as an address space composed of a set of addressable data elements. The data elements are called objects and an object Id identifies them. ''' function_code = 0x2b sub_function_code = 0x0e _rtu_frame_size = 3 def __init__(self, read_code=None, object_id=0x00, **kwargs): ''' Initializes a new instance :param read_code: The device information read code :param object_id: The object to read from ''' ModbusRequest.__init__(self, **kwargs) self.read_code = read_code or DeviceInformation.Basic self.object_id = object_id def encode(self): ''' Encodes the request packet :returns: The byte encoded packet ''' packet = struct.pack('>BBB', self.sub_function_code, self.read_code, self.object_id) return packet def decode(self, data): ''' Decodes data part of the message. :param data: The incoming data ''' params = struct.unpack('>BBB', data) self.sub_function_code, self.read_code, self.object_id = params def execute(self, context): ''' Run a read exeception status request against the store :param context: The datastore to request from :returns: The populated response ''' if not (0x00 <= self.object_id <= 0xff): return self.doException(merror.IllegalValue) if not (0x00 <= self.read_code <= 0x04): return self.doException(merror.IllegalValue) information = DeviceInformationFactory.get(_MCB, self.read_code, self.object_id) return ReadDeviceInformationResponse(self.read_code, information) def __str__(self): ''' Builds a representation of the request :returns: The string representation of the request ''' params = (self.read_code, self.object_id) return "ReadDeviceInformationRequest(%d,%d)" % params class ReadDeviceInformationResponse(ModbusResponse): ''' ''' function_code = 0x2b sub_function_code = 0x0e @classmethod def calculateRtuFrameSize(cls, buffer): ''' Calculates the size of the message :param buffer: A buffer containing the data that have been received. :returns: The number of bytes in the response. ''' size = 8 # skip the header information count = struct.unpack('>B', buffer[7])[0] while count > 0: _, object_length = struct.unpack('>BB', buffer[size:size+2]) size += object_length + 2 count -= 1 return size + 2 def __init__(self, read_code=None, information=None, **kwargs): ''' Initializes a new instance :param read_code: The device information read code :param information: The requested information request ''' ModbusResponse.__init__(self, **kwargs) self.read_code = read_code or DeviceInformation.Basic self.information = information or {} self.number_of_objects = len(self.information) self.conformity = 0x83 # I support everything right now # TODO calculate self.next_object_id = 0x00 # self.information[-1](0) self.more_follows = MoreData.Nothing def encode(self): ''' Encodes the response :returns: The byte encoded message ''' packet = struct.pack('>BBBBBB', self.sub_function_code, self.read_code, self.conformity, self.more_follows, self.next_object_id, self.number_of_objects) for (object_id, data) in self.information.iteritems(): packet += struct.pack('>BB', object_id, len(data)) packet += data return packet def decode(self, data): ''' Decodes a the response :param data: The packet data to decode ''' params = struct.unpack('>BBBBBB', data[0:6]) self.sub_function_code, self.read_code = params[0:2] self.conformity, self.more_follows = params[2:4] self.next_object_id, self.number_of_objects = params[4:6] self.information, count = {}, 6 # skip the header information while count < len(data): object_id, object_length = struct.unpack('>BB', data[count:count+2]) count += object_length + 2 self.information[object_id] = data[count-object_length:count] def __str__(self): ''' Builds a representation of the response :returns: The string representation of the response ''' return "ReadDeviceInformationResponse(%d)" % self.read_code #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "ReadDeviceInformationRequest", "ReadDeviceInformationResponse", ] pymodbus/pymodbus/pdu.py0000644000175500017550000001722212607272152015466 0ustar debacledebacle''' Contains base classes for modbus request/response/error packets ''' from pymodbus.interfaces import Singleton from pymodbus.exceptions import NotImplementedException from pymodbus.constants import Defaults from pymodbus.utilities import rtuFrameSize #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Base PDU's #---------------------------------------------------------------------------# class ModbusPDU(object): ''' Base class for all Modbus mesages .. attribute:: transaction_id This value is used to uniquely identify a request response pair. It can be implemented as a simple counter .. attribute:: protocol_id This is a constant set at 0 to indicate Modbus. It is put here for ease of expansion. .. attribute:: unit_id This is used to route the request to the correct child. In the TCP modbus, it is used for routing (or not used at all. However, for the serial versions, it is used to specify which child to perform the requests against. The value 0x00 represents the broadcast address (also 0xff). .. attribute:: check This is used for LRC/CRC in the serial modbus protocols .. attribute:: skip_encode This is used when the message payload has already been encoded. Generally this will occur when the PayloadBuilder is being used to create a complicated message. By setting this to True, the request will pass the currently encoded message through instead of encoding it again. ''' def __init__(self, **kwargs): ''' Initializes the base data for a modbus request ''' self.transaction_id = kwargs.get('transaction', Defaults.TransactionId) self.protocol_id = kwargs.get('protocol', Defaults.ProtocolId) self.unit_id = kwargs.get('unit', Defaults.UnitId) self.skip_encode = kwargs.get('skip_encode', False) self.check = 0x0000 def encode(self): ''' Encodes the message :raises: A not implemented exception ''' raise NotImplementedException() def decode(self, data): ''' Decodes data part of the message. :param data: is a string object :raises: A not implemented exception ''' raise NotImplementedException() @classmethod def calculateRtuFrameSize(cls, buffer): ''' Calculates the size of a PDU. :param buffer: A buffer containing the data that have been received. :returns: The number of bytes in the PDU. ''' if hasattr(cls, '_rtu_frame_size'): return cls._rtu_frame_size elif hasattr(cls, '_rtu_byte_count_pos'): return rtuFrameSize(buffer, cls._rtu_byte_count_pos) else: raise NotImplementedException( "Cannot determine RTU frame size for %s" % cls.__name__) class ModbusRequest(ModbusPDU): ''' Base class for a modbus request PDU ''' def __init__(self, **kwargs): ''' Proxy to the lower level initializer ''' ModbusPDU.__init__(self, **kwargs) def doException(self, exception): ''' Builds an error response based on the function :param exception: The exception to return :raises: An exception response ''' _logger.error("Exception Response F(%d) E(%d)" % (self.function_code, exception)) return ExceptionResponse(self.function_code, exception) class ModbusResponse(ModbusPDU): ''' Base class for a modbus response PDU .. attribute:: should_respond A flag that indicates if this response returns a result back to the client issuing the request .. attribute:: _rtu_frame_size Indicates the size of the modbus rtu response used for calculating how much to read. ''' should_respond = True def __init__(self, **kwargs): ''' Proxy to the lower level initializer ''' ModbusPDU.__init__(self, **kwargs) #---------------------------------------------------------------------------# # Exception PDU's #---------------------------------------------------------------------------# class ModbusExceptions(Singleton): ''' An enumeration of the valid modbus exceptions ''' IllegalFunction = 0x01 IllegalAddress = 0x02 IllegalValue = 0x03 SlaveFailure = 0x04 Acknowledge = 0x05 SlaveBusy = 0x06 MemoryParityError = 0x08 GatewayPathUnavailable = 0x0A GatewayNoResponse = 0x0B @classmethod def decode(cls, code): ''' Given an error code, translate it to a string error name. :param code: The code number to translate ''' values = dict((v, k) for k, v in cls.__dict__.iteritems() if not k.startswith('__') and not callable(v)) return values.get(code, None) class ExceptionResponse(ModbusResponse): ''' Base class for a modbus exception PDU ''' ExceptionOffset = 0x80 _rtu_frame_size = 5 def __init__(self, function_code, exception_code=None, **kwargs): ''' Initializes the modbus exception response :param function_code: The function to build an exception response for :param exception_code: The specific modbus exception to return ''' ModbusResponse.__init__(self, **kwargs) self.original_code = function_code self.function_code = function_code | self.ExceptionOffset self.exception_code = exception_code def encode(self): ''' Encodes a modbus exception response :returns: The encoded exception packet ''' return chr(self.exception_code) def decode(self, data): ''' Decodes a modbus exception response :param data: The packet data to decode ''' self.exception_code = ord(data[0]) def __str__(self): ''' Builds a representation of an exception response :returns: The string representation of an exception response ''' message = ModbusExceptions.decode(self.exception_code) parameters = (self.function_code, self.original_code, message) return "Exception Response(%d, %d, %s)" % parameters class IllegalFunctionRequest(ModbusRequest): ''' Defines the Modbus slave exception type 'Illegal Function' This exception code is returned if the slave:: - does not implement the function code **or** - is not in a state that allows it to process the function ''' ErrorCode = 1 def __init__(self, function_code, **kwargs): ''' Initializes a IllegalFunctionRequest :param function_code: The function we are erroring on ''' ModbusRequest.__init__(self, **kwargs) self.function_code = function_code def decode(self, data): ''' This is here so this failure will run correctly :param data: Not used ''' pass def execute(self, context): ''' Builds an illegal function request error response :param context: The current context for the message :returns: The error response packet ''' return ExceptionResponse(self.function_code, self.ErrorCode) #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ 'ModbusRequest', 'ModbusResponse', 'ModbusExceptions', 'ExceptionResponse', 'IllegalFunctionRequest', ] pymodbus/pymodbus/file_message.py0000644000175500017550000004225612607272152017326 0ustar debacledebacle''' File Record Read/Write Messages ------------------------------- Currently none of these messages are implemented ''' import struct from pymodbus.pdu import ModbusRequest from pymodbus.pdu import ModbusResponse from pymodbus.pdu import ModbusExceptions as merror #---------------------------------------------------------------------------# # File Record Types #---------------------------------------------------------------------------# class FileRecord(object): ''' Represents a file record and its relevant data. ''' def __init__(self, **kwargs): ''' Initializes a new instance :params reference_type: Defaults to 0x06 (must be) :params file_number: Indicates which file number we are reading :params record_number: Indicates which record in the file :params record_data: The actual data of the record :params record_length: The length in registers of the record :params response_length: The length in bytes of the record ''' self.reference_type = kwargs.get('reference_type', 0x06) self.file_number = kwargs.get('file_number', 0x00) self.record_number = kwargs.get('record_number', 0x00) self.record_data = kwargs.get('record_data', '') self.record_length = kwargs.get('record_length', len(self.record_data) / 2) self.response_length = kwargs.get('response_length', len(self.record_data) + 1) def __eq__(self, relf): ''' Compares the left object to the right ''' return self.reference_type == relf.reference_type \ and self.file_number == relf.file_number \ and self.record_number == relf.record_number \ and self.record_length == relf.record_length \ and self.record_data == relf.record_data def __ne__(self, relf): ''' Compares the left object to the right ''' return not self.__eq__(relf) def __repr__(self): ''' Gives a representation of the file record ''' params = (self.file_number, self.record_number, self.record_length) return 'FileRecord(file=%d, record=%d, length=%d)' % params #---------------------------------------------------------------------------# # File Requests/Responses #---------------------------------------------------------------------------# class ReadFileRecordRequest(ModbusRequest): ''' This function code is used to perform a file record read. All request data lengths are provided in terms of number of bytes and all record lengths are provided in terms of registers. A file is an organization of records. Each file contains 10000 records, addressed 0000 to 9999 decimal or 0x0000 to 0x270f. For example, record 12 is addressed as 12. The function can read multiple groups of references. The groups can be separating (non-contiguous), but the references within each group must be sequential. Each group is defined in a seperate 'sub-request' field that contains seven bytes:: The reference type: 1 byte (must be 0x06) The file number: 2 bytes The starting record number within the file: 2 bytes The length of the record to be read: 2 bytes The quantity of registers to be read, combined with all other fields in the expected response, must not exceed the allowable length of the MODBUS PDU: 235 bytes. ''' function_code = 0x14 _rtu_byte_count_pos = 2 def __init__(self, records=None, **kwargs): ''' Initializes a new instance :param records: The file record requests to be read ''' ModbusRequest.__init__(self, **kwargs) self.records = records or [] def encode(self): ''' Encodes the request packet :returns: The byte encoded packet ''' packet = struct.pack('B', len(self.records) * 7) for record in self.records: packet += struct.pack('>BHHH', 0x06, record.file_number, record.record_number, record.record_length) return packet def decode(self, data): ''' Decodes the incoming request :param data: The data to decode into the address ''' self.records = [] byte_count = struct.unpack('B', data[0])[0] for count in xrange(1, byte_count, 7): decoded = struct.unpack('>BHHH', data[count:count+7]) record = FileRecord(file_number=decoded[1], record_number=decoded[2], record_length=decoded[3]) if decoded[0] == 0x06: self.records.append(record) def execute(self, context): ''' Run a read exeception status request against the store :param context: The datastore to request from :returns: The populated response ''' # TODO do some new context operation here # if file number, record number, or address + length # is too big, return an error. files = [] return ReadFileRecordResponse(files) class ReadFileRecordResponse(ModbusResponse): ''' The normal response is a series of 'sub-responses,' one for each 'sub-request.' The byte count field is the total combined count of bytes in all 'sub-responses.' In addition, each 'sub-response' contains a field that shows its own byte count. ''' function_code = 0x14 _rtu_byte_count_pos = 2 def __init__(self, records=None, **kwargs): ''' Initializes a new instance :param records: The requested file records ''' ModbusResponse.__init__(self, **kwargs) self.records = records or [] def encode(self): ''' Encodes the response :returns: The byte encoded message ''' total = sum(record.response_length + 1 for record in self.records) packet = struct.pack('B', total) for record in self.records: packet += struct.pack('>BB', 0x06, record.record_length) packet += record.record_data return packet def decode(self, data): ''' Decodes a the response :param data: The packet data to decode ''' count, self.records = 1, [] byte_count = struct.unpack('B', data[0])[0] while count < byte_count: response_length, reference_type = struct.unpack('>BB', data[count:count+2]) count += response_length + 1 # the count is not included record = FileRecord(response_length=response_length, record_data=data[count - response_length + 1:count]) if reference_type == 0x06: self.records.append(record) class WriteFileRecordRequest(ModbusRequest): ''' This function code is used to perform a file record write. All request data lengths are provided in terms of number of bytes and all record lengths are provided in terms of the number of 16 bit words. ''' function_code = 0x15 _rtu_byte_count_pos = 2 def __init__(self, records=None, **kwargs): ''' Initializes a new instance :param records: The file record requests to be read ''' ModbusRequest.__init__(self, **kwargs) self.records = records or [] def encode(self): ''' Encodes the request packet :returns: The byte encoded packet ''' total_length = sum((record.record_length * 2) + 7 for record in self.records) packet = struct.pack('B', total_length) for record in self.records: packet += struct.pack('>BHHH', 0x06, record.file_number, record.record_number, record.record_length) packet += record.record_data return packet def decode(self, data): ''' Decodes the incoming request :param data: The data to decode into the address ''' count, self.records = 1, [] byte_count = struct.unpack('B', data[0])[0] while count < byte_count: decoded = struct.unpack('>BHHH', data[count:count+7]) response_length = decoded[3] * 2 count += response_length + 7 record = FileRecord(record_length=decoded[3], file_number=decoded[1], record_number=decoded[2], record_data=data[count - response_length:count]) if decoded[0] == 0x06: self.records.append(record) def execute(self, context): ''' Run the write file record request against the context :param context: The datastore to request from :returns: The populated response ''' # TODO do some new context operation here # if file number, record number, or address + length # is too big, return an error. return WriteFileRecordResponse(self.records) class WriteFileRecordResponse(ModbusResponse): ''' The normal response is an echo of the request. ''' function_code = 0x15 _rtu_byte_count_pos = 2 def __init__(self, records=None, **kwargs): ''' Initializes a new instance :param records: The file record requests to be read ''' ModbusResponse.__init__(self, **kwargs) self.records = records or [] def encode(self): ''' Encodes the response :returns: The byte encoded message ''' total_length = sum((record.record_length * 2) + 7 for record in self.records) packet = struct.pack('B', total_length) for record in self.records: packet += struct.pack('>BHHH', 0x06, record.file_number, record.record_number, record.record_length) packet += record.record_data return packet def decode(self, data): ''' Decodes the incoming request :param data: The data to decode into the address ''' count, self.records = 1, [] byte_count = struct.unpack('B', data[0])[0] while count < byte_count: decoded = struct.unpack('>BHHH', data[count:count+7]) response_length = decoded[3] * 2 count += response_length + 7 record = FileRecord(record_length=decoded[3], file_number=decoded[1], record_number=decoded[2], record_data=data[count - response_length:count]) if decoded[0] == 0x06: self.records.append(record) class MaskWriteRegisterRequest(ModbusRequest): ''' This function code is used to modify the contents of a specified holding register using a combination of an AND mask, an OR mask, and the register's current contents. The function can be used to set or clear individual bits in the register. ''' function_code = 0x16 _rtu_frame_size = 10 def __init__(self, address=0x0000, and_mask=0xffff, or_mask=0x0000, **kwargs): ''' Initializes a new instance :param address: The mask pointer address (0x0000 to 0xffff) :param and_mask: The and bitmask to apply to the register address :param or_mask: The or bitmask to apply to the register address ''' ModbusRequest.__init__(self, **kwargs) self.address = address self.and_mask = and_mask self.or_mask = or_mask def encode(self): ''' Encodes the request packet :returns: The byte encoded packet ''' return struct.pack('>HHH', self.address, self.and_mask, self.or_mask) def decode(self, data): ''' Decodes the incoming request :param data: The data to decode into the address ''' self.address, self.and_mask, self.or_mask = struct.unpack('>HHH', data) def execute(self, context): ''' Run a mask write register request against the store :param context: The datastore to request from :returns: The populated response ''' if not (0x0000 <= self.and_mask <= 0xffff): return self.doException(merror.IllegalValue) if not (0x0000 <= self.or_mask <= 0xffff): return self.doException(merror.IllegalValue) if not context.validate(self.function_code, self.address, 1): return self.doException(merror.IllegalAddress) values = context.getValues(self.function_code, self.address, 1)[0] values = ((values & self.and_mask) | self.or_mask) context.setValues(self.function_code, self.address, [values]) return MaskWriteRegisterResponse(self.address, self.and_mask, self.or_mask) class MaskWriteRegisterResponse(ModbusResponse): ''' The normal response is an echo of the request. The response is returned after the register has been written. ''' function_code = 0x16 _rtu_frame_size = 10 def __init__(self, address=0x0000, and_mask=0xffff, or_mask=0x0000, **kwargs): ''' Initializes a new instance :param address: The mask pointer address (0x0000 to 0xffff) :param and_mask: The and bitmask applied to the register address :param or_mask: The or bitmask applied to the register address ''' ModbusResponse.__init__(self, **kwargs) self.address = address self.and_mask = and_mask self.or_mask = or_mask def encode(self): ''' Encodes the response :returns: The byte encoded message ''' return struct.pack('>HHH', self.address, self.and_mask, self.or_mask) def decode(self, data): ''' Decodes a the response :param data: The packet data to decode ''' self.address, self.and_mask, self.or_mask = struct.unpack('>HHH', data) class ReadFifoQueueRequest(ModbusRequest): ''' This function code allows to read the contents of a First-In-First-Out (FIFO) queue of register in a remote device. The function returns a count of the registers in the queue, followed by the queued data. Up to 32 registers can be read: the count, plus up to 31 queued data registers. The queue count register is returned first, followed by the queued data registers. The function reads the queue contents, but does not clear them. ''' function_code = 0x18 _rtu_frame_size = 6 def __init__(self, address=0x0000, **kwargs): ''' Initializes a new instance :param address: The fifo pointer address (0x0000 to 0xffff) ''' ModbusRequest.__init__(self, **kwargs) self.address = address self.values = [] # this should be added to the context def encode(self): ''' Encodes the request packet :returns: The byte encoded packet ''' return struct.pack('>H', self.address) def decode(self, data): ''' Decodes the incoming request :param data: The data to decode into the address ''' self.address = struct.unpack('>H', data)[0] def execute(self, context): ''' Run a read exeception status request against the store :param context: The datastore to request from :returns: The populated response ''' if not (0x0000 <= self.address <= 0xffff): return self.doException(merror.IllegalValue) if len(self.values) > 31: return self.doException(merror.IllegalValue) # TODO pull the values from some context return ReadFifoQueueResponse(self.values) class ReadFifoQueueResponse(ModbusResponse): ''' In a normal response, the byte count shows the quantity of bytes to follow, including the queue count bytes and value register bytes (but not including the error check field). The queue count is the quantity of data registers in the queue (not including the count register). If the queue count exceeds 31, an exception response is returned with an error code of 03 (Illegal Data Value). ''' function_code = 0x18 @classmethod def calculateRtuFrameSize(cls, buffer): ''' Calculates the size of the message :param buffer: A buffer containing the data that have been received. :returns: The number of bytes in the response. ''' hi_byte = struct.unpack(">B", buffer[2])[0] lo_byte = struct.unpack(">B", buffer[3])[0] return (hi_byte << 16) + lo_byte + 6 def __init__(self, values=None, **kwargs): ''' Initializes a new instance :param values: The list of values of the fifo to return ''' ModbusResponse.__init__(self, **kwargs) self.values = values or [] def encode(self): ''' Encodes the response :returns: The byte encoded message ''' length = len(self.values) * 2 packet = struct.pack('>HH', 2 + length, length) for value in self.values: packet += struct.pack('>H', value) return packet def decode(self, data): ''' Decodes a the response :param data: The packet data to decode ''' self.values = [] _, count = struct.unpack('>HH', data[0:4]) for index in xrange(0, count - 4): idx = 4 + index * 2 self.values.append(struct.unpack('>H', data[idx:idx + 2])[0]) #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "FileRecord", "ReadFileRecordRequest", "ReadFileRecordResponse", "WriteFileRecordRequest", "WriteFileRecordResponse", "MaskWriteRegisterRequest", "MaskWriteRegisterResponse", "ReadFifoQueueRequest", "ReadFifoQueueResponse", ] pymodbus/pymodbus/bit_write_message.py0000644000175500017550000002117012607272152020367 0ustar debacledebacle""" Bit Writing Request/Response ------------------------------ TODO write mask request/response """ import struct from pymodbus.constants import ModbusStatus from pymodbus.pdu import ModbusRequest from pymodbus.pdu import ModbusResponse from pymodbus.pdu import ModbusExceptions as merror from pymodbus.utilities import pack_bitstring, unpack_bitstring #---------------------------------------------------------------------------# # Local Constants #---------------------------------------------------------------------------# # These are defined in the spec to turn a coil on/off #---------------------------------------------------------------------------# _turn_coil_on = struct.pack(">H", ModbusStatus.On) _turn_coil_off = struct.pack(">H", ModbusStatus.Off) class WriteSingleCoilRequest(ModbusRequest): ''' This function code is used to write a single output to either ON or OFF in a remote device. The requested ON/OFF state is specified by a constant in the request data field. A value of FF 00 hex requests the output to be ON. A value of 00 00 requests it to be OFF. All other values are illegal and will not affect the output. The Request PDU specifies the address of the coil to be forced. Coils are addressed starting at zero. Therefore coil numbered 1 is addressed as 0. The requested ON/OFF state is specified by a constant in the Coil Value field. A value of 0XFF00 requests the coil to be ON. A value of 0X0000 requests the coil to be off. All other values are illegal and will not affect the coil. ''' function_code = 5 _rtu_frame_size = 8 def __init__(self, address=None, value=None, **kwargs): ''' Initializes a new instance :param address: The variable address to write :param value: The value to write at address ''' ModbusRequest.__init__(self, **kwargs) self.address = address self.value = bool(value) def encode(self): ''' Encodes write coil request :returns: The byte encoded message ''' result = struct.pack('>H', self.address) if self.value: result += _turn_coil_on else: result += _turn_coil_off return result def decode(self, data): ''' Decodes a write coil request :param data: The packet data to decode ''' self.address, value = struct.unpack('>HH', data) self.value = (value == ModbusStatus.On) def execute(self, context): ''' Run a write coil request against a datastore :param context: The datastore to request from :returns: The populated response or exception message ''' #if self.value not in [ModbusStatus.Off, ModbusStatus.On]: # return self.doException(merror.IllegalValue) if not context.validate(self.function_code, self.address, 1): return self.doException(merror.IllegalAddress) context.setValues(self.function_code, self.address, [self.value]) values = context.getValues(self.function_code, self.address, 1) return WriteSingleCoilResponse(self.address, values[0]) def __str__(self): ''' Returns a string representation of the instance :return: A string representation of the instance ''' return "WriteCoilRequest(%d, %s) => " % (self.address, self.value) class WriteSingleCoilResponse(ModbusResponse): ''' The normal response is an echo of the request, returned after the coil state has been written. ''' function_code = 5 _rtu_frame_size = 8 def __init__(self, address=None, value=None, **kwargs): ''' Initializes a new instance :param address: The variable address written to :param value: The value written at address ''' ModbusResponse.__init__(self, **kwargs) self.address = address self.value = value def encode(self): ''' Encodes write coil response :return: The byte encoded message ''' result = struct.pack('>H', self.address) if self.value: result += _turn_coil_on else: result += _turn_coil_off return result def decode(self, data): ''' Decodes a write coil response :param data: The packet data to decode ''' self.address, value = struct.unpack('>HH', data) self.value = (value == ModbusStatus.On) def __str__(self): ''' Returns a string representation of the instance :returns: A string representation of the instance ''' return "WriteCoilResponse(%d) => %d" % (self.address, self.value) class WriteMultipleCoilsRequest(ModbusRequest): ''' "This function code is used to force each coil in a sequence of coils to either ON or OFF in a remote device. The Request PDU specifies the coil references to be forced. Coils are addressed starting at zero. Therefore coil numbered 1 is addressed as 0. The requested ON/OFF states are specified by contents of the request data field. A logical '1' in a bit position of the field requests the corresponding output to be ON. A logical '0' requests it to be OFF." ''' function_code = 15 _rtu_byte_count_pos = 6 def __init__(self, address=None, values=None, **kwargs): ''' Initializes a new instance :param address: The starting request address :param values: The values to write ''' ModbusRequest.__init__(self, **kwargs) self.address = address if not values: values = [] elif not hasattr(values, '__iter__'): values = [values] self.values = values self.byte_count = (len(self.values) + 7) / 8 def encode(self): ''' Encodes write coils request :returns: The byte encoded message ''' count = len(self.values) self.byte_count = (count + 7) / 8 packet = struct.pack('>HHB', self.address, count, self.byte_count) packet += pack_bitstring(self.values) return packet def decode(self, data): ''' Decodes a write coils request :param data: The packet data to decode ''' self.address, count, self.byte_count = struct.unpack('>HHB', data[0:5]) values = unpack_bitstring(data[5:]) self.values = values[:count] def execute(self, context): ''' Run a write coils request against a datastore :param context: The datastore to request from :returns: The populated response or exception message ''' count = len(self.values) if not (1 <= count <= 0x07b0): return self.doException(merror.IllegalValue) if (self.byte_count != (count + 7) / 8): return self.doException(merror.IllegalValue) if not context.validate(self.function_code, self.address, count): return self.doException(merror.IllegalAddress) context.setValues(self.function_code, self.address, self.values) return WriteMultipleCoilsResponse(self.address, count) def __str__(self): ''' Returns a string representation of the instance :returns: A string representation of the instance ''' params = (self.address, len(self.values)) return "WriteNCoilRequest (%d) => %d " % params class WriteMultipleCoilsResponse(ModbusResponse): ''' The normal response returns the function code, starting address, and quantity of coils forced. ''' function_code = 15 _rtu_frame_size = 8 def __init__(self, address=None, count=None, **kwargs): ''' Initializes a new instance :param address: The starting variable address written to :param count: The number of values written ''' ModbusResponse.__init__(self, **kwargs) self.address = address self.count = count def encode(self): ''' Encodes write coils response :returns: The byte encoded message ''' return struct.pack('>HH', self.address, self.count) def decode(self, data): ''' Decodes a write coils response :param data: The packet data to decode ''' self.address, self.count = struct.unpack('>HH', data) def __str__(self): ''' Returns a string representation of the instance :returns: A string representation of the instance ''' return "WriteNCoilResponse(%d, %d)" % (self.address, self.count) #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "WriteSingleCoilRequest", "WriteSingleCoilResponse", "WriteMultipleCoilsRequest", "WriteMultipleCoilsResponse", ] pymodbus/pymodbus/diag_message.py0000644000175500017550000006551512607272152017316 0ustar debacledebacle''' Diagnostic Record Read/Write ------------------------------ These need to be tied into a the current server context or linked to the appropriate data ''' import struct from pymodbus.constants import ModbusStatus, ModbusPlusOperation from pymodbus.pdu import ModbusRequest from pymodbus.pdu import ModbusResponse from pymodbus.device import ModbusControlBlock from pymodbus.exceptions import NotImplementedException from pymodbus.utilities import pack_bitstring _MCB = ModbusControlBlock() #---------------------------------------------------------------------------# # Diagnostic Function Codes Base Classes # diagnostic 08, 00-18,20 #---------------------------------------------------------------------------# # TODO Make sure all the data is decoded from the response #---------------------------------------------------------------------------# class DiagnosticStatusRequest(ModbusRequest): ''' This is a base class for all of the diagnostic request functions ''' function_code = 0x08 _rtu_frame_size = 8 def __init__(self, **kwargs): ''' Base initializer for a diagnostic request ''' ModbusRequest.__init__(self, **kwargs) self.message = None def encode(self): ''' Base encoder for a diagnostic response we encode the data set in self.message :returns: The encoded packet ''' packet = struct.pack('>H', self.sub_function_code) if self.message is not None: if isinstance(self.message, str): packet += self.message elif isinstance(self.message, list): for piece in self.message: packet += struct.pack('>H', piece) elif isinstance(self.message, int): packet += struct.pack('>H', self.message) return packet def decode(self, data): ''' Base decoder for a diagnostic request :param data: The data to decode into the function code ''' self.sub_function_code, self.message = struct.unpack('>HH', data) class DiagnosticStatusResponse(ModbusResponse): ''' This is a base class for all of the diagnostic response functions It works by performing all of the encoding and decoding of variable data and lets the higher classes define what extra data to append and how to execute a request ''' function_code = 0x08 _rtu_frame_size = 8 def __init__(self, **kwargs): ''' Base initializer for a diagnostic response ''' ModbusResponse.__init__(self, **kwargs) self.message = None def encode(self): ''' Base encoder for a diagnostic response we encode the data set in self.message :returns: The encoded packet ''' packet = struct.pack('>H', self.sub_function_code) if self.message is not None: if isinstance(self.message, str): packet += self.message elif isinstance(self.message, list): for piece in self.message: packet += struct.pack('>H', piece) elif isinstance(self.message, int): packet += struct.pack('>H', self.message) return packet def decode(self, data): ''' Base decoder for a diagnostic response :param data: The data to decode into the function code ''' self.sub_function_code, self.message = struct.unpack('>HH', data) class DiagnosticStatusSimpleRequest(DiagnosticStatusRequest): ''' A large majority of the diagnostic functions are simple status request functions. They work by sending 0x0000 as data and their function code and they are returned 2 bytes of data. If a function inherits this, they only need to implement the execute method ''' def __init__(self, data=0x0000, **kwargs): ''' General initializer for a simple diagnostic request The data defaults to 0x0000 if not provided as over half of the functions require it. :param data: The data to send along with the request ''' DiagnosticStatusRequest.__init__(self, **kwargs) self.message = data def execute(self, *args): ''' Base function to raise if not implemented ''' raise NotImplementedException("Diagnostic Message Has No Execute Method") class DiagnosticStatusSimpleResponse(DiagnosticStatusResponse): ''' A large majority of the diagnostic functions are simple status request functions. They work by sending 0x0000 as data and their function code and they are returned 2 bytes of data. ''' def __init__(self, data=0x0000, **kwargs): ''' General initializer for a simple diagnostic response :param data: The resulting data to return to the client ''' DiagnosticStatusResponse.__init__(self, **kwargs) self.message = data #---------------------------------------------------------------------------# # Diagnostic Sub Code 00 #---------------------------------------------------------------------------# class ReturnQueryDataRequest(DiagnosticStatusRequest): ''' The data passed in the request data field is to be returned (looped back) in the response. The entire response message should be identical to the request. ''' sub_function_code = 0x0000 def __init__(self, message=0x0000, **kwargs): ''' Initializes a new instance of the request :param message: The message to send to loopback ''' DiagnosticStatusRequest.__init__(self, **kwargs) if isinstance(message, list): self.message = message else: self.message = [message] def execute(self, *args): ''' Executes the loopback request (builds the response) :returns: The populated loopback response message ''' return ReturnQueryDataResponse(self.message) class ReturnQueryDataResponse(DiagnosticStatusResponse): ''' The data passed in the request data field is to be returned (looped back) in the response. The entire response message should be identical to the request. ''' sub_function_code = 0x0000 def __init__(self, message=0x0000, **kwargs): ''' Initializes a new instance of the response :param message: The message to loopback ''' DiagnosticStatusResponse.__init__(self, **kwargs) if isinstance(message, list): self.message = message else: self.message = [message] #---------------------------------------------------------------------------# # Diagnostic Sub Code 01 #---------------------------------------------------------------------------# class RestartCommunicationsOptionRequest(DiagnosticStatusRequest): ''' The remote device serial line port must be initialized and restarted, and all of its communications event counters are cleared. If the port is currently in Listen Only Mode, no response is returned. This function is the only one that brings the port out of Listen Only Mode. If the port is not currently in Listen Only Mode, a normal response is returned. This occurs before the restart is executed. ''' sub_function_code = 0x0001 def __init__(self, toggle=False, **kwargs): ''' Initializes a new request :param toggle: Set to True to toggle, False otherwise ''' DiagnosticStatusRequest.__init__(self, **kwargs) if toggle: self.message = [ModbusStatus.On] else: self.message = [ModbusStatus.Off] def execute(self, *args): ''' Clear event log and restart :returns: The initialized response message ''' #if _MCB.ListenOnly: return RestartCommunicationsOptionResponse(self.message) class RestartCommunicationsOptionResponse(DiagnosticStatusResponse): ''' The remote device serial line port must be initialized and restarted, and all of its communications event counters are cleared. If the port is currently in Listen Only Mode, no response is returned. This function is the only one that brings the port out of Listen Only Mode. If the port is not currently in Listen Only Mode, a normal response is returned. This occurs before the restart is executed. ''' sub_function_code = 0x0001 def __init__(self, toggle=False, **kwargs): ''' Initializes a new response :param toggle: Set to True if we toggled, False otherwise ''' DiagnosticStatusResponse.__init__(self, **kwargs) if toggle: self.message = [ModbusStatus.On] else: self.message = [ModbusStatus.Off] #---------------------------------------------------------------------------# # Diagnostic Sub Code 02 #---------------------------------------------------------------------------# class ReturnDiagnosticRegisterRequest(DiagnosticStatusSimpleRequest): ''' The contents of the remote device's 16-bit diagnostic register are returned in the response ''' sub_function_code = 0x0002 def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' #if _MCB.isListenOnly(): register = pack_bitstring(_MCB.getDiagnosticRegister()) return ReturnDiagnosticRegisterResponse(register) class ReturnDiagnosticRegisterResponse(DiagnosticStatusSimpleResponse): ''' The contents of the remote device's 16-bit diagnostic register are returned in the response ''' sub_function_code = 0x0002 #---------------------------------------------------------------------------# # Diagnostic Sub Code 03 #---------------------------------------------------------------------------# class ChangeAsciiInputDelimiterRequest(DiagnosticStatusSimpleRequest): ''' The character 'CHAR' passed in the request data field becomes the end of message delimiter for future messages (replacing the default LF character). This function is useful in cases of a Line Feed is not required at the end of ASCII messages. ''' sub_function_code = 0x0003 def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' char = (self.message & 0xff00) >> 8 _MCB.Delimiter = char return ChangeAsciiInputDelimiterResponse(self.message) class ChangeAsciiInputDelimiterResponse(DiagnosticStatusSimpleResponse): ''' The character 'CHAR' passed in the request data field becomes the end of message delimiter for future messages (replacing the default LF character). This function is useful in cases of a Line Feed is not required at the end of ASCII messages. ''' sub_function_code = 0x0003 #---------------------------------------------------------------------------# # Diagnostic Sub Code 04 #---------------------------------------------------------------------------# class ForceListenOnlyModeRequest(DiagnosticStatusSimpleRequest): ''' Forces the addressed remote device to its Listen Only Mode for MODBUS communications. This isolates it from the other devices on the network, allowing them to continue communicating without interruption from the addressed remote device. No response is returned. ''' sub_function_code = 0x0004 def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' _MCB.ListenOnly = True return ForceListenOnlyModeResponse() class ForceListenOnlyModeResponse(DiagnosticStatusResponse): ''' Forces the addressed remote device to its Listen Only Mode for MODBUS communications. This isolates it from the other devices on the network, allowing them to continue communicating without interruption from the addressed remote device. No response is returned. This does not send a response ''' sub_function_code = 0x0004 should_respond = False def __init__(self, **kwargs): ''' Initializer to block a return response ''' DiagnosticStatusResponse.__init__(self, **kwargs) self.message = [] #---------------------------------------------------------------------------# # Diagnostic Sub Code 10 #---------------------------------------------------------------------------# class ClearCountersRequest(DiagnosticStatusSimpleRequest): ''' The goal is to clear ll counters and the diagnostic register. Also, counters are cleared upon power-up ''' sub_function_code = 0x000A def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' _MCB.reset() return ClearCountersResponse(self.message) class ClearCountersResponse(DiagnosticStatusSimpleResponse): ''' The goal is to clear ll counters and the diagnostic register. Also, counters are cleared upon power-up ''' sub_function_code = 0x000A #---------------------------------------------------------------------------# # Diagnostic Sub Code 11 #---------------------------------------------------------------------------# class ReturnBusMessageCountRequest(DiagnosticStatusSimpleRequest): ''' The response data field returns the quantity of messages that the remote device has detected on the communications systems since its last restart, clear counters operation, or power-up ''' sub_function_code = 0x000B def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' count = _MCB.Counter.BusMessage return ReturnBusMessageCountResponse(count) class ReturnBusMessageCountResponse(DiagnosticStatusSimpleResponse): ''' The response data field returns the quantity of messages that the remote device has detected on the communications systems since its last restart, clear counters operation, or power-up ''' sub_function_code = 0x000B #---------------------------------------------------------------------------# # Diagnostic Sub Code 12 #---------------------------------------------------------------------------# class ReturnBusCommunicationErrorCountRequest(DiagnosticStatusSimpleRequest): ''' The response data field returns the quantity of CRC errors encountered by the remote device since its last restart, clear counter operation, or power-up ''' sub_function_code = 0x000C def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' count = _MCB.Counter.BusCommunicationError return ReturnBusCommunicationErrorCountResponse(count) class ReturnBusCommunicationErrorCountResponse(DiagnosticStatusSimpleResponse): ''' The response data field returns the quantity of CRC errors encountered by the remote device since its last restart, clear counter operation, or power-up ''' sub_function_code = 0x000C #---------------------------------------------------------------------------# # Diagnostic Sub Code 13 #---------------------------------------------------------------------------# class ReturnBusExceptionErrorCountRequest(DiagnosticStatusSimpleRequest): ''' The response data field returns the quantity of modbus exception responses returned by the remote device since its last restart, clear counters operation, or power-up ''' sub_function_code = 0x000D def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' count = _MCB.Counter.BusExceptionError return ReturnBusExceptionErrorCountResponse(count) class ReturnBusExceptionErrorCountResponse(DiagnosticStatusSimpleResponse): ''' The response data field returns the quantity of modbus exception responses returned by the remote device since its last restart, clear counters operation, or power-up ''' sub_function_code = 0x000D #---------------------------------------------------------------------------# # Diagnostic Sub Code 14 #---------------------------------------------------------------------------# class ReturnSlaveMessageCountRequest(DiagnosticStatusSimpleRequest): ''' The response data field returns the quantity of messages addressed to the remote device, or broadcast, that the remote device has processed since its last restart, clear counters operation, or power-up ''' sub_function_code = 0x000E def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' count = _MCB.Counter.SlaveMessage return ReturnSlaveMessageCountResponse(count) class ReturnSlaveMessageCountResponse(DiagnosticStatusSimpleResponse): ''' The response data field returns the quantity of messages addressed to the remote device, or broadcast, that the remote device has processed since its last restart, clear counters operation, or power-up ''' sub_function_code = 0x000E #---------------------------------------------------------------------------# # Diagnostic Sub Code 15 #---------------------------------------------------------------------------# class ReturnSlaveNoResponseCountRequest(DiagnosticStatusSimpleRequest): ''' The response data field returns the quantity of messages addressed to the remote device, or broadcast, that the remote device has processed since its last restart, clear counters operation, or power-up ''' sub_function_code = 0x000F def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' count = _MCB.Counter.SlaveNoResponse return ReturnSlaveNoReponseCountResponse(count) class ReturnSlaveNoReponseCountResponse(DiagnosticStatusSimpleResponse): ''' The response data field returns the quantity of messages addressed to the remote device, or broadcast, that the remote device has processed since its last restart, clear counters operation, or power-up ''' sub_function_code = 0x000F #---------------------------------------------------------------------------# # Diagnostic Sub Code 16 #---------------------------------------------------------------------------# class ReturnSlaveNAKCountRequest(DiagnosticStatusSimpleRequest): ''' The response data field returns the quantity of messages addressed to the remote device for which it returned a Negative Acknowledge (NAK) exception response, since its last restart, clear counters operation, or power-up. Exception responses are described and listed in section 7 . ''' sub_function_code = 0x0010 def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' count = _MCB.Counter.SlaveNAK return ReturnSlaveNAKCountResponse(count) class ReturnSlaveNAKCountResponse(DiagnosticStatusSimpleResponse): ''' The response data field returns the quantity of messages addressed to the remote device for which it returned a Negative Acknowledge (NAK) exception response, since its last restart, clear counters operation, or power-up. Exception responses are described and listed in section 7. ''' sub_function_code = 0x0010 #---------------------------------------------------------------------------# # Diagnostic Sub Code 17 #---------------------------------------------------------------------------# class ReturnSlaveBusyCountRequest(DiagnosticStatusSimpleRequest): ''' The response data field returns the quantity of messages addressed to the remote device for which it returned a Slave Device Busy exception response, since its last restart, clear counters operation, or power-up. ''' sub_function_code = 0x0011 def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' count = _MCB.Counter.SlaveBusy return ReturnSlaveBusyCountResponse(count) class ReturnSlaveBusyCountResponse(DiagnosticStatusSimpleResponse): ''' The response data field returns the quantity of messages addressed to the remote device for which it returned a Slave Device Busy exception response, since its last restart, clear counters operation, or power-up. ''' sub_function_code = 0x0011 #---------------------------------------------------------------------------# # Diagnostic Sub Code 18 #---------------------------------------------------------------------------# class ReturnSlaveBusCharacterOverrunCountRequest(DiagnosticStatusSimpleRequest): ''' The response data field returns the quantity of messages addressed to the remote device that it could not handle due to a character overrun condition, since its last restart, clear counters operation, or power-up. A character overrun is caused by data characters arriving at the port faster than they can be stored, or by the loss of a character due to a hardware malfunction. ''' sub_function_code = 0x0012 def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' count = _MCB.Counter.BusCharacterOverrun return ReturnSlaveBusCharacterOverrunCountResponse(count) class ReturnSlaveBusCharacterOverrunCountResponse(DiagnosticStatusSimpleResponse): ''' The response data field returns the quantity of messages addressed to the remote device that it could not handle due to a character overrun condition, since its last restart, clear counters operation, or power-up. A character overrun is caused by data characters arriving at the port faster than they can be stored, or by the loss of a character due to a hardware malfunction. ''' sub_function_code = 0x0012 #---------------------------------------------------------------------------# # Diagnostic Sub Code 19 #---------------------------------------------------------------------------# class ReturnIopOverrunCountRequest(DiagnosticStatusSimpleRequest): ''' An IOP overrun is caused by data characters arriving at the port faster than they can be stored, or by the loss of a character due to a hardware malfunction. This function is specific to the 884. ''' sub_function_code = 0x0013 def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' count = _MCB.Counter.BusCharacterOverrun return ReturnIopOverrunCountResponse(count) class ReturnIopOverrunCountResponse(DiagnosticStatusSimpleResponse): ''' The response data field returns the quantity of messages addressed to the slave that it could not handle due to an 884 IOP overrun condition, since its last restart, clear counters operation, or power-up. ''' sub_function_code = 0x0013 #---------------------------------------------------------------------------# # Diagnostic Sub Code 20 #---------------------------------------------------------------------------# class ClearOverrunCountRequest(DiagnosticStatusSimpleRequest): ''' Clears the overrun error counter and reset the error flag An error flag should be cleared, but nothing else in the specification mentions is, so it is ignored. ''' sub_function_code = 0x0014 def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' _MCB.Counter.BusCharacterOverrun = 0x0000 return ClearOverrunCountResponse(self.message) class ClearOverrunCountResponse(DiagnosticStatusSimpleResponse): ''' Clears the overrun error counter and reset the error flag ''' sub_function_code = 0x0014 #---------------------------------------------------------------------------# # Diagnostic Sub Code 21 #---------------------------------------------------------------------------# class GetClearModbusPlusRequest(DiagnosticStatusSimpleRequest): ''' In addition to the Function code (08) and Subfunction code (00 15 hex) in the query, a two-byte Operation field is used to specify either a 'Get Statistics' or a 'Clear Statistics' operation. The two operations are exclusive - the 'Get' operation cannot clear the statistics, and the 'Clear' operation does not return statistics prior to clearing them. Statistics are also cleared on power-up of the slave device. ''' sub_function_code = 0x0015 def execute(self, *args): ''' Execute the diagnostic request on the given device :returns: The initialized response message ''' message = None # the clear operation does not return info if self.message == ModbusPlusOperation.ClearStatistics: _MCB.Plus.reset() else: message = _MCB.Plus.encode() return GetClearModbusPlusResponse(message) class GetClearModbusPlusResponse(DiagnosticStatusSimpleResponse): ''' Returns a series of 54 16-bit words (108 bytes) in the data field of the response (this function differs from the usual two-byte length of the data field). The data contains the statistics for the Modbus Plus peer processor in the slave device. ''' sub_function_code = 0x0015 #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "DiagnosticStatusRequest", "DiagnosticStatusResponse", "ReturnQueryDataRequest", "ReturnQueryDataResponse", "RestartCommunicationsOptionRequest", "RestartCommunicationsOptionResponse", "ReturnDiagnosticRegisterRequest", "ReturnDiagnosticRegisterResponse", "ChangeAsciiInputDelimiterRequest", "ChangeAsciiInputDelimiterResponse", "ForceListenOnlyModeRequest", "ForceListenOnlyModeResponse", "ClearCountersRequest", "ClearCountersResponse", "ReturnBusMessageCountRequest", "ReturnBusMessageCountResponse", "ReturnBusCommunicationErrorCountRequest", "ReturnBusCommunicationErrorCountResponse", "ReturnBusExceptionErrorCountRequest", "ReturnBusExceptionErrorCountResponse", "ReturnSlaveMessageCountRequest", "ReturnSlaveMessageCountResponse", "ReturnSlaveNoResponseCountRequest", "ReturnSlaveNoReponseCountResponse", "ReturnSlaveNAKCountRequest", "ReturnSlaveNAKCountResponse", "ReturnSlaveBusyCountRequest", "ReturnSlaveBusyCountResponse", "ReturnSlaveBusCharacterOverrunCountRequest", "ReturnSlaveBusCharacterOverrunCountResponse", "ReturnIopOverrunCountRequest", "ReturnIopOverrunCountResponse", "ClearOverrunCountRequest", "ClearOverrunCountResponse", "GetClearModbusPlusRequest", "GetClearModbusPlusResponse", ] pymodbus/pymodbus/register_read_message.py0000644000175500017550000003027512607272152021224 0ustar debacledebacle''' Register Reading Request/Response --------------------------------- ''' import struct from pymodbus.pdu import ModbusRequest from pymodbus.pdu import ModbusResponse from pymodbus.pdu import ModbusExceptions as merror class ReadRegistersRequestBase(ModbusRequest): ''' Base class for reading a modbus register ''' _rtu_frame_size = 8 def __init__(self, address, count, **kwargs): ''' Initializes a new instance :param address: The address to start the read from :param count: The number of registers to read ''' ModbusRequest.__init__(self, **kwargs) self.address = address self.count = count def encode(self): ''' Encodes the request packet :return: The encoded packet ''' return struct.pack('>HH', self.address, self.count) def decode(self, data): ''' Decode a register request packet :param data: The request to decode ''' self.address, self.count = struct.unpack('>HH', data) def __str__(self): ''' Returns a string representation of the instance :returns: A string representation of the instance ''' return "ReadRegisterRequest (%d,%d)" % (self.address, self.count) class ReadRegistersResponseBase(ModbusResponse): ''' Base class for responsing to a modbus register read ''' _rtu_byte_count_pos = 2 def __init__(self, values, **kwargs): ''' Initializes a new instance :param values: The values to write to ''' ModbusResponse.__init__(self, **kwargs) self.registers = values or [] def encode(self): ''' Encodes the response packet :returns: The encoded packet ''' result = chr(len(self.registers) * 2) for register in self.registers: result += struct.pack('>H', register) return result def decode(self, data): ''' Decode a register response packet :param data: The request to decode ''' byte_count = ord(data[0]) self.registers = [] for i in range(1, byte_count + 1, 2): self.registers.append(struct.unpack('>H', data[i:i + 2])[0]) def getRegister(self, index): ''' Get the requested register :param index: The indexed register to retrieve :returns: The request register ''' return self.registers[index] def __str__(self): ''' Returns a string representation of the instance :returns: A string representation of the instance ''' return "ReadRegisterResponse (%d)" % len(self.registers) class ReadHoldingRegistersRequest(ReadRegistersRequestBase): ''' This function code is used to read the contents of a contiguous block of holding registers in a remote device. The Request PDU specifies the starting register address and the number of registers. In the PDU Registers are addressed starting at zero. Therefore registers numbered 1-16 are addressed as 0-15. ''' function_code = 3 def __init__(self, address=None, count=None, **kwargs): ''' Initializes a new instance of the request :param address: The starting address to read from :param count: The number of registers to read from address ''' ReadRegistersRequestBase.__init__(self, address, count, **kwargs) def execute(self, context): ''' Run a read holding request against a datastore :param context: The datastore to request from :returns: An initialized response, exception message otherwise ''' if not (1 <= self.count <= 0x7d): return self.doException(merror.IllegalValue) if not context.validate(self.function_code, self.address, self.count): return self.doException(merror.IllegalAddress) values = context.getValues(self.function_code, self.address, self.count) return ReadHoldingRegistersResponse(values) class ReadHoldingRegistersResponse(ReadRegistersResponseBase): ''' This function code is used to read the contents of a contiguous block of holding registers in a remote device. The Request PDU specifies the starting register address and the number of registers. In the PDU Registers are addressed starting at zero. Therefore registers numbered 1-16 are addressed as 0-15. ''' function_code = 3 def __init__(self, values=None, **kwargs): ''' Initializes a new response instance :param values: The resulting register values ''' ReadRegistersResponseBase.__init__(self, values, **kwargs) class ReadInputRegistersRequest(ReadRegistersRequestBase): ''' This function code is used to read from 1 to approx. 125 contiguous input registers in a remote device. The Request PDU specifies the starting register address and the number of registers. In the PDU Registers are addressed starting at zero. Therefore input registers numbered 1-16 are addressed as 0-15. ''' function_code = 4 def __init__(self, address=None, count=None, **kwargs): ''' Initializes a new instance of the request :param address: The starting address to read from :param count: The number of registers to read from address ''' ReadRegistersRequestBase.__init__(self, address, count, **kwargs) def execute(self, context): ''' Run a read input request against a datastore :param context: The datastore to request from :returns: An initialized response, exception message otherwise ''' if not (1 <= self.count <= 0x7d): return self.doException(merror.IllegalValue) if not context.validate(self.function_code, self.address, self.count): return self.doException(merror.IllegalAddress) values = context.getValues(self.function_code, self.address, self.count) return ReadInputRegistersResponse(values) class ReadInputRegistersResponse(ReadRegistersResponseBase): ''' This function code is used to read from 1 to approx. 125 contiguous input registers in a remote device. The Request PDU specifies the starting register address and the number of registers. In the PDU Registers are addressed starting at zero. Therefore input registers numbered 1-16 are addressed as 0-15. ''' function_code = 4 def __init__(self, values=None, **kwargs): ''' Initializes a new response instance :param values: The resulting register values ''' ReadRegistersResponseBase.__init__(self, values, **kwargs) class ReadWriteMultipleRegistersRequest(ModbusRequest): ''' This function code performs a combination of one read operation and one write operation in a single MODBUS transaction. The write operation is performed before the read. Holding registers are addressed starting at zero. Therefore holding registers 1-16 are addressed in the PDU as 0-15. The request specifies the starting address and number of holding registers to be read as well as the starting address, number of holding registers, and the data to be written. The byte count specifies the number of bytes to follow in the write data field." ''' function_code = 23 _rtu_byte_count_pos = 10 def __init__(self, **kwargs): ''' Initializes a new request message :param read_address: The address to start reading from :param read_count: The number of registers to read from address :param write_address: The address to start writing to :param write_registers: The registers to write to the specified address ''' ModbusRequest.__init__(self, **kwargs) self.read_address = kwargs.get('read_address', 0x00) self.read_count = kwargs.get('read_count', 0) self.write_address = kwargs.get('write_address', 0x00) self.write_registers = kwargs.get('write_registers', None) if not hasattr(self.write_registers, '__iter__'): self.write_registers = [self.write_registers] self.write_count = len(self.write_registers) self.write_byte_count = self.write_count * 2 def encode(self): ''' Encodes the request packet :returns: The encoded packet ''' result = struct.pack('>HHHHB', self.read_address, self.read_count, \ self.write_address, self.write_count, self.write_byte_count) for register in self.write_registers: result += struct.pack('>H', register) return result def decode(self, data): ''' Decode the register request packet :param data: The request to decode ''' self.read_address, self.read_count, \ self.write_address, self.write_count, \ self.write_byte_count = struct.unpack('>HHHHB', data[:9]) self.write_registers = [] for i in range(9, self.write_byte_count + 9, 2): register = struct.unpack('>H', data[i:i + 2])[0] self.write_registers.append(register) def execute(self, context): ''' Run a write single register request against a datastore :param context: The datastore to request from :returns: An initialized response, exception message otherwise ''' if not (1 <= self.read_count <= 0x07d): return self.doException(merror.IllegalValue) if not (1 <= self.write_count <= 0x079): return self.doException(merror.IllegalValue) if (self.write_byte_count != self.write_count * 2): return self.doException(merror.IllegalValue) if not context.validate(self.function_code, self.write_address, self.write_count): return self.doException(merror.IllegalAddress) if not context.validate(self.function_code, self.read_address, self.read_count): return self.doException(merror.IllegalAddress) context.setValues(self.function_code, self.write_address, self.write_registers) registers = context.getValues(self.function_code, self.read_address, self.read_count) return ReadWriteMultipleRegistersResponse(registers) def __str__(self): ''' Returns a string representation of the instance :returns: A string representation of the instance ''' params = (self.read_address, self.read_count, self.write_address, self.write_count) return "ReadWriteNRegisterRequest R(%d,%d) W(%d,%d)" % params class ReadWriteMultipleRegistersResponse(ModbusResponse): ''' The normal response contains the data from the group of registers that were read. The byte count field specifies the quantity of bytes to follow in the read data field. ''' function_code = 23 _rtu_byte_count_pos = 2 def __init__(self, values=None, **kwargs): ''' Initializes a new instance :param values: The register values to write ''' ModbusResponse.__init__(self, **kwargs) self.registers = values or [] def encode(self): ''' Encodes the response packet :returns: The encoded packet ''' result = chr(len(self.registers) * 2) for register in self.registers: result += struct.pack('>H', register) return result def decode(self, data): ''' Decode the register response packet :param data: The response to decode ''' bytecount = ord(data[0]) for i in range(1, bytecount, 2): self.registers.append(struct.unpack('>H', data[i:i + 2])[0]) def __str__(self): ''' Returns a string representation of the instance :returns: A string representation of the instance ''' return "ReadWriteNRegisterResponse (%d)" % len(self.registers) #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "ReadHoldingRegistersRequest", "ReadHoldingRegistersResponse", "ReadInputRegistersRequest", "ReadInputRegistersResponse", "ReadWriteMultipleRegistersRequest", "ReadWriteMultipleRegistersResponse", ] pymodbus/pymodbus/utilities.py0000644000175500017550000001334712607272152016715 0ustar debacledebacle''' Modbus Utilities ----------------- A collection of utilities for packing data, unpacking data computing checksums, and decode checksums. ''' import struct #---------------------------------------------------------------------------# # Helpers #---------------------------------------------------------------------------# def default(value): ''' Given a python object, return the default value of that object. :param value: The value to get the default of :returns: The default value ''' return type(value)() def dict_property(store, index): ''' Helper to create class properties from a dictionary. Basically this allows you to remove a lot of possible boilerplate code. :param store: The store store to pull from :param index: The index into the store to close over :returns: An initialized property set ''' if hasattr(store, '__call__'): getter = lambda self: store(self)[index] setter = lambda self, value: store(self).__setitem__(index, value) elif isinstance(store, str): getter = lambda self: self.__getattribute__(store)[index] setter = lambda self, value: self.__getattribute__(store).__setitem__( index, value) else: getter = lambda self: store[index] setter = lambda self, value: store.__setitem__(index, value) return property(getter, setter) #---------------------------------------------------------------------------# # Bit packing functions #---------------------------------------------------------------------------# def pack_bitstring(bits): ''' Creates a string out of an array of bits :param bits: A bit array example:: bits = [False, True, False, True] result = pack_bitstring(bits) ''' ret = '' i = packed = 0 for bit in bits: if bit: packed += 128 i += 1 if i == 8: ret += chr(packed) i = packed = 0 else: packed >>= 1 if i > 0 and i < 8: packed >>= (7 - i) ret += chr(packed) return ret def unpack_bitstring(string): ''' Creates bit array out of a string :param string: The modbus data packet to decode example:: bytes = 'bytes to decode' result = unpack_bitstring(bytes) ''' byte_count = len(string) bits = [] for byte in range(byte_count): value = ord(string[byte]) for _ in range(8): bits.append((value & 1) == 1) value >>= 1 return bits #---------------------------------------------------------------------------# # Error Detection Functions #---------------------------------------------------------------------------# def __generate_crc16_table(): ''' Generates a crc16 lookup table .. note:: This will only be generated once ''' result = [] for byte in range(256): crc = 0x0000 for _ in range(8): if (byte ^ crc) & 0x0001: crc = (crc >> 1) ^ 0xa001 else: crc >>= 1 byte >>= 1 result.append(crc) return result __crc16_table = __generate_crc16_table() def computeCRC(data): ''' Computes a crc16 on the passed in string. For modbus, this is only used on the binary serial protocols (in this case RTU). The difference between modbus's crc16 and a normal crc16 is that modbus starts the crc value out at 0xffff. :param data: The data to create a crc16 of :returns: The calculated CRC ''' crc = 0xffff for a in data: idx = __crc16_table[(crc ^ ord(a)) & 0xff]; crc = ((crc >> 8) & 0xff) ^ idx swapped = ((crc << 8) & 0xff00) | ((crc >> 8) & 0x00ff) return swapped def checkCRC(data, check): ''' Checks if the data matches the passed in CRC :param data: The data to create a crc16 of :param check: The CRC to validate :returns: True if matched, False otherwise ''' return computeCRC(data) == check def computeLRC(data): ''' Used to compute the longitudinal redundancy check against a string. This is only used on the serial ASCII modbus protocol. A full description of this implementation can be found in appendex B of the serial line modbus description. :param data: The data to apply a lrc to :returns: The calculated LRC ''' lrc = sum(ord(a) for a in data) & 0xff lrc = (lrc ^ 0xff) + 1 return lrc & 0xff def checkLRC(data, check): ''' Checks if the passed in data matches the LRC :param data: The data to calculate :param check: The LRC to validate :returns: True if matched, False otherwise ''' return computeLRC(data) == check def rtuFrameSize(buffer, byte_count_pos): ''' Calculates the size of the frame based on the byte count. :param buffer: The buffer containing the frame. :param byte_count_pos: The index of the byte count in the buffer. :returns: The size of the frame. The structure of frames with a byte count field is always the same: - first, there are some header fields - then the byte count field - then as many data bytes as indicated by the byte count, - finally the CRC (two bytes). To calculate the frame size, it is therefore sufficient to extract the contents of the byte count field, add the position of this field, and finally increment the sum by three (one byte for the byte count field, two for the CRC). ''' return struct.unpack('>B', buffer[byte_count_pos])[0] + byte_count_pos + 3 #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ 'pack_bitstring', 'unpack_bitstring', 'default', 'computeCRC', 'checkCRC', 'computeLRC', 'checkLRC', 'rtuFrameSize' ] pymodbus/pymodbus/__init__.py0000644000175500017550000000220212607272152016425 0ustar debacledebacle''' Pymodbus: Modbus Protocol Implementation ----------------------------------------- TwistedModbus is built on top of the code developed by: Copyright (c) 2001-2005 S.W.A.C. GmbH, Germany. Copyright (c) 2001-2005 S.W.A.C. Bohemia s.r.o., Czech Republic. Hynek Petrak Released under the the BSD license ''' import pymodbus.version as __version __version__ = __version.version.short() __author__ = 'Galen Collins' #---------------------------------------------------------------------------# # Block unhandled logging #---------------------------------------------------------------------------# import logging as __logging try: from logging import NullHandler as __null except ImportError: class __null(__logging.Handler): def emit(self, record): pass __logging.getLogger(__name__).addHandler(__null()) #---------------------------------------------------------------------------# # Define True and False if we don't have them (2.3.2) #---------------------------------------------------------------------------# try: True, False except NameError: True, False = (1 == 1), (0 == 1) pymodbus/pymodbus/version.py0000644000175500017550000000247712607272152016371 0ustar debacledebacle''' Handle the version information here; you should only have to change the version tuple. Since we are using twisted's version class, we can also query the svn version as well using the local .entries file. ''' class Version(object): def __init__(self, package, major, minor, micro): ''' :param package: Name of the package that this is a version of. :param major: The major version number. :param minor: The minor version number. :param micro: The micro version number. ''' self.package = package self.major = major self.minor = minor self.micro = micro def short(self): ''' Return a string in canonical short version format .. ''' return '%d.%d.%d' % (self.major, self.minor, self.micro) def __str__(self): ''' Returns a string representation of the object :returns: A string representation of this object ''' return '[%s, version %s]' % (self.package, self.short()) version = Version('pymodbus', 1, 3, 0) version.__name__ = 'pymodbus' # fix epydoc error #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = ["version"] pymodbus/pymodbus/interfaces.py0000644000175500017550000001750412607272152017024 0ustar debacledebacle''' Pymodbus Interfaces --------------------- A collection of base classes that are used throughout the pymodbus library. ''' from pymodbus.exceptions import NotImplementedException #---------------------------------------------------------------------------# # Generic #---------------------------------------------------------------------------# class Singleton(object): ''' Singleton base class http://mail.python.org/pipermail/python-list/2007-July/450681.html ''' def __new__(cls, *args, **kwargs): ''' Create a new instance ''' if '_inst' not in vars(cls): cls._inst = object.__new__(cls) return cls._inst #---------------------------------------------------------------------------# # Project Specific #---------------------------------------------------------------------------# class IModbusDecoder(object): ''' Modbus Decoder Base Class This interface must be implemented by a modbus message decoder factory. These factories are responsible for abstracting away converting a raw packet into a request / response message object. ''' def decode(self, message): ''' Wrapper to decode a given packet :param message: The raw modbus request packet :return: The decoded modbus message or None if error ''' raise NotImplementedException( "Method not implemented by derived class") def lookupPduClass(self, function_code): ''' Use `function_code` to determine the class of the PDU. :param function_code: The function code specified in a frame. :returns: The class of the PDU that has a matching `function_code`. ''' raise NotImplementedException( "Method not implemented by derived class") class IModbusFramer(object): ''' A framer strategy interface. The idea is that we abstract away all the detail about how to detect if a current message frame exists, decoding it, sending it, etc so that we can plug in a new Framer object (tcp, rtu, ascii). ''' def checkFrame(self): ''' Check and decode the next frame :returns: True if we successful, False otherwise ''' raise NotImplementedException( "Method not implemented by derived class") def advanceFrame(self): ''' Skip over the current framed message This allows us to skip over the current message after we have processed it or determined that it contains an error. It also has to reset the current frame header handle ''' raise NotImplementedException( "Method not implemented by derived class") def addToFrame(self, message): ''' Add the next message to the frame buffer This should be used before the decoding while loop to add the received data to the buffer handle. :param message: The most recent packet ''' raise NotImplementedException( "Method not implemented by derived class") def isFrameReady(self): ''' Check if we should continue decode logic This is meant to be used in a while loop in the decoding phase to let the decoder know that there is still data in the buffer. :returns: True if ready, False otherwise ''' raise NotImplementedException( "Method not implemented by derived class") def getFrame(self): ''' Get the next frame from the buffer :returns: The frame data or '' ''' raise NotImplementedException( "Method not implemented by derived class") def populateResult(self, result): ''' Populates the modbus result with current frame header We basically copy the data back over from the current header to the result header. This may not be needed for serial messages. :param result: The response packet ''' raise NotImplementedException( "Method not implemented by derived class") def processIncomingPacket(self, data, callback): ''' The new packet processing pattern This takes in a new request packet, adds it to the current packet stream, and performs framing on it. That is, checks for complete messages, and once found, will process all that exist. This handles the case when we read N + 1 or 1 / N messages at a time instead of 1. The processed and decoded messages are pushed to the callback function to process and send. :param data: The new packet data :param callback: The function to send results to ''' raise NotImplementedException( "Method not implemented by derived class") def buildPacket(self, message): ''' Creates a ready to send modbus packet The raw packet is built off of a fully populated modbus request / response message. :param message: The request/response to send :returns: The built packet ''' raise NotImplementedException( "Method not implemented by derived class") class IModbusSlaveContext(object): ''' Interface for a modbus slave data context Derived classes must implemented the following methods: reset(self) validate(self, fx, address, count=1) getValues(self, fx, address, count=1) setValues(self, fx, address, values) ''' __fx_mapper = {2: 'd', 4: 'i'} __fx_mapper.update([(i, 'h') for i in [3, 6, 16, 22, 23]]) __fx_mapper.update([(i, 'c') for i in [1, 5, 15]]) def decode(self, fx): ''' Converts the function code to the datastore to :param fx: The function we are working with :returns: one of [d(iscretes),i(inputs),h(oliding),c(oils) ''' return self.__fx_mapper[fx] def reset(self): ''' Resets all the datastores to their default values ''' raise NotImplementedException("Context Reset") def validate(self, fx, address, count=1): ''' Validates the request to make sure it is in range :param fx: The function we are working with :param address: The starting address :param count: The number of values to test :returns: True if the request in within range, False otherwise ''' raise NotImplementedException("validate context values") def getValues(self, fx, address, count=1): ''' Validates the request to make sure it is in range :param fx: The function we are working with :param address: The starting address :param count: The number of values to retrieve :returns: The requested values from a:a+c ''' raise NotImplementedException("get context values") def setValues(self, fx, address, values): ''' Sets the datastore with the supplied values :param fx: The function we are working with :param address: The starting address :param values: The new values to be set ''' raise NotImplementedException("set context values") class IPayloadBuilder(object): ''' This is an interface to a class that can build a payload for a modbus register write command. It should abstract the codec for encoding data to the required format (bcd, binary, char, etc). ''' def build(self): ''' Return the payload buffer as a list This list is two bytes per element and can thus be treated as a list of registers. :returns: The payload buffer as a list ''' raise NotImplementedException("set context values") #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ 'Singleton', 'IModbusDecoder', 'IModbusFramer', 'IModbusSlaveContext', 'IPayloadBuilder', ] pymodbus/pymodbus/device.py0000644000175500017550000005435412607272152016144 0ustar debacledebacle""" Modbus Device Controller ------------------------- These are the device management handlers. They should be maintained in the server context and the various methods should be inserted in the correct locations. """ from itertools import izip from pymodbus.constants import DeviceInformation from pymodbus.interfaces import Singleton from pymodbus.utilities import dict_property #---------------------------------------------------------------------------# # Network Access Control #---------------------------------------------------------------------------# class ModbusAccessControl(Singleton): ''' This is a simple implementation of a Network Management System table. Its purpose is to control access to the server (if it is used). We assume that if an entry is in the table, it is allowed accesses to resources. However, if the host does not appear in the table (all unknown hosts) its connection will simply be closed. Since it is a singleton, only one version can possible exist and all instances pull from here. ''' __nmstable = [ "127.0.0.1", ] def __iter__(self): ''' Iterater over the network access table :returns: An iterator of the network access table ''' return self.__nmstable.__iter__() def __contains__(self, host): ''' Check if a host is allowed to access resources :param host: The host to check ''' return host in self.__nmstable def add(self, host): ''' Add allowed host(s) from the NMS table :param host: The host to add ''' if not isinstance(host, list): host = [host] for entry in host: if entry not in self.__nmstable: self.__nmstable.append(entry) def remove(self, host): ''' Remove allowed host(s) from the NMS table :param host: The host to remove ''' if not isinstance(host, list): host = [host] for entry in host: if entry in self.__nmstable: self.__nmstable.remove(entry) def check(self, host): ''' Check if a host is allowed to access resources :param host: The host to check ''' return host in self.__nmstable #---------------------------------------------------------------------------# # Modbus Plus Statistics #---------------------------------------------------------------------------# class ModbusPlusStatistics(object): ''' This is used to maintain the current modbus plus statistics count. As of right now this is simply a stub to complete the modbus implementation. For more information, see the modbus implementation guide page 87. ''' __data = { 'node_type_id' : [0x00] * 2, # 00 'software_version_number' : [0x00] * 2, # 01 'network_address' : [0x00] * 2, # 02 'mac_state_variable' : [0x00] * 2, # 03 'peer_status_code' : [0x00] * 2, # 04 'token_pass_counter' : [0x00] * 2, # 05 'token_rotation_time' : [0x00] * 2, # 06 'program_master_token_failed' : [0x00], # 07 hi 'data_master_token_failed' : [0x00], # 07 lo 'program_master_token_owner' : [0x00], # 08 hi 'data_master_token_owner' : [0x00], # 08 lo 'program_slave_token_owner' : [0x00], # 09 hi 'data_slave_token_owner' : [0x00], # 09 lo 'data_slave_command_transfer' : [0x00], # 10 hi '__unused_10_lowbit' : [0x00], # 10 lo 'program_slave_command_transfer' : [0x00], # 11 hi 'program_master_rsp_transfer' : [0x00], # 11 lo 'program_slave_auto_logout' : [0x00], # 12 hi 'program_master_connect_status' : [0x00], # 12 lo 'receive_buffer_dma_overrun' : [0x00], # 13 hi 'pretransmit_deferral_error' : [0x00], # 13 lo 'frame_size_error' : [0x00], # 14 hi 'repeated_command_received' : [0x00], # 14 lo 'receiver_alignment_error' : [0x00], # 15 hi 'receiver_collision_abort_error' : [0x00], # 15 lo 'bad_packet_length_error' : [0x00], # 16 hi 'receiver_crc_error' : [0x00], # 16 lo 'transmit_buffer_dma_underrun' : [0x00], # 17 hi 'bad_link_address_error' : [0x00], # 17 lo 'bad_mac_function_code_error' : [0x00], # 18 hi 'internal_packet_length_error' : [0x00], # 18 lo 'communication_failed_error' : [0x00], # 19 hi 'communication_retries' : [0x00], # 19 lo 'no_response_error' : [0x00], # 20 hi 'good_receive_packet' : [0x00], # 20 lo 'unexpected_path_error' : [0x00], # 21 hi 'exception_response_error' : [0x00], # 21 lo 'forgotten_transaction_error' : [0x00], # 22 hi 'unexpected_response_error' : [0x00], # 22 lo 'active_station_bit_map' : [0x00] * 8, # 23-26 'token_station_bit_map' : [0x00] * 8, # 27-30 'global_data_bit_map' : [0x00] * 8, # 31-34 'receive_buffer_use_bit_map' : [0x00] * 8, # 35-37 'data_master_output_path' : [0x00] * 8, # 38-41 'data_slave_input_path' : [0x00] * 8, # 42-45 'program_master_outptu_path' : [0x00] * 8, # 46-49 'program_slave_input_path' : [0x00] * 8, # 50-53 } def __init__(self): ''' Initialize the modbus plus statistics with the default information. ''' self.reset() def __iter__(self): ''' Iterater over the statistics :returns: An iterator of the modbus plus statistics ''' return self.__data.iteritems() def reset(self): ''' This clears all of the modbus plus statistics ''' for key in self.__data: self.__data[key] = [0x00] * len(self.__data[key]) def summary(self): ''' Returns a summary of the modbus plus statistics :returns: 54 16-bit words representing the status ''' return self.__data.values() def encode(self): ''' Returns a summary of the modbus plus statistics :returns: 54 16-bit words representing the status ''' total, values = [], sum(self.__data.values(), []) for c in xrange(0, len(values), 2): total.append((values[c] << 8) | values[c+1]) return total #---------------------------------------------------------------------------# # Device Information Control #---------------------------------------------------------------------------# class ModbusDeviceIdentification(object): ''' This is used to supply the device identification for the readDeviceIdentification function For more information read section 6.21 of the modbus application protocol. ''' __data = { 0x00: '', # VendorName 0x01: '', # ProductCode 0x02: '', # MajorMinorRevision 0x03: '', # VendorUrl 0x04: '', # ProductName 0x05: '', # ModelName 0x06: '', # UserApplicationName 0x07: '', # reserved 0x08: '', # reserved # 0x80 -> 0xFF are private } __names = [ 'VendorName', 'ProductCode', 'MajorMinorRevision', 'VendorUrl', 'ProductName', 'ModelName', 'UserApplicationName', ] def __init__(self, info=None): ''' Initialize the datastore with the elements you need. (note acceptable range is [0x00-0x06,0x80-0xFF] inclusive) :param information: A dictionary of {int:string} of values ''' if isinstance(info, dict): for key in info: if (0x06 >= key >= 0x00) or (0x80 > key > 0x08): self.__data[key] = info[key] def __iter__(self): ''' Iterater over the device information :returns: An iterator of the device information ''' return self.__data.iteritems() def summary(self): ''' Return a summary of the main items :returns: An dictionary of the main items ''' return dict(zip(self.__names, self.__data.itervalues())) def update(self, value): ''' Update the values of this identity using another identify as the value :param value: The value to copy values from ''' self.__data.update(value) def __setitem__(self, key, value): ''' Wrapper used to access the device information :param key: The register to set :param value: The new value for referenced register ''' if key not in [0x07, 0x08]: self.__data[key] = value def __getitem__(self, key): ''' Wrapper used to access the device information :param key: The register to read ''' return self.__data.setdefault(key, '') def __str__(self): ''' Build a representation of the device :returns: A string representation of the device ''' return "DeviceIdentity" #-------------------------------------------------------------------------# # Properties #-------------------------------------------------------------------------# VendorName = dict_property(lambda s: s.__data, 0) ProductCode = dict_property(lambda s: s.__data, 1) MajorMinorRevision = dict_property(lambda s: s.__data, 2) VendorUrl = dict_property(lambda s: s.__data, 3) ProductName = dict_property(lambda s: s.__data, 4) ModelName = dict_property(lambda s: s.__data, 5) UserApplicationName = dict_property(lambda s: s.__data, 6) class DeviceInformationFactory(Singleton): ''' This is a helper factory that really just hides some of the complexity of processing the device information requests (function code 0x2b 0x0e). ''' __lookup = { DeviceInformation.Basic: lambda c,r,i: c.__gets(r, range(0x00, 0x03)), DeviceInformation.Regular: lambda c,r,i: c.__gets(r, range(0x00, 0x08)), DeviceInformation.Extended: lambda c,r,i: c.__gets(r, range(0x80, i)), DeviceInformation.Specific: lambda c,r,i: c.__get(r, i), } @classmethod def get(cls, control, read_code=DeviceInformation.Basic, object_id=0x00): ''' Get the requested device data from the system :param control: The control block to pull data from :param read_code: The read code to process :param object_id: The specific object_id to read :returns: The requested data (id, length, value) ''' identity = control.Identity return cls.__lookup[read_code](cls, identity, object_id) @classmethod def __get(cls, identity, object_id): ''' Read a single object_id from the device information :param identity: The identity block to pull data from :param object_id: The specific object id to read :returns: The requested data (id, length, value) ''' return { object_id:identity[object_id] } @classmethod def __gets(cls, identity, object_ids): ''' Read multiple object_ids from the device information :param identity: The identity block to pull data from :param object_ids: The specific object ids to read :returns: The requested data (id, length, value) ''' return dict((oid, identity[oid]) for oid in object_ids) #---------------------------------------------------------------------------# # Counters Handler #---------------------------------------------------------------------------# class ModbusCountersHandler(object): ''' This is a helper class to simplify the properties for the counters:: 0x0B 1 Return Bus Message Count Quantity of messages that the remote device has detected on the communications system since its last restart, clear counters operation, or power-up. Messages with bad CRC are not taken into account. 0x0C 2 Return Bus Communication Error Count Quantity of CRC errors encountered by the remote device since its last restart, clear counters operation, or power-up. In case of an error detected on the character level, (overrun, parity error), or in case of a message length < 3 bytes, the receiving device is not able to calculate the CRC. In such cases, this counter is also incremented. 0x0D 3 Return Slave Exception Error Count Quantity of MODBUS exception error detected by the remote device since its last restart, clear counters operation, or power-up. It comprises also the error detected in broadcast messages even if an exception message is not returned in this case. Exception errors are described and listed in "MODBUS Application Protocol Specification" document. 0xOE 4 Return Slave Message Count Quantity of messages addressed to the remote device, including broadcast messages, that the remote device has processed since its last restart, clear counters operation, or power-up. 0x0F 5 Return Slave No Response Count Quantity of messages received by the remote device for which it returned no response (neither a normal response nor an exception response), since its last restart, clear counters operation, or power-up. Then, this counter counts the number of broadcast messages it has received. 0x10 6 Return Slave NAK Count Quantity of messages addressed to the remote device for which it returned a Negative Acknowledge (NAK) exception response, since its last restart, clear counters operation, or power-up. Exception responses are described and listed in "MODBUS Application Protocol Specification" document. 0x11 7 Return Slave Busy Count Quantity of messages addressed to the remote device for which it returned a Slave Device Busy exception response, since its last restart, clear counters operation, or power-up. Exception responses are described and listed in "MODBUS Application Protocol Specification" document. 0x12 8 Return Bus Character Overrun Count Quantity of messages addressed to the remote device that it could not handle due to a character overrun condition, since its last restart, clear counters operation, or power-up. A character overrun is caused by data characters arriving at the port faster than they can. .. note:: I threw the event counter in here for convinience ''' __data = dict([(i, 0x0000) for i in range(9)]) __names = [ 'BusMessage', 'BusCommunicationError', 'SlaveExceptionError', 'SlaveMessage', 'SlaveNoResponse', 'SlaveNAK', 'SlaveBusy', 'BusCharacterOverrun' 'Event ' ] def __iter__(self): ''' Iterater over the device counters :returns: An iterator of the device counters ''' return izip(self.__names, self.__data.itervalues()) def update(self, values): ''' Update the values of this identity using another identify as the value :param values: The value to copy values from ''' for k, v in values.iteritems(): v += self.__getattribute__(k) self.__setattr__(k, v) def reset(self): ''' This clears all of the system counters ''' self.__data = dict([(i, 0x0000) for i in range(9)]) def summary(self): ''' Returns a summary of the counters current status :returns: A byte with each bit representing each counter ''' count, result = 0x01, 0x00 for i in self.__data.itervalues(): if i != 0x00: result |= count count <<= 1 return result #-------------------------------------------------------------------------# # Properties #-------------------------------------------------------------------------# BusMessage = dict_property(lambda s: s.__data, 0) BusCommunicationError = dict_property(lambda s: s.__data, 1) BusExceptionError = dict_property(lambda s: s.__data, 2) SlaveMessage = dict_property(lambda s: s.__data, 3) SlaveNoResponse = dict_property(lambda s: s.__data, 4) SlaveNAK = dict_property(lambda s: s.__data, 5) SlaveBusy = dict_property(lambda s: s.__data, 6) BusCharacterOverrun = dict_property(lambda s: s.__data, 7) Event = dict_property(lambda s: s.__data, 8) #---------------------------------------------------------------------------# # Main server controll block #---------------------------------------------------------------------------# class ModbusControlBlock(Singleton): ''' This is a global singleotn that controls all system information All activity should be logged here and all diagnostic requests should come from here. ''' __mode = 'ASCII' __diagnostic = [False] * 16 __instance = None __listen_only = False __delimiter = '\r' __counters = ModbusCountersHandler() __identity = ModbusDeviceIdentification() __plus = ModbusPlusStatistics() __events = [] #-------------------------------------------------------------------------# # Magic #-------------------------------------------------------------------------# def __str__(self): ''' Build a representation of the control block :returns: A string representation of the control block ''' return "ModbusControl" def __iter__(self): ''' Iterater over the device counters :returns: An iterator of the device counters ''' return self.__counters.__iter__() #-------------------------------------------------------------------------# # Events #-------------------------------------------------------------------------# def addEvent(self, event): ''' Adds a new event to the event log :param event: A new event to add to the log ''' self.__events.insert(0, event) self.__events = self.__events[0:64] # chomp to 64 entries self.Counter.Event += 1 def getEvents(self): ''' Returns an encoded collection of the event log. :returns: The encoded events packet ''' events = [event.encode() for event in self.__events] return ''.join(events) def clearEvents(self): ''' Clears the current list of events ''' self.__events = [] #-------------------------------------------------------------------------# # Other Properties #-------------------------------------------------------------------------# Identity = property(lambda s: s.__identity) Counter = property(lambda s: s.__counters) Events = property(lambda s: s.__events) Plus = property(lambda s: s.__plus) def reset(self): ''' This clears all of the system counters and the diagnostic register ''' self.__events = [] self.__counters.reset() self.__diagnostic = [False] * 16 #-------------------------------------------------------------------------# # Listen Properties #-------------------------------------------------------------------------# def _setListenOnly(self, value): ''' This toggles the listen only status :param value: The value to set the listen status to ''' self.__listen_only = bool(value) ListenOnly = property(lambda s: s.__listen_only, _setListenOnly) #-------------------------------------------------------------------------# # Mode Properties #-------------------------------------------------------------------------# def _setMode(self, mode): ''' This toggles the current serial mode :param mode: The data transfer method in (RTU, ASCII) ''' if mode in ['ASCII', 'RTU']: self.__mode = mode Mode = property(lambda s: s.__mode, _setMode) #-------------------------------------------------------------------------# # Delimiter Properties #-------------------------------------------------------------------------# def _setDelimiter(self, char): ''' This changes the serial delimiter character :param char: The new serial delimiter character ''' if isinstance(char, str): self.__delimiter = char elif isinstance(char, int): self.__delimiter = chr(char) Delimiter = property(lambda s: s.__delimiter, _setDelimiter) #-------------------------------------------------------------------------# # Diagnostic Properties #-------------------------------------------------------------------------# def setDiagnostic(self, mapping): ''' This sets the value in the diagnostic register :param mapping: Dictionary of key:value pairs to set ''' for entry in mapping.iteritems(): if entry[0] >= 0 and entry[0] < len(self.__diagnostic): self.__diagnostic[entry[0]] = (entry[1] != 0) def getDiagnostic(self, bit): ''' This gets the value in the diagnostic register :param bit: The bit to get :returns: The current value of the requested bit ''' if bit >= 0 and bit < len(self.__diagnostic): return self.__diagnostic[bit] return None def getDiagnosticRegister(self): ''' This gets the entire diagnostic register :returns: The diagnostic register collection ''' return self.__diagnostic #---------------------------------------------------------------------------# # Exported Identifiers #---------------------------------------------------------------------------# __all__ = [ "ModbusAccessControl", "ModbusPlusStatistics", "ModbusDeviceIdentification", "DeviceInformationFactory", "ModbusControlBlock" ] pymodbus/pymodbus/datastore/0000755000175500017550000000000012607272152016306 5ustar debacledebaclepymodbus/pymodbus/datastore/__init__.py0000644000175500017550000000103012607272152020411 0ustar debacledebaclefrom pymodbus.datastore.store import ModbusSequentialDataBlock from pymodbus.datastore.store import ModbusSparseDataBlock from pymodbus.datastore.context import ModbusSlaveContext from pymodbus.datastore.context import ModbusServerContext #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "ModbusSequentialDataBlock", "ModbusSparseDataBlock", "ModbusSlaveContext", "ModbusServerContext", ] pymodbus/pymodbus/datastore/store.py0000644000175500017550000002102512607272152020014 0ustar debacledebacle""" Modbus Server Datastore ------------------------- For each server, you will create a ModbusServerContext and pass in the default address space for each data access. The class will create and manage the data. Further modification of said data accesses should be performed with [get,set][access]Values(address, count) Datastore Implementation ------------------------- There are two ways that the server datastore can be implemented. The first is a complete range from 'address' start to 'count' number of indecies. This can be thought of as a straight array:: data = range(1, 1 + count) [1,2,3,...,count] The other way that the datastore can be implemented (and how many devices implement it) is a associate-array:: data = {1:'1', 3:'3', ..., count:'count'} [1,3,...,count] The difference between the two is that the latter will allow arbitrary gaps in its datastore while the former will not. This is seen quite commonly in some modbus implementations. What follows is a clear example from the field: Say a company makes two devices to monitor power usage on a rack. One works with three-phase and the other with a single phase. The company will dictate a modbus data mapping such that registers:: n: phase 1 power n+1: phase 2 power n+2: phase 3 power Using this, layout, the first device will implement n, n+1, and n+2, however, the second device may set the latter two values to 0 or will simply not implmented the registers thus causing a single read or a range read to fail. I have both methods implemented, and leave it up to the user to change based on their preference. """ from pymodbus.exceptions import NotImplementedException, ParameterException #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Datablock Storage #---------------------------------------------------------------------------# class BaseModbusDataBlock(object): ''' Base class for a modbus datastore Derived classes must create the following fields: @address The starting address point @defult_value The default value of the datastore @values The actual datastore values Derived classes must implemented the following methods: validate(self, address, count=1) getValues(self, address, count=1) setValues(self, address, values) ''' def default(self, count, value=False): ''' Used to initialize a store to one value :param count: The number of fields to set :param value: The default value to set to the fields ''' self.default_value = value self.values = [self.default_value] * count self.address = 0x00 def reset(self): ''' Resets the datastore to the initialized default value ''' self.values = [self.default_value] * len(self.values) def validate(self, address, count=1): ''' Checks to see if the request is in range :param address: The starting address :param count: The number of values to test for :returns: True if the request in within range, False otherwise ''' raise NotImplementedException("Datastore Address Check") def getValues(self, address, count=1): ''' Returns the requested values from the datastore :param address: The starting address :param count: The number of values to retrieve :returns: The requested values from a:a+c ''' raise NotImplementedException("Datastore Value Retrieve") def setValues(self, address, values): ''' Returns the requested values from the datastore :param address: The starting address :param values: The values to store ''' raise NotImplementedException("Datastore Value Retrieve") def __str__(self): ''' Build a representation of the datastore :returns: A string representation of the datastore ''' return "DataStore(%d, %d)" % (len(self.values), self.default_value) def __iter__(self): ''' Iterater over the data block data :returns: An iterator of the data block data ''' if isinstance(self.values, dict): return self.values.iteritems() return enumerate(self.values, self.address) class ModbusSequentialDataBlock(BaseModbusDataBlock): ''' Creates a sequential modbus datastore ''' def __init__(self, address, values): ''' Initializes the datastore :param address: The starting address of the datastore :param values: Either a list or a dictionary of values ''' self.address = address if hasattr(values, '__iter__'): self.values = list(values) else: self.values = [values] self.default_value = self.values[0].__class__() @classmethod def create(klass): ''' Factory method to create a datastore with the full address space initialized to 0x00 :returns: An initialized datastore ''' return klass(0x00, [0x00] * 65536) def validate(self, address, count=1): ''' Checks to see if the request is in range :param address: The starting address :param count: The number of values to test for :returns: True if the request in within range, False otherwise ''' result = (self.address <= address) result &= ((self.address + len(self.values)) >= (address + count)) return result def getValues(self, address, count=1): ''' Returns the requested values of the datastore :param address: The starting address :param count: The number of values to retrieve :returns: The requested values from a:a+c ''' start = address - self.address return self.values[start:start + count] def setValues(self, address, values): ''' Sets the requested values of the datastore :param address: The starting address :param values: The new values to be set ''' if not isinstance(values, list): values = [values] start = address - self.address self.values[start:start + len(values)] = values class ModbusSparseDataBlock(BaseModbusDataBlock): ''' Creates a sparse modbus datastore ''' def __init__(self, values): ''' Initializes the datastore Using the input values we create the default datastore value and the starting address :param values: Either a list or a dictionary of values ''' if isinstance(values, dict): self.values = values elif hasattr(values, '__iter__'): self.values = dict(enumerate(values)) else: raise ParameterException( "Values for datastore must be a list or dictionary") self.default_value = self.values.itervalues().next().__class__() self.address = self.values.iterkeys().next() @classmethod def create(klass): ''' Factory method to create a datastore with the full address space initialized to 0x00 :returns: An initialized datastore ''' return klass([0x00] * 65536) def validate(self, address, count=1): ''' Checks to see if the request is in range :param address: The starting address :param count: The number of values to test for :returns: True if the request in within range, False otherwise ''' if count == 0: return False handle = set(range(address, address + count)) return handle.issubset(set(self.values.iterkeys())) def getValues(self, address, count=1): ''' Returns the requested values of the datastore :param address: The starting address :param count: The number of values to retrieve :returns: The requested values from a:a+c ''' return [self.values[i] for i in range(address, address + count)] def setValues(self, address, values): ''' Sets the requested values of the datastore :param address: The starting address :param values: The new values to be set ''' if isinstance(values, dict): for idx, val in values.iteritems(): self.values[idx] = val else: if not isinstance(values, list): values = [values] for idx, val in enumerate(values): self.values[address + idx] = val pymodbus/pymodbus/datastore/context.py0000644000175500017550000001322012607272152020342 0ustar debacledebaclefrom pymodbus.exceptions import NoSuchSlaveException from pymodbus.interfaces import IModbusSlaveContext from pymodbus.datastore.store import ModbusSequentialDataBlock from pymodbus.constants import Defaults #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging; _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Slave Contexts #---------------------------------------------------------------------------# class ModbusSlaveContext(IModbusSlaveContext): ''' This creates a modbus data model with each data access stored in its own personal block ''' def __init__(self, *args, **kwargs): ''' Initializes the datastores, defaults to fully populated sequential data blocks if none are passed in. :param kwargs: Each element is a ModbusDataBlock 'di' - Discrete Inputs initializer 'co' - Coils initializer 'hr' - Holding Register initializer 'ir' - Input Registers iniatializer ''' self.store = {} self.store['d'] = kwargs.get('di', ModbusSequentialDataBlock.create()) self.store['c'] = kwargs.get('co', ModbusSequentialDataBlock.create()) self.store['i'] = kwargs.get('ir', ModbusSequentialDataBlock.create()) self.store['h'] = kwargs.get('hr', ModbusSequentialDataBlock.create()) self.zero_mode = kwargs.get('zero_mode', Defaults.ZeroMode) def __str__(self): ''' Returns a string representation of the context :returns: A string representation of the context ''' return "Modbus Slave Context" def reset(self): ''' Resets all the datastores to their default values ''' for datastore in self.store.itervalues(): datastore.reset() def validate(self, fx, address, count=1): ''' Validates the request to make sure it is in range :param fx: The function we are working with :param address: The starting address :param count: The number of values to test :returns: True if the request in within range, False otherwise ''' if not self.zero_mode: address = address + 1 _logger.debug("validate[%d] %d:%d" % (fx, address, count)) return self.store[self.decode(fx)].validate(address, count) def getValues(self, fx, address, count=1): ''' Validates the request to make sure it is in range :param fx: The function we are working with :param address: The starting address :param count: The number of values to retrieve :returns: The requested values from a:a+c ''' if not self.zero_mode: address = address + 1 _logger.debug("getValues[%d] %d:%d" % (fx, address, count)) return self.store[self.decode(fx)].getValues(address, count) def setValues(self, fx, address, values): ''' Sets the datastore with the supplied values :param fx: The function we are working with :param address: The starting address :param values: The new values to be set ''' if not self.zero_mode: address = address + 1 _logger.debug("setValues[%d] %d:%d" % (fx, address, len(values))) self.store[self.decode(fx)].setValues(address, values) class ModbusServerContext(object): ''' This represents a master collection of slave contexts. If single is set to true, it will be treated as a single context so every unit-id returns the same context. If single is set to false, it will be interpreted as a collection of slave contexts. ''' def __init__(self, slaves=None, single=True): ''' Initializes a new instance of a modbus server context. :param slaves: A dictionary of client contexts :param single: Set to true to treat this as a single context ''' self.single = single self.__slaves = slaves or {} if self.single: self.__slaves = {Defaults.UnitId: self.__slaves} def __iter__(self): ''' Iterater over the current collection of slave contexts. :returns: An iterator over the slave contexts ''' return self.__slaves.iteritems() def __contains__(self, slave): ''' Check if the given slave is in this list :param slave: slave The slave to check for existance :returns: True if the slave exists, False otherwise ''' return slave in self.__slaves def __setitem__(self, slave, context): ''' Used to set a new slave context :param slave: The slave context to set :param context: The new context to set for this slave ''' if self.single: slave = Defaults.UnitId if 0xf7 >= slave >= 0x00: self.__slaves[slave] = context else: raise NoSuchSlaveException('slave index[%d] out of range' % slave) def __delitem__(self, slave): ''' Wrapper used to access the slave context :param slave: The slave context to remove ''' if not self.single and (0xf7 >= slave >= 0x00): del self.__slaves[slave] else: raise NoSuchSlaveException('slave index[%d] out of range' % slave) def __getitem__(self, slave): ''' Used to get access to a slave context :param slave: The slave context to get :returns: The requested slave context ''' if self.single: slave = Defaults.UnitId if slave in self.__slaves: return self.__slaves.get(slave) else: raise NoSuchSlaveException('slave index[%d] out of range' % slave) pymodbus/pymodbus/datastore/remote.py0000644000175500017550000000736012607272152020161 0ustar debacledebaclefrom pymodbus.exceptions import NotImplementedException from pymodbus.interfaces import IModbusSlaveContext #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Context #---------------------------------------------------------------------------# class RemoteSlaveContext(IModbusSlaveContext): ''' TODO This creates a modbus data model that connects to a remote device (depending on the client used) ''' def __init__(self, client): ''' Initializes the datastores :param client: The client to retrieve values with ''' self._client = client self.__build_mapping() def reset(self): ''' Resets all the datastores to their default values ''' raise NotImplementedException() def validate(self, fx, address, count=1): ''' Validates the request to make sure it is in range :param fx: The function we are working with :param address: The starting address :param count: The number of values to test :returns: True if the request in within range, False otherwise ''' _logger.debug("validate[%d] %d:%d" % (fx, address, count)) result = self.__get_callbacks[self.decode(fx)](address, count) return result.function_code < 0x80 def getValues(self, fx, address, count=1): ''' Validates the request to make sure it is in range :param fx: The function we are working with :param address: The starting address :param count: The number of values to retrieve :returns: The requested values from a:a+c ''' # TODO deal with deferreds _logger.debug("get values[%d] %d:%d" % (fx, address, count)) result = self.__get_callbacks[self.decode(fx)](address, count) return self.__extract_result(self.decode(fx), result) def setValues(self, fx, address, values): ''' Sets the datastore with the supplied values :param fx: The function we are working with :param address: The starting address :param values: The new values to be set ''' # TODO deal with deferreds _logger.debug("set values[%d] %d:%d" % (fx, address, len(values))) self.__set_callbacks[self.decode(fx)](address, values) def __str__(self): ''' Returns a string representation of the context :returns: A string representation of the context ''' return "Remote Slave Context(%s)" % self._client def __build_mapping(self): ''' A quick helper method to build the function code mapper. ''' self.__get_callbacks = { 'd': lambda a, c: self._client.read_discrete_inputs(a, c), 'c': lambda a, c: self._client.read_coils(a, c), 'h': lambda a, c: self._client.read_holding_registers(a, c), 'i': lambda a, c: self._client.read_input_registers(a, c), } self.__set_callbacks = { 'd': lambda a, v: self._client.write_coils(a, v), 'c': lambda a, v: self._client.write_coils(a, v), 'h': lambda a, v: self._client.write_registers(a, v), 'i': lambda a, v: self._client.write_registers(a, v), } def __extract_result(self, fx, result): ''' A helper method to extract the values out of a response. TODO make this consistent (values?) ''' if result.function_code < 0x80: if fx in ['d', 'c']: return result.bits if fx in ['h', 'i']: return result.registers else: return result pymodbus/pymodbus/bit_read_message.py0000644000175500017550000002004212607272152020145 0ustar debacledebacle""" Bit Reading Request/Response messages -------------------------------------- """ import struct from pymodbus.pdu import ModbusRequest from pymodbus.pdu import ModbusResponse from pymodbus.pdu import ModbusExceptions as merror from pymodbus.utilities import pack_bitstring, unpack_bitstring class ReadBitsRequestBase(ModbusRequest): ''' Base class for Messages Requesting bit values ''' _rtu_frame_size = 8 def __init__(self, address, count, **kwargs): ''' Initializes the read request data :param address: The start address to read from :param count: The number of bits after 'address' to read ''' ModbusRequest.__init__(self, **kwargs) self.address = address self.count = count def encode(self): ''' Encodes a request pdu :returns: The encoded pdu ''' return struct.pack('>HH', self.address, self.count) def decode(self, data): ''' Decodes a request pdu :param data: The packet data to decode ''' self.address, self.count = struct.unpack('>HH', data) def __str__(self): ''' Returns a string representation of the instance :returns: A string representation of the instance ''' return "ReadBitRequest(%d,%d)" % (self.address, self.count) class ReadBitsResponseBase(ModbusResponse): ''' Base class for Messages responding to bit-reading values ''' _rtu_byte_count_pos = 2 def __init__(self, values, **kwargs): ''' Initializes a new instance :param values: The requested values to be returned ''' ModbusResponse.__init__(self, **kwargs) self.bits = values or [] def encode(self): ''' Encodes response pdu :returns: The encoded packet message ''' result = pack_bitstring(self.bits) packet = struct.pack(">B", len(result)) + result return packet def decode(self, data): ''' Decodes response pdu :param data: The packet data to decode ''' self.byte_count = struct.unpack(">B", data[0])[0] self.bits = unpack_bitstring(data[1:]) def setBit(self, address, value=1): ''' Helper function to set the specified bit :param address: The bit to set :param value: The value to set the bit to ''' self.bits[address] = (value != 0) def resetBit(self, address): ''' Helper function to set the specified bit to 0 :param address: The bit to reset ''' self.setBit(address, 0) def getBit(self, address): ''' Helper function to get the specified bit's value :param address: The bit to query :returns: The value of the requested bit ''' return self.bits[address] def __str__(self): ''' Returns a string representation of the instance :returns: A string representation of the instance ''' return "ReadBitResponse(%d)" % len(self.bits) class ReadCoilsRequest(ReadBitsRequestBase): ''' This function code is used to read from 1 to 2000(0x7d0) contiguous status of coils in a remote device. The Request PDU specifies the starting address, ie the address of the first coil specified, and the number of coils. In the PDU Coils are addressed starting at zero. Therefore coils numbered 1-16 are addressed as 0-15. ''' function_code = 1 def __init__(self, address=None, count=None, **kwargs): ''' Initializes a new instance :param address: The address to start reading from :param count: The number of bits to read ''' ReadBitsRequestBase.__init__(self, address, count, **kwargs) def execute(self, context): ''' Run a read coils request against a datastore Before running the request, we make sure that the request is in the max valid range (0x001-0x7d0). Next we make sure that the request is valid against the current datastore. :param context: The datastore to request from :returns: The initializes response message, exception message otherwise ''' if not (1 <= self.count <= 0x7d0): return self.doException(merror.IllegalValue) if not context.validate(self.function_code, self.address, self.count): return self.doException(merror.IllegalAddress) values = context.getValues(self.function_code, self.address, self.count) return ReadCoilsResponse(values) class ReadCoilsResponse(ReadBitsResponseBase): ''' The coils in the response message are packed as one coil per bit of the data field. Status is indicated as 1= ON and 0= OFF. The LSB of the first data byte contains the output addressed in the query. The other coils follow toward the high order end of this byte, and from low order to high order in subsequent bytes. If the returned output quantity is not a multiple of eight, the remaining bits in the final data byte will be padded with zeros (toward the high order end of the byte). The Byte Count field specifies the quantity of complete bytes of data. ''' function_code = 1 def __init__(self, values=None, **kwargs): ''' Intializes a new instance :param values: The request values to respond with ''' ReadBitsResponseBase.__init__(self, values, **kwargs) class ReadDiscreteInputsRequest(ReadBitsRequestBase): ''' This function code is used to read from 1 to 2000(0x7d0) contiguous status of discrete inputs in a remote device. The Request PDU specifies the starting address, ie the address of the first input specified, and the number of inputs. In the PDU Discrete Inputs are addressed starting at zero. Therefore Discrete inputs numbered 1-16 are addressed as 0-15. ''' function_code = 2 def __init__(self, address=None, count=None, **kwargs): ''' Intializes a new instance :param address: The address to start reading from :param count: The number of bits to read ''' ReadBitsRequestBase.__init__(self, address, count, **kwargs) def execute(self, context): ''' Run a read discrete input request against a datastore Before running the request, we make sure that the request is in the max valid range (0x001-0x7d0). Next we make sure that the request is valid against the current datastore. :param context: The datastore to request from :returns: The initializes response message, exception message otherwise ''' if not (1 <= self.count <= 0x7d0): return self.doException(merror.IllegalValue) if not context.validate(self.function_code, self.address, self.count): return self.doException(merror.IllegalAddress) values = context.getValues(self.function_code, self.address, self.count) return ReadDiscreteInputsResponse(values) class ReadDiscreteInputsResponse(ReadBitsResponseBase): ''' The discrete inputs in the response message are packed as one input per bit of the data field. Status is indicated as 1= ON; 0= OFF. The LSB of the first data byte contains the input addressed in the query. The other inputs follow toward the high order end of this byte, and from low order to high order in subsequent bytes. If the returned input quantity is not a multiple of eight, the remaining bits in the final data byte will be padded with zeros (toward the high order end of the byte). The Byte Count field specifies the quantity of complete bytes of data. ''' function_code = 2 def __init__(self, values=None, **kwargs): ''' Intializes a new instance :param values: The request values to respond with ''' ReadBitsResponseBase.__init__(self, values, **kwargs) #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "ReadCoilsRequest", "ReadCoilsResponse", "ReadDiscreteInputsRequest", "ReadDiscreteInputsResponse", ] pymodbus/pymodbus/register_write_message.py0000644000175500017550000001744112607272152021443 0ustar debacledebacle''' Register Writing Request/Response Messages ------------------------------------------- ''' import struct from pymodbus.pdu import ModbusRequest from pymodbus.pdu import ModbusResponse from pymodbus.pdu import ModbusExceptions as merror class WriteSingleRegisterRequest(ModbusRequest): ''' This function code is used to write a single holding register in a remote device. The Request PDU specifies the address of the register to be written. Registers are addressed starting at zero. Therefore register numbered 1 is addressed as 0. ''' function_code = 6 _rtu_frame_size = 8 def __init__(self, address=None, value=None, **kwargs): ''' Initializes a new instance :param address: The address to start writing add :param value: The values to write ''' ModbusRequest.__init__(self, **kwargs) self.address = address self.value = value def encode(self): ''' Encode a write single register packet packet request :returns: The encoded packet ''' if self.skip_encode: return self.value return struct.pack('>HH', self.address, self.value) def decode(self, data): ''' Decode a write single register packet packet request :param data: The request to decode ''' self.address, self.value = struct.unpack('>HH', data) def execute(self, context): ''' Run a write single register request against a datastore :param context: The datastore to request from :returns: An initialized response, exception message otherwise ''' if not (0 <= self.value <= 0xffff): return self.doException(merror.IllegalValue) if not context.validate(self.function_code, self.address, 1): return self.doException(merror.IllegalAddress) context.setValues(self.function_code, self.address, [self.value]) values = context.getValues(self.function_code, self.address, 1) return WriteSingleRegisterResponse(self.address, values[0]) def __str__(self): ''' Returns a string representation of the instance :returns: A string representation of the instance ''' return "WriteRegisterRequest %d => %d" % (self.address, self.value) class WriteSingleRegisterResponse(ModbusResponse): ''' The normal response is an echo of the request, returned after the register contents have been written. ''' function_code = 6 _rtu_frame_size = 8 def __init__(self, address=None, value=None, **kwargs): ''' Initializes a new instance :param address: The address to start writing add :param value: The values to write ''' ModbusResponse.__init__(self, **kwargs) self.address = address self.value = value def encode(self): ''' Encode a write single register packet packet request :returns: The encoded packet ''' return struct.pack('>HH', self.address, self.value) def decode(self, data): ''' Decode a write single register packet packet request :param data: The request to decode ''' self.address, self.value = struct.unpack('>HH', data) def __str__(self): ''' Returns a string representation of the instance :returns: A string representation of the instance ''' params = (self.address, self.value) return "WriteRegisterResponse %d => %d" % params #---------------------------------------------------------------------------# # Write Multiple Registers #---------------------------------------------------------------------------# class WriteMultipleRegistersRequest(ModbusRequest): ''' This function code is used to write a block of contiguous registers (1 to approx. 120 registers) in a remote device. The requested written values are specified in the request data field. Data is packed as two bytes per register. ''' function_code = 16 _rtu_byte_count_pos = 6 def __init__(self, address=None, values=None, **kwargs): ''' Initializes a new instance :param address: The address to start writing to :param values: The values to write ''' ModbusRequest.__init__(self, **kwargs) self.address = address if values is None: values = [] elif not hasattr(values, '__iter__'): values = [values] self.values = values self.count = len(self.values) self.byte_count = self.count * 2 def encode(self): ''' Encode a write single register packet packet request :returns: The encoded packet ''' packet = struct.pack('>HHB', self.address, self.count, self.byte_count) if self.skip_encode: return packet + ''.join(self.values) for value in self.values: packet += struct.pack('>H', value) return packet def decode(self, data): ''' Decode a write single register packet packet request :param data: The request to decode ''' self.address, self.count, \ self.byte_count = struct.unpack('>HHB', data[:5]) self.values = [] # reset for idx in range(5, (self.count * 2) + 5, 2): self.values.append(struct.unpack('>H', data[idx:idx + 2])[0]) def execute(self, context): ''' Run a write single register request against a datastore :param context: The datastore to request from :returns: An initialized response, exception message otherwise ''' if not (1 <= self.count <= 0x07b): return self.doException(merror.IllegalValue) if (self.byte_count != self.count * 2): return self.doException(merror.IllegalValue) if not context.validate(self.function_code, self.address, self.count): return self.doException(merror.IllegalAddress) context.setValues(self.function_code, self.address, self.values) return WriteMultipleRegistersResponse(self.address, self.count) def __str__(self): ''' Returns a string representation of the instance :returns: A string representation of the instance ''' params = (self.address, self.count) return "WriteMultipleRegisterRequest %d => %d" % params class WriteMultipleRegistersResponse(ModbusResponse): ''' "The normal response returns the function code, starting address, and quantity of registers written. ''' function_code = 16 _rtu_frame_size = 8 def __init__(self, address=None, count=None, **kwargs): ''' Initializes a new instance :param address: The address to start writing to :param count: The number of registers to write to ''' ModbusResponse.__init__(self, **kwargs) self.address = address self.count = count def encode(self): ''' Encode a write single register packet packet request :returns: The encoded packet ''' return struct.pack('>HH', self.address, self.count) def decode(self, data): ''' Decode a write single register packet packet request :param data: The request to decode ''' self.address, self.count = struct.unpack('>HH', data) def __str__(self): ''' Returns a string representation of the instance :returns: A string representation of the instance ''' params = (self.address, self.count) return "WriteMultipleRegisterResponse (%d,%d)" % params #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "WriteSingleRegisterRequest", "WriteSingleRegisterResponse", "WriteMultipleRegistersRequest", "WriteMultipleRegistersResponse", ] pymodbus/pymodbus/internal/0000755000175500017550000000000012607272152016134 5ustar debacledebaclepymodbus/pymodbus/internal/__init__.py0000644000175500017550000000000012607272152020233 0ustar debacledebaclepymodbus/pymodbus/internal/ptwisted.py0000644000175500017550000000244312607272152020354 0ustar debacledebacle''' A collection of twisted utility code ''' from twisted.cred import portal, checkers from twisted.conch import manhole, manhole_ssh from twisted.conch.insults import insults #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Twisted Helper Methods #---------------------------------------------------------------------------# def InstallManagementConsole(namespace, users={'admin': 'admin'}, port=503): ''' Helper method to start an ssh management console for the modbus server. :param namespace: The data to constrain the server to :param users: The users to login with :param port: The port to host the server on ''' from twisted.internet import reactor def build_protocol(): p = insults.ServerProtocol(manhole.ColoredManhole, namespace) return p r = manhole_ssh.TerminalRealm() r.chainedProtocolFactory = build_protocol c = checkers.InMemoryUsernamePasswordDatabaseDontUse(**users) p = portal.Portal(r, [c]) factory = manhole_ssh.ConchFactory(p) reactor.listenTCP(port, factory) pymodbus/pymodbus/other_message.py0000644000175500017550000003433412607272152017526 0ustar debacledebacle''' Diagnostic record read/write Currently not all implemented ''' import struct from pymodbus.constants import ModbusStatus from pymodbus.pdu import ModbusRequest from pymodbus.pdu import ModbusResponse from pymodbus.device import ModbusControlBlock _MCB = ModbusControlBlock() #---------------------------------------------------------------------------# # TODO Make these only work on serial #---------------------------------------------------------------------------# class ReadExceptionStatusRequest(ModbusRequest): ''' This function code is used to read the contents of eight Exception Status outputs in a remote device. The function provides a simple method for accessing this information, because the Exception Output references are known (no output reference is needed in the function). ''' function_code = 0x07 _rtu_frame_size = 4 def __init__(self, **kwargs): ''' Initializes a new instance ''' ModbusRequest.__init__(self, **kwargs) def encode(self): ''' Encodes the message ''' return '' def decode(self, data): ''' Decodes data part of the message. :param data: The incoming data ''' pass def execute(self, context): ''' Run a read exeception status request against the store :returns: The populated response ''' status = _MCB.Counter.summary() return ReadExceptionStatusResponse(status) def __str__(self): ''' Builds a representation of the request :returns: The string representation of the request ''' return "ReadExceptionStatusRequest(%d)" % (self.function_code) class ReadExceptionStatusResponse(ModbusResponse): ''' The normal response contains the status of the eight Exception Status outputs. The outputs are packed into one data byte, with one bit per output. The status of the lowest output reference is contained in the least significant bit of the byte. The contents of the eight Exception Status outputs are device specific. ''' function_code = 0x07 _rtu_frame_size = 5 def __init__(self, status=0x00, **kwargs): ''' Initializes a new instance :param status: The status response to report ''' ModbusResponse.__init__(self, **kwargs) self.status = status def encode(self): ''' Encodes the response :returns: The byte encoded message ''' return struct.pack('>B', self.status) def decode(self, data): ''' Decodes a the response :param data: The packet data to decode ''' self.status = struct.unpack('>B', data)[0] def __str__(self): ''' Builds a representation of the response :returns: The string representation of the response ''' arguments = (self.function_code, self.status) return "ReadExceptionStatusResponse(%d, %s)" % arguments # Encapsulate interface transport 43, 14 # CANopen general reference 43, 13 #---------------------------------------------------------------------------# # TODO Make these only work on serial #---------------------------------------------------------------------------# class GetCommEventCounterRequest(ModbusRequest): ''' This function code is used to get a status word and an event count from the remote device's communication event counter. By fetching the current count before and after a series of messages, a client can determine whether the messages were handled normally by the remote device. The device's event counter is incremented once for each successful message completion. It is not incremented for exception responses, poll commands, or fetch event counter commands. The event counter can be reset by means of the Diagnostics function (code 08), with a subfunction of Restart Communications Option (code 00 01) or Clear Counters and Diagnostic Register (code 00 0A). ''' function_code = 0x0b _rtu_frame_size = 4 def __init__(self, **kwargs): ''' Initializes a new instance ''' ModbusRequest.__init__(self, **kwargs) def encode(self): ''' Encodes the message ''' return '' def decode(self, data): ''' Decodes data part of the message. :param data: The incoming data ''' pass def execute(self, context): ''' Run a read exeception status request against the store :returns: The populated response ''' status = _MCB.Counter.Event return GetCommEventCounterResponse(status) def __str__(self): ''' Builds a representation of the request :returns: The string representation of the request ''' return "GetCommEventCounterRequest(%d)" % (self.function_code) class GetCommEventCounterResponse(ModbusResponse): ''' The normal response contains a two-byte status word, and a two-byte event count. The status word will be all ones (FF FF hex) if a previously-issued program command is still being processed by the remote device (a busy condition exists). Otherwise, the status word will be all zeros. ''' function_code = 0x0b _rtu_frame_size = 8 def __init__(self, count=0x0000, **kwargs): ''' Initializes a new instance :param count: The current event counter value ''' ModbusResponse.__init__(self, **kwargs) self.count = count self.status = True # this means we are ready, not waiting def encode(self): ''' Encodes the response :returns: The byte encoded message ''' if self.status: ready = ModbusStatus.Ready else: ready = ModbusStatus.Waiting return struct.pack('>HH', ready, self.count) def decode(self, data): ''' Decodes a the response :param data: The packet data to decode ''' ready, self.count = struct.unpack('>HH', data) self.status = (ready == ModbusStatus.Ready) def __str__(self): ''' Builds a representation of the response :returns: The string representation of the response ''' arguments = (self.function_code, self.count, self.status) return "GetCommEventCounterResponse(%d, %d, %d)" % arguments #---------------------------------------------------------------------------# # TODO Make these only work on serial #---------------------------------------------------------------------------# class GetCommEventLogRequest(ModbusRequest): ''' This function code is used to get a status word, event count, message count, and a field of event bytes from the remote device. The status word and event counts are identical to that returned by the Get Communications Event Counter function (11, 0B hex). The message counter contains the quantity of messages processed by the remote device since its last restart, clear counters operation, or power-up. This count is identical to that returned by the Diagnostic function (code 08), sub-function Return Bus Message Count (code 11, 0B hex). The event bytes field contains 0-64 bytes, with each byte corresponding to the status of one MODBUS send or receive operation for the remote device. The remote device enters the events into the field in chronological order. Byte 0 is the most recent event. Each new byte flushes the oldest byte from the field. ''' function_code = 0x0c _rtu_frame_size = 4 def __init__(self, **kwargs): ''' Initializes a new instance ''' ModbusRequest.__init__(self, **kwargs) def encode(self): ''' Encodes the message ''' return '' def decode(self, data): ''' Decodes data part of the message. :param data: The incoming data ''' pass def execute(self, context): ''' Run a read exeception status request against the store :returns: The populated response ''' results = { 'status' : True, 'message_count' : _MCB.Counter.BusMessage, 'event_count' : _MCB.Counter.Event, 'events' : _MCB.getEvents(), } return GetCommEventLogResponse(**results) def __str__(self): ''' Builds a representation of the request :returns: The string representation of the request ''' return "GetCommEventLogRequest(%d)" % self.function_code class GetCommEventLogResponse(ModbusResponse): ''' The normal response contains a two-byte status word field, a two-byte event count field, a two-byte message count field, and a field containing 0-64 bytes of events. A byte count field defines the total length of the data in these four field ''' function_code = 0x0c _rtu_byte_count_pos = 3 def __init__(self, **kwargs): ''' Initializes a new instance :param status: The status response to report :param message_count: The current message count :param event_count: The current event count :param events: The collection of events to send ''' ModbusResponse.__init__(self, **kwargs) self.status = kwargs.get('status', True) self.message_count = kwargs.get('message_count', 0) self.event_count = kwargs.get('event_count', 0) self.events = kwargs.get('events', []) def encode(self): ''' Encodes the response :returns: The byte encoded message ''' if self.status: ready = ModbusStatus.Ready else: ready = ModbusStatus.Waiting packet = struct.pack('>B', 6 + len(self.events)) packet += struct.pack('>H', ready) packet += struct.pack('>HH', self.event_count, self.message_count) packet += ''.join(struct.pack('>B', e) for e in self.events) return packet def decode(self, data): ''' Decodes a the response :param data: The packet data to decode ''' length = struct.unpack('>B', data[0])[0] status = struct.unpack('>H', data[1:3])[0] self.status = (status == ModbusStatus.Ready) self.event_count = struct.unpack('>H', data[3:5])[0] self.message_count = struct.unpack('>H', data[5:7])[0] self.events = [] for e in xrange(7, length + 1): self.events.append(struct.unpack('>B', data[e])[0]) def __str__(self): ''' Builds a representation of the response :returns: The string representation of the response ''' arguments = (self.function_code, self.status, self.message_count, self.event_count) return "GetCommEventLogResponse(%d, %d, %d, %d)" % arguments #---------------------------------------------------------------------------# # TODO Make these only work on serial #---------------------------------------------------------------------------# class ReportSlaveIdRequest(ModbusRequest): ''' This function code is used to read the description of the type, the current status, and other information specific to a remote device. ''' function_code = 0x11 _rtu_frame_size = 4 def __init__(self, **kwargs): ''' Initializes a new instance ''' ModbusRequest.__init__(self, **kwargs) def encode(self): ''' Encodes the message ''' return '' def decode(self, data): ''' Decodes data part of the message. :param data: The incoming data ''' pass def execute(self, context): ''' Run a read exeception status request against the store :returns: The populated response ''' identifier = '\x70\x79\x6d\x6f\x64\x62\x75\x73' return ReportSlaveIdResponse(identifier) def __str__(self): ''' Builds a representation of the request :returns: The string representation of the request ''' return "ResportSlaveIdRequest(%d)" % self.function_code class ReportSlaveIdResponse(ModbusResponse): ''' The format of a normal response is shown in the following example. The data contents are specific to each type of device. ''' function_code = 0x11 _rtu_byte_count_pos = 2 def __init__(self, identifier='\x00', status=True, **kwargs): ''' Initializes a new instance :param identifier: The identifier of the slave :param status: The status response to report ''' ModbusResponse.__init__(self, **kwargs) self.identifier = identifier self.status = status def encode(self): ''' Encodes the response :returns: The byte encoded message ''' if self.status: status = ModbusStatus.SlaveOn else: status = ModbusStatus.SlaveOff length = len(self.identifier) + 2 packet = struct.pack('>B', length) packet += self.identifier # we assume it is already encoded packet += struct.pack('>B', status) return packet def decode(self, data): ''' Decodes a the response Since the identifier is device dependent, we just return the raw value that a user can decode to whatever it should be. :param data: The packet data to decode ''' length = struct.unpack('>B', data[0])[0] self.identifier = data[1:length + 1] status = struct.unpack('>B', data[-1])[0] self.status = status == ModbusStatus.SlaveOn def __str__(self): ''' Builds a representation of the response :returns: The string representation of the response ''' arguments = (self.function_code, self.identifier, self.status) return "ResportSlaveIdResponse(%d, %d, %d)" % arguments #---------------------------------------------------------------------------# # TODO Make these only work on serial #---------------------------------------------------------------------------# # report device identification 43, 14 #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "ReadExceptionStatusRequest", "ReadExceptionStatusResponse", "GetCommEventCounterRequest", "GetCommEventCounterResponse", "GetCommEventLogRequest", "GetCommEventLogResponse", "ReportSlaveIdRequest", "ReportSlaveIdResponse", ] pymodbus/pymodbus/constants.py0000644000175500017550000001471112607272152016712 0ustar debacledebacle''' Constants For Modbus Server/Client ---------------------------------- This is the single location for storing default values for the servers and clients. ''' from pymodbus.interfaces import Singleton class Defaults(Singleton): ''' A collection of modbus default values .. attribute:: Port The default modbus tcp server port (502) .. attribute:: Retries The default number of times a client should retry the given request before failing (3) .. attribute:: RetryOnEmpty A flag indicating if a transaction should be retried in the case that an empty response is received. This is useful for slow clients that may need more time to process a requst. .. attribute:: Timeout The default amount of time a client should wait for a request to be processed (3 seconds) .. attribute:: Reconnects The default number of times a client should attempt to reconnect before deciding the server is down (0) .. attribute:: TransactionId The starting transaction identifier number (0) .. attribute:: ProtocolId The modbus protocol id. Currently this is set to 0 in all but proprietary implementations. .. attribute:: UnitId The modbus slave addrss. Currently this is set to 0x00 which means this request should be broadcast to all the slave devices (really means that all the devices should respons). .. attribute:: Baudrate The speed at which the data is transmitted over the serial line. This defaults to 19200. .. attribute:: Parity The type of checksum to use to verify data integrity. This can be on of the following:: - (E)ven - 1 0 1 0 | P(0) - (O)dd - 1 0 1 0 | P(1) - (N)one - 1 0 1 0 | no parity This defaults to (N)one. .. attribute:: Bytesize The number of bits in a byte of serial data. This can be one of 5, 6, 7, or 8. This defaults to 8. .. attribute:: Stopbits The number of bits sent after each character in a message to indicate the end of the byte. This defaults to 1. .. attribute:: ZeroMode Indicates if the slave datastore should use indexing at 0 or 1. More 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 IgnoreMissingSlaves = False class ModbusStatus(Singleton): ''' These represent various status codes in the modbus protocol. .. attribute:: Waiting This indicates that a modbus device is currently waiting for a given request to finish some running task. .. attribute:: Ready This indicates that a modbus device is currently free to perform the next request task. .. attribute:: On This indicates that the given modbus entity is on .. attribute:: Off This indicates that the given modbus entity is off .. attribute:: SlaveOn This indicates that the given modbus slave is running .. attribute:: SlaveOff This indicates that the given modbus slave is not running ''' Waiting = 0xffff Ready = 0x0000 On = 0xff00 Off = 0x0000 SlaveOn = 0xff SlaveOff = 0x00 class Endian(Singleton): ''' An enumeration representing the various byte endianess. .. attribute:: Auto This indicates that the byte order is chosen by the current native environment. .. attribute:: Big This indicates that the bytes are in little endian format .. attribute:: Little This indicates that the bytes are in big endian format .. note:: I am simply borrowing the format strings from the python struct module for my convenience. ''' Auto = '@' Big = '>' Little = '<' class ModbusPlusOperation(Singleton): ''' Represents the type of modbus plus request .. attribute:: GetStatistics Operation requesting that the current modbus plus statistics be returned in the response. .. attribute:: ClearStatistics Operation requesting that the current modbus plus statistics be cleared and not returned in the response. ''' GetStatistics = 0x0003 ClearStatistics = 0x0004 class DeviceInformation(Singleton): ''' Represents what type of device information to read .. attribute:: Basic This is the basic (required) device information to be returned. This includes VendorName, ProductCode, and MajorMinorRevision code. .. attribute:: Regular In addition to basic data objects, the device provides additional and optinoal identification and description data objects. All of the objects of this category are defined in the standard but their implementation is optional. .. attribute:: Extended In addition to regular data objects, the device provides additional and optional identification and description private data about the physical device itself. All of these data are device dependent. .. attribute:: Specific Request to return a single data object. ''' Basic = 0x01 Regular = 0x02 Extended = 0x03 Specific = 0x04 class MoreData(Singleton): ''' Represents the more follows condition .. attribute:: Nothing This indiates that no more objects are going to be returned. .. attribute:: KeepReading This indicates that there are more objects to be returned. ''' Nothing = 0x00 KeepReading = 0xFF #---------------------------------------------------------------------------# # Exported Identifiers #---------------------------------------------------------------------------# __all__ = [ "Defaults", "ModbusStatus", "Endian", "ModbusPlusOperation", "DeviceInformation", "MoreData", ] pymodbus/pymodbus/payload.py0000644000175500017550000002466112607272152016334 0ustar debacledebacle''' Modbus Payload Builders ------------------------ A collection of utilities for building and decoding modbus messages payloads. ''' from struct import pack, unpack from pymodbus.interfaces import IPayloadBuilder from pymodbus.constants import Endian from pymodbus.utilities import pack_bitstring from pymodbus.utilities import unpack_bitstring from pymodbus.exceptions import ParameterException class BinaryPayloadBuilder(IPayloadBuilder): ''' A utility that helps build payload messages to be written with the various modbus messages. It really is just a simple wrapper around the struct module, however it saves time looking up the format strings. What follows is a simple example:: builder = BinaryPayloadBuilder(endian=Endian.Little) builder.add_8bit_uint(1) builder.add_16bit_uint(2) payload = builder.build() ''' def __init__(self, payload=None, endian=Endian.Little): ''' Initialize a new instance of the payload builder :param payload: Raw payload data to initialize with :param endian: The endianess of the payload ''' self._payload = payload or [] self._endian = endian def __str__(self): ''' Return the payload buffer as a string :returns: The payload buffer as a string ''' return ''.join(self._payload) def reset(self): ''' Reset the payload buffer ''' self._payload = [] def build(self): ''' Return the payload buffer as a list This list is two bytes per element and can thus be treated as a list of registers. :returns: The payload buffer as a list ''' string = str(self) length = len(string) string = string + ('\x00' * (length % 2)) return [string[i:i+2] for i in xrange(0, length, 2)] def add_bits(self, values): ''' Adds a collection of bits to be encoded If these are less than a multiple of eight, they will be left padded with 0 bits to make it so. :param value: The value to add to the buffer ''' value = pack_bitstring(values) self._payload.append(value) def add_8bit_uint(self, value): ''' Adds a 8 bit unsigned int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'B' self._payload.append(pack(fstring, value)) def add_16bit_uint(self, value): ''' Adds a 16 bit unsigned int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'H' self._payload.append(pack(fstring, value)) def add_32bit_uint(self, value): ''' Adds a 32 bit unsigned int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'I' self._payload.append(pack(fstring, value)) def add_64bit_uint(self, value): ''' Adds a 64 bit unsigned int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'Q' self._payload.append(pack(fstring, value)) def add_8bit_int(self, value): ''' Adds a 8 bit signed int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'b' self._payload.append(pack(fstring, value)) def add_16bit_int(self, value): ''' Adds a 16 bit signed int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'h' self._payload.append(pack(fstring, value)) def add_32bit_int(self, value): ''' Adds a 32 bit signed int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'i' self._payload.append(pack(fstring, value)) def add_64bit_int(self, value): ''' Adds a 64 bit signed int to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'q' self._payload.append(pack(fstring, value)) def add_32bit_float(self, value): ''' Adds a 32 bit float to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'f' self._payload.append(pack(fstring, value)) def add_64bit_float(self, value): ''' Adds a 64 bit float(double) to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 'd' self._payload.append(pack(fstring, value)) def add_string(self, value): ''' Adds a string to the buffer :param value: The value to add to the buffer ''' fstring = self._endian + 's' for c in value: self._payload.append(pack(fstring, c)) class BinaryPayloadDecoder(object): ''' A utility that helps decode payload messages from a modbus reponse message. It really is just a simple wrapper around the struct module, however it saves time looking up the format strings. What follows is a simple example:: decoder = BinaryPayloadDecoder(payload) first = decoder.decode_8bit_uint() second = decoder.decode_16bit_uint() ''' def __init__(self, payload, endian=Endian.Little): ''' Initialize a new payload decoder :param payload: The payload to decode with :param endian: The endianess of the payload ''' self._payload = payload self._pointer = 0x00 self._endian = endian @classmethod def fromRegisters(klass, registers, endian=Endian.Little): ''' Initialize a payload decoder with the result of reading a collection of registers from a modbus device. The registers are treated as a list of 2 byte values. We have to do this because of how the data has already been decoded by the rest of the library. :param registers: The register results to initialize with :param endian: The endianess of the payload :returns: An initialized PayloadDecoder ''' if isinstance(registers, list): # repack into flat binary payload = ''.join(pack(endian + 'H', x) for x in registers) return klass(payload, endian) raise ParameterException('Invalid collection of registers supplied') @classmethod def fromCoils(klass, coils, endian=Endian.Little): ''' Initialize a payload decoder with the result of reading a collection of coils from a modbus device. The coils are treated as a list of bit(boolean) values. :param coils: The coil results to initialize with :param endian: The endianess of the payload :returns: An initialized PayloadDecoder ''' if isinstance(coils, list): payload = pack_bitstring(coils) return klass(payload, endian) raise ParameterException('Invalid collection of coils supplied') def reset(self): ''' Reset the decoder pointer back to the start ''' self._pointer = 0x00 def decode_8bit_uint(self): ''' Decodes a 8 bit unsigned int from the buffer ''' self._pointer += 1 fstring = self._endian + 'B' handle = self._payload[self._pointer - 1:self._pointer] return unpack(fstring, handle)[0] def decode_bits(self): ''' Decodes a byte worth of bits from the buffer ''' self._pointer += 1 fstring = self._endian + 'B' handle = self._payload[self._pointer - 1:self._pointer] return unpack_bitstring(handle) def decode_16bit_uint(self): ''' Decodes a 16 bit unsigned int from the buffer ''' self._pointer += 2 fstring = self._endian + 'H' handle = self._payload[self._pointer - 2:self._pointer] return unpack(fstring, handle)[0] def decode_32bit_uint(self): ''' Decodes a 32 bit unsigned int from the buffer ''' self._pointer += 4 fstring = self._endian + 'I' handle = self._payload[self._pointer - 4:self._pointer] return unpack(fstring, handle)[0] def decode_64bit_uint(self): ''' Decodes a 64 bit unsigned int from the buffer ''' self._pointer += 8 fstring = self._endian + 'Q' handle = self._payload[self._pointer - 8:self._pointer] return unpack(fstring, handle)[0] def decode_8bit_int(self): ''' Decodes a 8 bit signed int from the buffer ''' self._pointer += 1 fstring = self._endian + 'b' handle = self._payload[self._pointer - 1:self._pointer] return unpack(fstring, handle)[0] def decode_16bit_int(self): ''' Decodes a 16 bit signed int from the buffer ''' self._pointer += 2 fstring = self._endian + 'h' handle = self._payload[self._pointer - 2:self._pointer] return unpack(fstring, handle)[0] def decode_32bit_int(self): ''' Decodes a 32 bit signed int from the buffer ''' self._pointer += 4 fstring = self._endian + 'i' handle = self._payload[self._pointer - 4:self._pointer] return unpack(fstring, handle)[0] def decode_64bit_int(self): ''' Decodes a 64 bit signed int from the buffer ''' self._pointer += 8 fstring = self._endian + 'q' handle = self._payload[self._pointer - 8:self._pointer] return unpack(fstring, handle)[0] def decode_32bit_float(self): ''' Decodes a 32 bit float from the buffer ''' self._pointer += 4 fstring = self._endian + 'f' handle = self._payload[self._pointer - 4:self._pointer] return unpack(fstring, handle)[0] def decode_64bit_float(self): ''' Decodes a 64 bit float(double) from the buffer ''' self._pointer += 8 fstring = self._endian + 'd' handle = self._payload[self._pointer - 8:self._pointer] return unpack(fstring, handle)[0] def decode_string(self, size=1): ''' Decodes a string from the buffer :param size: The size of the string to decode ''' self._pointer += size return self._payload[self._pointer - size:self._pointer] #---------------------------------------------------------------------------# # Exported Identifiers #---------------------------------------------------------------------------# __all__ = ["BinaryPayloadBuilder", "BinaryPayloadDecoder"] pymodbus/pymodbus/server/0000755000175500017550000000000012607272152015626 5ustar debacledebaclepymodbus/pymodbus/server/async.py0000644000175500017550000002614212607272152017322 0ustar debacledebacle''' Implementation of a Twisted Modbus Server ------------------------------------------ ''' import traceback from binascii import b2a_hex from twisted.internet import protocol from twisted.internet.protocol import ServerFactory from pymodbus.constants import Defaults from pymodbus.factory import ServerDecoder from pymodbus.datastore import ModbusServerContext from pymodbus.device import ModbusControlBlock from pymodbus.device import ModbusAccessControl from pymodbus.device import ModbusDeviceIdentification from pymodbus.exceptions import NoSuchSlaveException from pymodbus.transaction import ModbusSocketFramer, ModbusAsciiFramer from pymodbus.pdu import ModbusExceptions as merror #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Modbus TCP Server #---------------------------------------------------------------------------# class ModbusTcpProtocol(protocol.Protocol): ''' Implements a modbus server in twisted ''' def connectionMade(self): ''' Callback for when a client connects ..note:: since the protocol factory cannot be accessed from the protocol __init__, the client connection made is essentially our __init__ method. ''' _logger.debug("Client Connected [%s]" % self.transport.getHost()) self.framer = self.factory.framer(decoder=self.factory.decoder) def connectionLost(self, reason): ''' Callback for when a client disconnects :param reason: The client's reason for disconnecting ''' _logger.debug("Client Disconnected: %s" % reason) def dataReceived(self, data): ''' Callback when we receive any data :param data: The data sent by the client ''' if _logger.isEnabledFor(logging.DEBUG): _logger.debug(" ".join([hex(ord(x)) for x in data])) if not self.factory.control.ListenOnly: self.framer.processIncomingPacket(data, self._execute) def _execute(self, request): ''' Executes the request and returns the result :param request: The decoded request message ''' 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) #self.framer.populateResult(response) response.transaction_id = request.transaction_id response.unit_id = request.unit_id self._send(response) def _send(self, message): ''' Send a request (string) to the network :param message: The unencoded modbus response ''' if message.should_respond: self.factory.control.Counter.BusMessage += 1 pdu = self.framer.buildPacket(message) if _logger.isEnabledFor(logging.DEBUG): _logger.debug('send: %s' % b2a_hex(pdu)) return self.transport.write(pdu) class ModbusServerFactory(ServerFactory): ''' Builder class for a modbus server This also holds the server datastore so that it is persisted between connections ''' protocol = ModbusTcpProtocol 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 uses its own empty structure. :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) #---------------------------------------------------------------------------# # Modbus UDP Server #---------------------------------------------------------------------------# class ModbusUdpProtocol(protocol.DatagramProtocol): ''' Implements a modbus udp server in twisted ''' 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 uses its own empty structure. :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) def datagramReceived(self, data, addr): ''' Callback when we receive any data :param data: The data sent by the client ''' _logger.debug("Client Connected [%s:%s]" % addr) if _logger.isEnabledFor(logging.DEBUG): _logger.debug(" ".join([hex(ord(x)) for x in data])) if not self.control.ListenOnly: continuation = lambda request: self._execute(request, addr) self.framer.processIncomingPacket(data, continuation) def _execute(self, request, addr): ''' Executes the request and returns the result :param request: The decoded request message ''' 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) #self.framer.populateResult(response) response.transaction_id = request.transaction_id response.unit_id = request.unit_id self._send(response, addr) def _send(self, message, addr): ''' Send a request (string) to the network :param message: The unencoded modbus response :param addr: The (host, port) to send the message to ''' self.control.Counter.BusMessage += 1 pdu = self.framer.buildPacket(message) if _logger.isEnabledFor(logging.DEBUG): _logger.debug('send: %s' % b2a_hex(pdu)) return self.transport.write(pdu, addr) #---------------------------------------------------------------------------# # Starting Factories #---------------------------------------------------------------------------# 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, **kwargs) if console: from pymodbus.internal.ptwisted import InstallManagementConsole 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, **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, **kwargs) _logger.info("Starting Modbus UDP Server on %s:%s" % address) reactor.listenUDP(address[1], server, interface=address[0]) reactor.run() def StartSerialServer(context, identity=None, framer=ModbusAsciiFramer, **kwargs): ''' Helper method to start the Modbus Async Serial server :param context: The server data context :param identify: The server identity to use (default empty) :param framer: The framer to use (default ModbusAsciiFramer) :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 port = kwargs.get('port', '/dev/ttyS0') baudrate = kwargs.get('baudrate', Defaults.Baudrate) console = kwargs.get('console', False) _logger.info("Starting Modbus Serial Server on %s" % port) factory = ModbusServerFactory(context, framer, identity, **kwargs) if console: from pymodbus.internal.ptwisted import InstallManagementConsole InstallManagementConsole({'factory': factory}) protocol = factory.buildProtocol(None) SerialPort.getHost = lambda self: port # hack for logging SerialPort(protocol, port, reactor, baudrate) reactor.run() #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "StartTcpServer", "StartUdpServer", "StartSerialServer", ] pymodbus/pymodbus/server/sync.py0000644000175500017550000004536212607272152017166 0ustar debacledebacle''' Implementation of a Threaded Modbus Server ------------------------------------------ ''' from binascii import b2a_hex import SocketServer import serial import socket import traceback from pymodbus.constants import Defaults from pymodbus.factory import ServerDecoder from pymodbus.datastore import ModbusServerContext from pymodbus.device import ModbusControlBlock from pymodbus.device import ModbusDeviceIdentification from pymodbus.transaction import * from pymodbus.exceptions import NotImplementedException, NoSuchSlaveException from pymodbus.pdu import ModbusExceptions as merror #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Protocol Handlers #---------------------------------------------------------------------------# class ModbusBaseRequestHandler(SocketServer.BaseRequestHandler): ''' Implements the modbus server protocol This uses the socketserver.BaseRequestHandler to implement the client handler. ''' def setup(self): ''' Callback for when a client connects ''' _logger.debug("Client Connected [%s:%s]" % self.client_address) self.running = True self.framer = self.server.framer(self.server.decoder) self.server.threads.append(self) def finish(self): ''' Callback for when a client disconnects ''' _logger.debug("Client Disconnected [%s:%s]" % self.client_address) self.server.threads.remove(self) def execute(self, request): ''' The callback to call with the resulting message :param request: The decoded request message ''' try: context = self.server.context[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.server.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; %s", ex, traceback.format_exc() ) response = request.doException(merror.SlaveFailure) response.transaction_id = request.transaction_id response.unit_id = request.unit_id self.send(response) #---------------------------------------------------------------------------# # Base class implementations #---------------------------------------------------------------------------# def handle(self): ''' Callback when we receive any data ''' raise NotImplementedException("Method not implemented by derived class") def send(self, message): ''' Send a request (string) to the network :param message: The unencoded modbus response ''' raise NotImplementedException("Method not implemented by derived class") class ModbusSingleRequestHandler(ModbusBaseRequestHandler): ''' Implements the modbus server protocol This uses the socketserver.BaseRequestHandler to implement the client handler for a single client(serial clients) ''' def handle(self): ''' Callback when we receive any data ''' while self.running: try: data = self.request.recv(1024) if data: if _logger.isEnabledFor(logging.DEBUG): _logger.debug(" ".join([hex(ord(x)) for x in data])) self.framer.processIncomingPacket(data, self.execute) except Exception, msg: # since we only have a single socket, we cannot exit _logger.error("Socket error occurred %s" % msg) def send(self, message): ''' Send a request (string) to the network :param message: The unencoded modbus response ''' if message.should_respond: #self.server.control.Counter.BusMessage += 1 pdu = self.framer.buildPacket(message) if _logger.isEnabledFor(logging.DEBUG): _logger.debug('send: %s' % b2a_hex(pdu)) return self.request.send(pdu) class ModbusConnectedRequestHandler(ModbusBaseRequestHandler): ''' Implements the modbus server protocol This uses the socketserver.BaseRequestHandler to implement the client handler for a connected protocol (TCP). ''' def handle(self): '''Callback when we receive any data, until self.running becomes not True. Blocks indefinitely awaiting data. If shutdown is required, then the global socket.settimeout() may be used, to allow timely checking of self.running. However, since this also affects socket connects, if there are outgoing socket connections used in the same program, then these will be prevented, if the specfied timeout is too short. Hence, this is unreliable. To respond to Modbus...Server.server_close() (which clears each handler's self.running), derive from this class to provide an alternative handler that awakens from time to time when no input is available and checks self.running. Use Modbus...Server( handler=... ) keyword to supply the alternative request handler class. ''' while self.running: try: data = self.request.recv(1024) if not data: self.running = False if _logger.isEnabledFor(logging.DEBUG): _logger.debug(" ".join([hex(ord(x)) for x in data])) # if not self.server.control.ListenOnly: self.framer.processIncomingPacket(data, self.execute) except socket.timeout as msg: if _logger.isEnabledFor(logging.DEBUG): _logger.debug("Socket timeout occurred %s", msg) pass except socket.error as msg: _logger.error("Socket error occurred %s", msg) self.running = False except: _logger.error("Socket exception occurred %s", traceback.format_exc() ) self.running = False def send(self, message): ''' Send a request (string) to the network :param message: The unencoded modbus response ''' if message.should_respond: #self.server.control.Counter.BusMessage += 1 pdu = self.framer.buildPacket(message) if _logger.isEnabledFor(logging.DEBUG): _logger.debug('send: %s' % b2a_hex(pdu)) return self.request.send(pdu) class ModbusDisconnectedRequestHandler(ModbusBaseRequestHandler): ''' Implements the modbus server protocol This uses the socketserver.BaseRequestHandler to implement the client handler for a disconnected protocol (UDP). The only difference is that we have to specify who to send the resulting packet data to. ''' def handle(self): ''' Callback when we receive any data ''' while self.running: try: data, self.request = self.request if not data: self.running = False if _logger.isEnabledFor(logging.DEBUG): _logger.debug(" ".join([hex(ord(x)) for x in data])) # if not self.server.control.ListenOnly: self.framer.processIncomingPacket(data, self.execute) except socket.timeout: pass except socket.error, msg: _logger.error("Socket error occurred %s" % msg) self.running = False except: self.running = False def send(self, message): ''' Send a request (string) to the network :param message: The unencoded modbus response ''' if message.should_respond: #self.server.control.Counter.BusMessage += 1 pdu = self.framer.buildPacket(message) if _logger.isEnabledFor(logging.DEBUG): _logger.debug('send: %s' % b2a_hex(pdu)) return self.request.sendto(pdu, self.client_address) #---------------------------------------------------------------------------# # Server Implementations #---------------------------------------------------------------------------# class ModbusTcpServer(SocketServer.ThreadingTCPServer): ''' A modbus threaded tcp socket server We inherit and overload the socket server so that we can control the client threads as well as have a single server context instance. ''' def __init__(self, context, framer=None, identity=None, address=None, handler=None, **kwargs): ''' Overloaded initializer for the socket server If the identify structure is not passed in, the ModbusControlBlock uses its own empty structure. :param context: The ModbusServerContext datastore :param framer: The framer strategy to use :param identity: An optional identify structure :param address: An optional (interface, port) to bind to. :param handler: A handler for each client session; default is ModbusConnectedRequestHandler :param ignore_missing_slaves: True to not send errors on a request to a missing slave ''' self.threads = [] self.decoder = ServerDecoder() self.framer = framer or ModbusSocketFramer self.context = context or ModbusServerContext() self.control = ModbusControlBlock() self.address = address or ("", Defaults.Port) self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves', Defaults.IgnoreMissingSlaves) if isinstance(identity, ModbusDeviceIdentification): self.control.Identity.update(identity) SocketServer.ThreadingTCPServer.__init__(self, self.address, handler or ModbusConnectedRequestHandler) def process_request(self, request, client): ''' Callback for connecting a new client thread :param request: The request to handle :param client: The address of the client ''' _logger.debug("Started thread to serve client at " + str(client)) SocketServer.ThreadingTCPServer.process_request(self, request, client) def shutdown(self): ''' Stops the serve_forever loop. Overridden to signal handlers to stop. ''' for thread in self.threads: thread.running = False SocketServer.ThreadingTCPServer.shutdown(self) def server_close(self): ''' Callback for stopping the running server ''' _logger.debug("Modbus server stopped") self.socket.close() for thread in self.threads: thread.running = False class ModbusUdpServer(SocketServer.ThreadingUDPServer): ''' A modbus threaded udp socket server We inherit and overload the socket server so that we can control the client threads as well as have a single server context instance. ''' def __init__(self, context, framer=None, identity=None, address=None, handler=None, **kwargs): ''' Overloaded initializer for the socket server If the identify structure is not passed in, the ModbusControlBlock uses its own empty structure. :param context: The ModbusServerContext datastore :param framer: The framer strategy to use :param identity: An optional identify structure :param address: An optional (interface, port) to bind to. :param handler: A handler for each client session; default is ModbusDisonnectedRequestHandler :param ignore_missing_slaves: True to not send errors on a request to a missing slave ''' self.threads = [] self.decoder = ServerDecoder() self.framer = framer or ModbusSocketFramer self.context = context or ModbusServerContext() self.control = ModbusControlBlock() self.address = address or ("", Defaults.Port) self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves', Defaults.IgnoreMissingSlaves) if isinstance(identity, ModbusDeviceIdentification): self.control.Identity.update(identity) SocketServer.ThreadingUDPServer.__init__(self, self.address, handler or ModbusDisconnectedRequestHandler) def process_request(self, request, client): ''' Callback for connecting a new client thread :param request: The request to handle :param client: The address of the client ''' packet, socket = request # TODO I might have to rewrite _logger.debug("Started thread to serve client at " + str(client)) SocketServer.ThreadingUDPServer.process_request(self, request, client) def server_close(self): ''' Callback for stopping the running server ''' _logger.debug("Modbus server stopped") self.socket.close() for thread in self.threads: thread.running = False class ModbusSerialServer(object): ''' A modbus threaded udp socket server We inherit and overload the socket server so that we can control the client threads as well as have a single server context instance. ''' def __init__(self, context, framer=None, identity=None, **kwargs): ''' Overloaded initializer for the socket server If the identify structure is not passed in, the ModbusControlBlock uses its own empty structure. :param context: The ModbusServerContext datastore :param framer: The framer strategy to use :param identity: An optional identify structure :param port: The serial port to attach to :param stopbits: The number of stop bits to use :param bytesize: The bytesize of the serial messages :param parity: Which kind of parity to use :param baudrate: The baud rate to use for the serial device :param timeout: The timeout to use for the serial device :param ignore_missing_slaves: True to not send errors on a request to a missing slave ''' self.threads = [] self.decoder = ServerDecoder() self.framer = framer or ModbusAsciiFramer self.context = context or ModbusServerContext() self.control = ModbusControlBlock() if isinstance(identity, ModbusDeviceIdentification): self.control.Identity.update(identity) self.device = kwargs.get('port', 0) self.stopbits = kwargs.get('stopbits', Defaults.Stopbits) self.bytesize = kwargs.get('bytesize', Defaults.Bytesize) self.parity = kwargs.get('parity', Defaults.Parity) self.baudrate = kwargs.get('baudrate', Defaults.Baudrate) self.timeout = kwargs.get('timeout', Defaults.Timeout) self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves', Defaults.IgnoreMissingSlaves) self.socket = None self._connect() self.is_running = True def _connect(self): ''' Connect to the serial server :returns: True if connection succeeded, False otherwise ''' if self.socket: return True try: self.socket = serial.Serial(port=self.device, timeout=self.timeout, bytesize=self.bytesize, stopbits=self.stopbits, baudrate=self.baudrate, parity=self.parity) except serial.SerialException, msg: _logger.error(msg) return self.socket != None def _build_handler(self): ''' A helper method to create and monkeypatch a serial handler. :returns: A patched handler ''' request = self.socket request.send = request.write request.recv = request.read handler = ModbusSingleRequestHandler(request, (self.device, self.device), self) return handler def serve_forever(self): ''' Callback for connecting a new client thread :param request: The request to handle :param client: The address of the client ''' _logger.debug("Started thread to serve client") handler = self._build_handler() while self.is_running: handler.handle() def server_close(self): ''' Callback for stopping the running server ''' _logger.debug("Modbus server stopped") self.is_running = False self.socket.close() #---------------------------------------------------------------------------# # Creation Factories #---------------------------------------------------------------------------# def StartTcpServer(context=None, identity=None, address=None, **kwargs): ''' A factory to start and run a tcp modbus server :param context: The ModbusServerContext datastore :param identity: An optional identify structure :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 ''' framer = ModbusSocketFramer server = ModbusTcpServer(context, framer, identity, address, **kwargs) server.serve_forever() def StartUdpServer(context=None, identity=None, address=None, **kwargs): ''' A factory to start and run a udp modbus server :param context: The ModbusServerContext datastore :param identity: An optional identify structure :param address: An optional (interface, port) to bind to. :param framer: The framer to operate with (default ModbusSocketFramer) :param ignore_missing_slaves: True to not send errors on a request to a missing slave ''' framer = kwargs.pop('framer', ModbusSocketFramer) server = ModbusUdpServer(context, framer, identity, address, **kwargs) server.serve_forever() def StartSerialServer(context=None, identity=None, **kwargs): ''' A factory to start and run a udp modbus server :param context: The ModbusServerContext datastore :param identity: An optional identify structure :param framer: The framer to operate with (default ModbusAsciiFramer) :param port: The serial port to attach to :param stopbits: The number of stop bits to use :param bytesize: The bytesize of the serial messages :param parity: Which kind of parity to use :param baudrate: The baud rate to use for the serial device :param timeout: The timeout to use for the serial device :param ignore_missing_slaves: True to not send errors on a request to a missing slave ''' framer = kwargs.pop('framer', ModbusAsciiFramer) server = ModbusSerialServer(context, framer, identity, **kwargs) server.serve_forever() #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "StartTcpServer", "StartUdpServer", "StartSerialServer" ] pymodbus/pymodbus/server/__init__.py0000644000175500017550000000001012607272152017726 0ustar debacledebacle''' ''' pymodbus/pymodbus/events.py0000644000175500017550000001435412607272152016205 0ustar debacledebacle''' Modbus Remote Events ------------------------------------------------------------ An event byte returned by the Get Communications Event Log function can be any one of four types. The type is defined by bit 7 (the high-order bit) in each byte. It may be further defined by bit 6. ''' from pymodbus.exceptions import NotImplementedException from pymodbus.exceptions import ParameterException from pymodbus.utilities import pack_bitstring, unpack_bitstring class ModbusEvent(object): def encode(self): ''' Encodes the status bits to an event message :returns: The encoded event message ''' raise NotImplementedException() def decode(self, event): ''' Decodes the event message to its status bits :param event: The event to decode ''' raise NotImplementedException() class RemoteReceiveEvent(ModbusEvent): ''' Remote device MODBUS Receive Event The remote device stores this type of event byte when a query message is received. It is stored before the remote device processes the message. This event is defined by bit 7 set to logic '1'. The other bits will be set to a logic '1' if the corresponding condition is TRUE. The bit layout is:: Bit Contents ---------------------------------- 0 Not Used 2 Not Used 3 Not Used 4 Character Overrun 5 Currently in Listen Only Mode 6 Broadcast Receive 7 1 ''' def __init__(self, **kwargs): ''' Initialize a new event instance ''' self.overrun = kwargs.get('overrun', False) self.listen = kwargs.get('listen', False) self.broadcast = kwargs.get('broadcast', False) def encode(self): ''' Encodes the status bits to an event message :returns: The encoded event message ''' bits = [False] * 3 bits += [self.overrun, self.listen, self.broadcast, True] packet = pack_bitstring(bits) return packet def decode(self, event): ''' Decodes the event message to its status bits :param event: The event to decode ''' bits = unpack_bitstring(event) self.overrun = bits[4] self.listen = bits[5] self.broadcast = bits[6] class RemoteSendEvent(ModbusEvent): ''' Remote device MODBUS Send Event The remote device stores this type of event byte when it finishes processing a request message. It is stored if the remote device returned a normal or exception response, or no response. This event is defined by bit 7 set to a logic '0', with bit 6 set to a '1'. The other bits will be set to a logic '1' if the corresponding condition is TRUE. The bit layout is:: Bit Contents ----------------------------------------------------------- 0 Read Exception Sent (Exception Codes 1-3) 1 Slave Abort Exception Sent (Exception Code 4) 2 Slave Busy Exception Sent (Exception Codes 5-6) 3 Slave Program NAK Exception Sent (Exception Code 7) 4 Write Timeout Error Occurred 5 Currently in Listen Only Mode 6 1 7 0 ''' def __init__(self, **kwargs): ''' Initialize a new event instance ''' self.read = kwargs.get('read', False) self.slave_abort = kwargs.get('slave_abort', False) self.slave_busy = kwargs.get('slave_busy', False) self.slave_nak = kwargs.get('slave_nak', False) self.write_timeout = kwargs.get('write_timeout', False) self.listen = kwargs.get('listen', False) def encode(self): ''' Encodes the status bits to an event message :returns: The encoded event message ''' bits = [self.read, self.slave_abort, self.slave_busy, self.slave_nak, self.write_timeout, self.listen] bits += [True, False] packet = pack_bitstring(bits) return packet def decode(self, event): ''' Decodes the event message to its status bits :param event: The event to decode ''' # todo fix the start byte count bits = unpack_bitstring(event) self.read = bits[0] self.slave_abort = bits[1] self.slave_busy = bits[2] self.slave_nak = bits[3] self.write_timeout = bits[4] self.listen = bits[5] class EnteredListenModeEvent(ModbusEvent): ''' Remote device Entered Listen Only Mode The remote device stores this type of event byte when it enters the Listen Only Mode. The event is defined by a content of 04 hex. ''' value = 0x04 __encoded = '\x04' def encode(self): ''' Encodes the status bits to an event message :returns: The encoded event message ''' return self.__encoded def decode(self, event): ''' Decodes the event message to its status bits :param event: The event to decode ''' if event != self.__encoded: raise ParameterException('Invalid decoded value') class CommunicationRestartEvent(ModbusEvent): ''' Remote device Initiated Communication Restart The remote device stores this type of event byte when its communications port is restarted. The remote device can be restarted by the Diagnostics function (code 08), with sub-function Restart Communications Option (code 00 01). That function also places the remote device into a 'Continue on Error' or 'Stop on Error' mode. If the remote device is placed into 'Continue on Error' mode, the event byte is added to the existing event log. If the remote device is placed into 'Stop on Error' mode, the byte is added to the log and the rest of the log is cleared to zeros. The event is defined by a content of zero. ''' value = 0x00 __encoded = '\x00' def encode(self): ''' Encodes the status bits to an event message :returns: The encoded event message ''' return self.__encoded def decode(self, event): ''' Decodes the event message to its status bits :param event: The event to decode ''' if event != self.__encoded: raise ParameterException('Invalid decoded value') pymodbus/pymodbus/transaction.py0000644000175500017550000007657312607272152017241 0ustar debacledebacle''' Collection of transaction based abstractions ''' import sys import struct import socket from binascii import b2a_hex, a2b_hex from pymodbus.exceptions import ModbusIOException from pymodbus.constants import Defaults from pymodbus.interfaces import IModbusFramer from pymodbus.utilities import checkCRC, computeCRC from pymodbus.utilities import checkLRC, computeLRC #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # The Global Transaction Manager #---------------------------------------------------------------------------# class ModbusTransactionManager(object): ''' Impelements a transaction for a manager The transaction protocol can be represented by the following pseudo code:: count = 0 do result = send(message) if (timeout or result == bad) count++ else break while (count < 3) This module helps to abstract this away from the framer and protocol. ''' def __init__(self, client, **kwargs): ''' Initializes an instance of the ModbusTransactionManager :param client: The client socket wrapper :param retry_on_empty: Should the client retry on empty :param retries: The number of retries to allow ''' self.tid = Defaults.TransactionId self.client = client self.retry_on_empty = kwargs.get('retry_on_empty', Defaults.RetryOnEmpty) self.retries = kwargs.get('retries', Defaults.Retries) def execute(self, request): ''' Starts the producer to send the next request to consumer.write(Frame(request)) ''' retries = self.retries request.transaction_id = self.getNextTID() _logger.debug("Running transaction %d" % request.transaction_id) while retries > 0: try: self.client.connect() self.client._send(self.client.framer.buildPacket(request)) # I need to fix this to read the header and the result size, # as this may not read the full result set, but right now # it should be fine... result = self.client._recv(1024) if not result and self.retry_on_empty: retries -= 1 continue if _logger.isEnabledFor(logging.DEBUG): _logger.debug("recv: " + " ".join([hex(ord(x)) for x in result])) self.client.framer.processIncomingPacket(result, self.addTransaction) break; except socket.error, msg: self.client.close() _logger.debug("Transaction failed. (%s) " % msg) retries -= 1 return self.getTransaction(request.transaction_id) def addTransaction(self, request, tid=None): ''' Adds a transaction to the handler This holds the requets in case it needs to be resent. After being sent, the request is removed. :param request: The request to hold on to :param tid: The overloaded transaction id to use ''' raise NotImplementedException("addTransaction") def getTransaction(self, tid): ''' Returns a transaction matching the referenced tid If the transaction does not exist, None is returned :param tid: The transaction to retrieve ''' raise NotImplementedException("getTransaction") def delTransaction(self, tid): ''' Removes a transaction matching the referenced tid :param tid: The transaction to remove ''' raise NotImplementedException("delTransaction") def getNextTID(self): ''' Retrieve the next unique transaction identifier This handles incrementing the identifier after retrieval :returns: The next unique transaction identifier ''' self.tid = (self.tid + 1) & 0xffff return self.tid def reset(self): ''' Resets the transaction identifier ''' self.tid = Defaults.TransactionId self.transactions = type(self.transactions)() class DictTransactionManager(ModbusTransactionManager): ''' Impelements a transaction for a manager where the results are keyed based on the supplied transaction id. ''' def __init__(self, client, **kwargs): ''' Initializes an instance of the ModbusTransactionManager :param client: The client socket wrapper ''' self.transactions = {} super(DictTransactionManager, self).__init__(client, **kwargs) def __iter__(self): ''' Iterater over the current managed transactions :returns: An iterator of the managed transactions ''' return iter(self.transactions.keys()) def addTransaction(self, request, tid=None): ''' Adds a transaction to the handler This holds the requets in case it needs to be resent. After being sent, the request is removed. :param request: The request to hold on to :param tid: The overloaded transaction id to use ''' tid = tid if tid != None else request.transaction_id _logger.debug("adding transaction %d" % tid) self.transactions[tid] = request def getTransaction(self, tid): ''' Returns a transaction matching the referenced tid If the transaction does not exist, None is returned :param tid: The transaction to retrieve ''' _logger.debug("getting transaction %d" % tid) return self.transactions.pop(tid, None) def delTransaction(self, tid): ''' Removes a transaction matching the referenced tid :param tid: The transaction to remove ''' _logger.debug("deleting transaction %d" % tid) self.transactions.pop(tid, None) class FifoTransactionManager(ModbusTransactionManager): ''' Impelements a transaction for a manager where the results are returned in a FIFO manner. ''' def __init__(self, client, **kwargs): ''' Initializes an instance of the ModbusTransactionManager :param client: The client socket wrapper ''' super(FifoTransactionManager, self).__init__(client, **kwargs) self.transactions = [] def __iter__(self): ''' Iterater over the current managed transactions :returns: An iterator of the managed transactions ''' return iter(self.transactions) def addTransaction(self, request, tid=None): ''' Adds a transaction to the handler This holds the requets in case it needs to be resent. After being sent, the request is removed. :param request: The request to hold on to :param tid: The overloaded transaction id to use ''' tid = tid if tid != None else request.transaction_id _logger.debug("adding transaction %d" % tid) self.transactions.append(request) def getTransaction(self, tid): ''' Returns a transaction matching the referenced tid If the transaction does not exist, None is returned :param tid: The transaction to retrieve ''' _logger.debug("getting transaction %s" % str(tid)) return self.transactions.pop(0) if self.transactions else None def delTransaction(self, tid): ''' Removes a transaction matching the referenced tid :param tid: The transaction to remove ''' _logger.debug("deleting transaction %d" % tid) if self.transactions: self.transactions.pop(0) #---------------------------------------------------------------------------# # Modbus TCP Message #---------------------------------------------------------------------------# class ModbusSocketFramer(IModbusFramer): ''' Modbus Socket Frame controller Before each modbus TCP message is an MBAP header which is used as a message frame. It allows us to easily separate messages as follows:: [ MBAP Header ] [ Function Code] [ Data ] [ tid ][ pid ][ length ][ uid ] 2b 2b 2b 1b 1b Nb while len(message) > 0: tid, pid, length`, uid = struct.unpack(">HHHB", message) request = message[0:7 + length - 1`] message = [7 + length - 1:] * length = uid + function code + data * The -1 is to account for the uid byte ''' def __init__(self, decoder): ''' Initializes a new instance of the framer :param decoder: The decoder factory implementation to use ''' self.__buffer = '' self.__header = {'tid':0, 'pid':0, 'len':0, 'uid':0} self.__hsize = 0x07 self.decoder = decoder #-----------------------------------------------------------------------# # Private Helper Functions #-----------------------------------------------------------------------# def checkFrame(self): ''' Check and decode the next frame Return true if we were successful ''' if len(self.__buffer) > self.__hsize: self.__header['tid'], self.__header['pid'], \ self.__header['len'], self.__header['uid'] = struct.unpack( '>HHHB', self.__buffer[0:self.__hsize]) # someone sent us an error? ignore it if self.__header['len'] < 2: self.advanceFrame() # we have at least a complete message, continue elif len(self.__buffer) - self.__hsize + 1 >= self.__header['len']: return True # we don't have enough of a message yet, wait return False def advanceFrame(self): ''' Skip over the current framed message This allows us to skip over the current message after we have processed it or determined that it contains an error. It also has to reset the current frame header handle ''' length = self.__hsize + self.__header['len'] - 1 self.__buffer = self.__buffer[length:] self.__header = {'tid':0, 'pid':0, 'len':0, 'uid':0} def isFrameReady(self): ''' Check if we should continue decode logic This is meant to be used in a while loop in the decoding phase to let the decoder factory know that there is still data in the buffer. :returns: True if ready, False otherwise ''' return len(self.__buffer) > self.__hsize def addToFrame(self, message): ''' Adds new packet data to the current frame buffer :param message: The most recent packet ''' self.__buffer += message def getFrame(self): ''' Return the next frame from the buffered data :returns: The next full frame buffer ''' length = self.__hsize + self.__header['len'] - 1 return self.__buffer[self.__hsize:length] def populateResult(self, result): ''' Populates the modbus result with the transport specific header information (pid, tid, uid, checksum, etc) :param result: The response packet ''' result.transaction_id = self.__header['tid'] result.protocol_id = self.__header['pid'] result.unit_id = self.__header['uid'] #-----------------------------------------------------------------------# # Public Member Functions #-----------------------------------------------------------------------# def processIncomingPacket(self, data, callback): ''' The new packet processing pattern This takes in a new request packet, adds it to the current packet stream, and performs framing on it. That is, checks for complete messages, and once found, will process all that exist. This handles the case when we read N + 1 or 1 / N messages at a time instead of 1. The processed and decoded messages are pushed to the callback function to process and send. :param data: The new packet data :param callback: The function to send results to ''' self.addToFrame(data) while self.isFrameReady(): if self.checkFrame(): result = self.decoder.decode(self.getFrame()) if result is None: raise ModbusIOException("Unable to decode request") self.populateResult(result) self.advanceFrame() callback(result) # defer or push to a thread? else: break def buildPacket(self, message): ''' Creates a ready to send modbus packet :param message: The populated request/response to send ''' data = message.encode() packet = struct.pack('>HHHBB', message.transaction_id, message.protocol_id, len(data) + 2, message.unit_id, message.function_code) + data return packet #---------------------------------------------------------------------------# # Modbus RTU Message #---------------------------------------------------------------------------# class ModbusRtuFramer(IModbusFramer): ''' Modbus RTU Frame controller:: [ Start Wait ] [Address ][ Function Code] [ Data ][ CRC ][ End Wait ] 3.5 chars 1b 1b Nb 2b 3.5 chars Wait refers to the amount of time required to transmist at least x many characters. In this case it is 3.5 characters. Also, if we recieve a wait of 1.5 characters at any point, we must trigger an error message. Also, it appears as though this message is little endian. The logic is simplified as the following:: block-on-read: read until 3.5 delay check for errors decode The following table is a listing of the baud wait times for the specified baud rates:: ------------------------------------------------------------------ Baud 1.5c (18 bits) 3.5c (38 bits) ------------------------------------------------------------------ 1200 13333.3 us 31666.7 us 4800 3333.3 us 7916.7 us 9600 1666.7 us 3958.3 us 19200 833.3 us 1979.2 us 38400 416.7 us 989.6 us ------------------------------------------------------------------ 1 Byte = start + 8 bits + parity + stop = 11 bits (1/Baud)(bits) = delay seconds ''' def __init__(self, decoder): ''' Initializes a new instance of the framer :param decoder: The decoder factory implementation to use ''' self.__buffer = '' self.__header = {} self.__hsize = 0x01 self.__end = '\x0d\x0a' self.__min_frame_size = 4 self.decoder = decoder #-----------------------------------------------------------------------# # Private Helper Functions #-----------------------------------------------------------------------# def checkFrame(self): ''' Check if the next frame is available. Return True if we were successful. ''' try: self.populateHeader() frame_size = self.__header['len'] data = self.__buffer[:frame_size - 2] crc = self.__buffer[frame_size - 2:frame_size] crc_val = (ord(crc[0]) << 8) + ord(crc[1]) return checkCRC(data, crc_val) except (IndexError, KeyError): return False def advanceFrame(self): ''' Skip over the current framed message This allows us to skip over the current message after we have processed it or determined that it contains an error. It also has to reset the current frame header handle ''' self.__buffer = self.__buffer[self.__header['len']:] self.__header = {} def resetFrame(self): ''' Reset the entire message frame. This allows us to skip ovver errors that may be in the stream. It is hard to know if we are simply out of sync or if there is an error in the stream as we have no way to check the start or end of the message (python just doesn't have the resolution to check for millisecond delays). ''' self.__buffer = '' self.__header = {} def isFrameReady(self): ''' Check if we should continue decode logic This is meant to be used in a while loop in the decoding phase to let the decoder know that there is still data in the buffer. :returns: True if ready, False otherwise ''' return len(self.__buffer) > self.__hsize def populateHeader(self): ''' Try to set the headers `uid`, `len` and `crc`. This method examines `self.__buffer` and writes meta information into `self.__header`. It calculates only the values for headers that are not already in the dictionary. Beware that this method will raise an IndexError if `self.__buffer` is not yet long enough. ''' self.__header['uid'] = struct.unpack('>B', self.__buffer[0])[0] func_code = struct.unpack('>B', self.__buffer[1])[0] pdu_class = self.decoder.lookupPduClass(func_code) size = pdu_class.calculateRtuFrameSize(self.__buffer) self.__header['len'] = size self.__header['crc'] = self.__buffer[size - 2:size] def addToFrame(self, message): ''' This should be used before the decoding while loop to add the received data to the buffer handle. :param message: The most recent packet ''' self.__buffer += message def getFrame(self): ''' Get the next frame from the buffer :returns: The frame data or '' ''' start = self.__hsize end = self.__header['len'] - 2 buffer = self.__buffer[start:end] if end > 0: return buffer return '' def populateResult(self, result): ''' Populates the modbus result header The serial packets do not have any header information that is copied. :param result: The response packet ''' result.unit_id = self.__header['uid'] #-----------------------------------------------------------------------# # Public Member Functions #-----------------------------------------------------------------------# def processIncomingPacket(self, data, callback): ''' The new packet processing pattern This takes in a new request packet, adds it to the current packet stream, and performs framing on it. That is, checks for complete messages, and once found, will process all that exist. This handles the case when we read N + 1 or 1 / N messages at a time instead of 1. The processed and decoded messages are pushed to the callback function to process and send. :param data: The new packet data :param callback: The function to send results to ''' self.addToFrame(data) while self.isFrameReady(): if self.checkFrame(): result = self.decoder.decode(self.getFrame()) if result is None: raise ModbusIOException("Unable to decode response") self.populateResult(result) self.advanceFrame() callback(result) # defer or push to a thread? else: self.resetFrame() # clear possible errors def buildPacket(self, message): ''' Creates a ready to send modbus packet :param message: The populated request/response to send ''' data = message.encode() packet = struct.pack('>BB', message.unit_id, message.function_code) + data packet += struct.pack(">H", computeCRC(packet)) return packet #---------------------------------------------------------------------------# # Modbus ASCII Message #---------------------------------------------------------------------------# class ModbusAsciiFramer(IModbusFramer): ''' Modbus ASCII Frame Controller:: [ Start ][Address ][ Function ][ Data ][ LRC ][ End ] 1c 2c 2c Nc 2c 2c * data can be 0 - 2x252 chars * end is '\\r\\n' (Carriage return line feed), however the line feed character can be changed via a special command * start is ':' This framer is used for serial transmission. Unlike the RTU protocol, the data in this framer is transferred in plain text ascii. ''' def __init__(self, decoder): ''' Initializes a new instance of the framer :param decoder: The decoder implementation to use ''' self.__buffer = '' self.__header = {'lrc':'0000', 'len':0, 'uid':0x00} self.__hsize = 0x02 self.__start = ':' self.__end = "\r\n" self.decoder = decoder #-----------------------------------------------------------------------# # Private Helper Functions #-----------------------------------------------------------------------# def checkFrame(self): ''' Check and decode the next frame :returns: True if we successful, False otherwise ''' start = self.__buffer.find(self.__start) if start == -1: return False if start > 0 : # go ahead and skip old bad data self.__buffer = self.__buffer[start:] start = 0 end = self.__buffer.find(self.__end) if (end != -1): self.__header['len'] = end self.__header['uid'] = int(self.__buffer[1:3], 16) self.__header['lrc'] = int(self.__buffer[end - 2:end], 16) data = a2b_hex(self.__buffer[start + 1:end - 2]) return checkLRC(data, self.__header['lrc']) return False def advanceFrame(self): ''' Skip over the current framed message This allows us to skip over the current message after we have processed it or determined that it contains an error. It also has to reset the current frame header handle ''' self.__buffer = self.__buffer[self.__header['len'] + 2:] self.__header = {'lrc':'0000', 'len':0, 'uid':0x00} def isFrameReady(self): ''' Check if we should continue decode logic This is meant to be used in a while loop in the decoding phase to let the decoder know that there is still data in the buffer. :returns: True if ready, False otherwise ''' return len(self.__buffer) > 1 def addToFrame(self, message): ''' Add the next message to the frame buffer This should be used before the decoding while loop to add the received data to the buffer handle. :param message: The most recent packet ''' self.__buffer += message def getFrame(self): ''' Get the next frame from the buffer :returns: The frame data or '' ''' start = self.__hsize + 1 end = self.__header['len'] - 2 buffer = self.__buffer[start:end] if end > 0: return a2b_hex(buffer) return '' def populateResult(self, result): ''' Populates the modbus result header The serial packets do not have any header information that is copied. :param result: The response packet ''' result.unit_id = self.__header['uid'] #-----------------------------------------------------------------------# # Public Member Functions #-----------------------------------------------------------------------# def processIncomingPacket(self, data, callback): ''' The new packet processing pattern This takes in a new request packet, adds it to the current packet stream, and performs framing on it. That is, checks for complete messages, and once found, will process all that exist. This handles the case when we read N + 1 or 1 / N messages at a time instead of 1. The processed and decoded messages are pushed to the callback function to process and send. :param data: The new packet data :param callback: The function to send results to ''' self.addToFrame(data) while self.isFrameReady(): if self.checkFrame(): result = self.decoder.decode(self.getFrame()) if result is None: raise ModbusIOException("Unable to decode response") self.populateResult(result) self.advanceFrame() callback(result) # defer this else: break def buildPacket(self, message): ''' Creates a ready to send modbus packet Built off of a modbus request/response :param message: The request/response to send :return: The encoded packet ''' encoded = message.encode() buffer = struct.pack('>BB', message.unit_id, message.function_code) checksum = computeLRC(encoded + buffer) params = (message.unit_id, message.function_code, b2a_hex(encoded)) packet = '%02x%02x%s' % params packet = '%c%s%02x%s' % (self.__start, packet, checksum, self.__end) return packet.upper() #---------------------------------------------------------------------------# # Modbus Binary Message #---------------------------------------------------------------------------# class ModbusBinaryFramer(IModbusFramer): ''' Modbus Binary Frame Controller:: [ Start ][Address ][ Function ][ Data ][ CRC ][ End ] 1b 1b 1b Nb 2b 1b * data can be 0 - 2x252 chars * end is '}' * start is '{' The idea here is that we implement the RTU protocol, however, instead of using timing for message delimiting, we use start and end of message characters (in this case { and }). Basically, this is a binary framer. The only case we have to watch out for is when a message contains the { or } characters. If we encounter these characters, we simply duplicate them. Hopefully we will not encounter those characters that often and will save a little bit of bandwitch without a real-time system. Protocol defined by jamod.sourceforge.net. ''' def __init__(self, decoder): ''' Initializes a new instance of the framer :param decoder: The decoder implementation to use ''' self.__buffer = '' self.__header = {'crc':0x0000, 'len':0, 'uid':0x00} self.__hsize = 0x02 self.__start = '\x7b' # { self.__end = '\x7d' # } self.decoder = decoder #-----------------------------------------------------------------------# # Private Helper Functions #-----------------------------------------------------------------------# def checkFrame(self): ''' Check and decode the next frame :returns: True if we are successful, False otherwise ''' start = self.__buffer.find(self.__start) if start == -1: return False if start > 0 : # go ahead and skip old bad data self.__buffer = self.__buffer[start:] end = self.__buffer.find(self.__end) if (end != -1): self.__header['len'] = end self.__header['uid'] = struct.unpack('>B', self.__buffer[1:2]) self.__header['crc'] = struct.unpack('>H', self.__buffer[end - 2:end])[0] data = self.__buffer[start + 1:end - 2] return checkCRC(data, self.__header['crc']) return False def advanceFrame(self): ''' Skip over the current framed message This allows us to skip over the current message after we have processed it or determined that it contains an error. It also has to reset the current frame header handle ''' self.__buffer = self.__buffer[self.__header['len'] + 2:] self.__header = {'crc':0x0000, 'len':0, 'uid':0x00} def isFrameReady(self): ''' Check if we should continue decode logic This is meant to be used in a while loop in the decoding phase to let the decoder know that there is still data in the buffer. :returns: True if ready, False otherwise ''' return len(self.__buffer) > 1 def addToFrame(self, message): ''' Add the next message to the frame buffer This should be used before the decoding while loop to add the received data to the buffer handle. :param message: The most recent packet ''' self.__buffer += message def getFrame(self): ''' Get the next frame from the buffer :returns: The frame data or '' ''' start = self.__hsize + 1 end = self.__header['len'] - 2 buffer = self.__buffer[start:end] if end > 0: return buffer return '' def populateResult(self, result): ''' Populates the modbus result header The serial packets do not have any header information that is copied. :param result: The response packet ''' result.unit_id = self.__header['uid'] #-----------------------------------------------------------------------# # Public Member Functions #-----------------------------------------------------------------------# def processIncomingPacket(self, data, callback): ''' The new packet processing pattern This takes in a new request packet, adds it to the current packet stream, and performs framing on it. That is, checks for complete messages, and once found, will process all that exist. This handles the case when we read N + 1 or 1 / N messages at a time instead of 1. The processed and decoded messages are pushed to the callback function to process and send. :param data: The new packet data :param callback: The function to send results to ''' self.addToFrame(data) while self.isFrameReady(): if self.checkFrame(): result = self.decoder.decode(self.getFrame()) if result is None: raise ModbusIOException("Unable to decode response") self.populateResult(result) self.advanceFrame() callback(result) # defer or push to a thread? else: break def buildPacket(self, message): ''' Creates a ready to send modbus packet :param message: The request/response to send :returns: The encoded packet ''' data = self._preflight(message.encode()) packet = struct.pack('>BB', message.unit_id, message.function_code) + data packet += struct.pack(">H", computeCRC(packet)) packet = '%s%s%s' % (self.__start, packet, self.__end) return packet def _preflight(self, data): ''' Preflight buffer test This basically scans the buffer for start and end tags and if found, escapes them. :param data: The message to escape :returns: the escaped packet ''' def _filter(a): if a in ['}', '{']: return a * 2 else: return a return ''.join(map(_filter, data)) #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "FifoTransactionManager", "DictTransactionManager", "ModbusSocketFramer", "ModbusRtuFramer", "ModbusAsciiFramer", "ModbusBinaryFramer", ] pymodbus/pymodbus/factory.py0000644000175500017550000002260312607272152016344 0ustar debacledebacle""" Modbus Request/Response Decoder Factories ------------------------------------------- The following factories make it easy to decode request/response messages. To add a new request/response pair to be decodeable by the library, simply add them to the respective function lookup table (order doesn't matter, but it does help keep things organized). Regardless of how many functions are added to the lookup, O(1) behavior is kept as a result of a pre-computed lookup dictionary. """ from pymodbus.pdu import IllegalFunctionRequest from pymodbus.pdu import ExceptionResponse from pymodbus.pdu import ModbusExceptions as ecode from pymodbus.interfaces import IModbusDecoder from pymodbus.exceptions import ModbusException from pymodbus.bit_read_message import * from pymodbus.bit_write_message import * from pymodbus.diag_message import * from pymodbus.file_message import * from pymodbus.other_message import * from pymodbus.mei_message import * from pymodbus.register_read_message import * from pymodbus.register_write_message import * #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Server Decoder #---------------------------------------------------------------------------# class ServerDecoder(IModbusDecoder): ''' Request Message Factory (Server) To add more implemented functions, simply add them to the list ''' __function_table = [ ReadHoldingRegistersRequest, ReadDiscreteInputsRequest, ReadInputRegistersRequest, ReadCoilsRequest, WriteMultipleCoilsRequest, WriteMultipleRegistersRequest, WriteSingleRegisterRequest, WriteSingleCoilRequest, ReadWriteMultipleRegistersRequest, DiagnosticStatusRequest, ReadExceptionStatusRequest, GetCommEventCounterRequest, GetCommEventLogRequest, ReportSlaveIdRequest, ReadFileRecordRequest, WriteFileRecordRequest, MaskWriteRegisterRequest, ReadFifoQueueRequest, ReadDeviceInformationRequest, ] __sub_function_table = [ ReturnQueryDataRequest, RestartCommunicationsOptionRequest, ReturnDiagnosticRegisterRequest, ChangeAsciiInputDelimiterRequest, ForceListenOnlyModeRequest, ClearCountersRequest, ReturnBusMessageCountRequest, ReturnBusCommunicationErrorCountRequest, ReturnBusExceptionErrorCountRequest, ReturnSlaveMessageCountRequest, ReturnSlaveNoResponseCountRequest, ReturnSlaveNAKCountRequest, ReturnSlaveBusyCountRequest, ReturnSlaveBusCharacterOverrunCountRequest, ReturnIopOverrunCountRequest, ClearOverrunCountRequest, GetClearModbusPlusRequest, ReadDeviceInformationRequest, ] def __init__(self): ''' Initializes the client lookup tables ''' functions = set(f.function_code for f in self.__function_table) self.__lookup = dict([(f.function_code, f) for f in self.__function_table]) self.__sub_lookup = dict((f, {}) for f in functions) for f in self.__sub_function_table: self.__sub_lookup[f.function_code][f.sub_function_code] = f def decode(self, message): ''' Wrapper to decode a request packet :param message: The raw modbus request packet :return: The decoded modbus message or None if error ''' try: return self._helper(message) except ModbusException, er: _logger.warn("Unable to decode request %s" % er) return None def lookupPduClass(self, function_code): ''' Use `function_code` to determine the class of the PDU. :param function_code: The function code specified in a frame. :returns: The class of the PDU that has a matching `function_code`. ''' return self.__lookup.get(function_code, ExceptionResponse) def _helper(self, data): ''' This factory is used to generate the correct request object from a valid request packet. This decodes from a list of the currently implemented request types. :param data: The request packet to decode :returns: The decoded request or illegal function request object ''' function_code = ord(data[0]) _logger.debug("Factory Request[%d]" % function_code) request = self.__lookup.get(function_code, lambda: None)() if not request: request = IllegalFunctionRequest(function_code) request.decode(data[1:]) if hasattr(request, 'sub_function_code'): lookup = self.__sub_lookup.get(request.function_code, {}) subtype = lookup.get(request.sub_function_code, None) if subtype: request.__class__ = subtype return request #---------------------------------------------------------------------------# # Client Decoder #---------------------------------------------------------------------------# class ClientDecoder(IModbusDecoder): ''' Response Message Factory (Client) To add more implemented functions, simply add them to the list ''' __function_table = [ ReadHoldingRegistersResponse, ReadDiscreteInputsResponse, ReadInputRegistersResponse, ReadCoilsResponse, WriteMultipleCoilsResponse, WriteMultipleRegistersResponse, WriteSingleRegisterResponse, WriteSingleCoilResponse, ReadWriteMultipleRegistersResponse, DiagnosticStatusResponse, ReadExceptionStatusResponse, GetCommEventCounterResponse, GetCommEventLogResponse, ReportSlaveIdResponse, ReadFileRecordResponse, WriteFileRecordResponse, MaskWriteRegisterResponse, ReadFifoQueueResponse, ReadDeviceInformationResponse, ] __sub_function_table = [ ReturnQueryDataResponse, RestartCommunicationsOptionResponse, ReturnDiagnosticRegisterResponse, ChangeAsciiInputDelimiterResponse, ForceListenOnlyModeResponse, ClearCountersResponse, ReturnBusMessageCountResponse, ReturnBusCommunicationErrorCountResponse, ReturnBusExceptionErrorCountResponse, ReturnSlaveMessageCountResponse, ReturnSlaveNoReponseCountResponse, ReturnSlaveNAKCountResponse, ReturnSlaveBusyCountResponse, ReturnSlaveBusCharacterOverrunCountResponse, ReturnIopOverrunCountResponse, ClearOverrunCountResponse, GetClearModbusPlusResponse, ReadDeviceInformationResponse, ] def __init__(self): ''' Initializes the client lookup tables ''' functions = set(f.function_code for f in self.__function_table) self.__lookup = dict([(f.function_code, f) for f in self.__function_table]) self.__sub_lookup = dict((f, {}) for f in functions) for f in self.__sub_function_table: self.__sub_lookup[f.function_code][f.sub_function_code] = f def lookupPduClass(self, function_code): ''' Use `function_code` to determine the class of the PDU. :param function_code: The function code specified in a frame. :returns: The class of the PDU that has a matching `function_code`. ''' return self.__lookup.get(function_code, ExceptionResponse) def decode(self, message): ''' Wrapper to decode a response packet :param message: The raw packet to decode :return: The decoded modbus message or None if error ''' try: return self._helper(message) except ModbusException, er: _logger.error("Unable to decode response %s" % er) return None def _helper(self, data): ''' This factory is used to generate the correct response object from a valid response packet. This decodes from a list of the currently implemented request types. :param data: The response packet to decode :returns: The decoded request or an exception response object ''' function_code = ord(data[0]) _logger.debug("Factory Response[%d]" % function_code) response = self.__lookup.get(function_code, lambda: None)() if function_code > 0x80: code = function_code & 0x7f # strip error portion response = ExceptionResponse(code, ecode.IllegalFunction) if not response: raise ModbusException("Unknown response %d" % function_code) response.decode(data[1:]) if hasattr(response, 'sub_function_code'): lookup = self.__sub_lookup.get(response.function_code, {}) subtype = lookup.get(response.sub_function_code, None) if subtype: response.__class__ = subtype return response #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = ['ServerDecoder', 'ClientDecoder'] pymodbus/pymodbus/client/0000755000175500017550000000000012607272226015600 5ustar debacledebaclepymodbus/pymodbus/client/async.py0000644000175500017550000001716612607272152017300 0ustar debacledebacle""" Implementation of a Modbus Client Using Twisted -------------------------------------------------- Example run:: from twisted.internet import reactor, protocol from pymodbus.client.async import ModbusClientProtocol def printResult(result): print "Result: %d" % result.bits[0] def process(client): result = client.write_coil(1, True) result.addCallback(printResult) reactor.callLater(1, reactor.stop) defer = protocol.ClientCreator(reactor, ModbusClientProtocol ).connectTCP("localhost", 502) defer.addCallback(process) Another example:: from twisted.internet import reactor from pymodbus.client.async import ModbusClientFactory def process(): factory = reactor.connectTCP("localhost", 502, ModbusClientFactory()) reactor.stop() if __name__ == "__main__": reactor.callLater(1, process) reactor.run() """ from twisted.internet import defer, protocol from pymodbus.factory import ClientDecoder from pymodbus.exceptions import ConnectionException from pymodbus.transaction import ModbusSocketFramer from pymodbus.transaction import FifoTransactionManager from pymodbus.transaction import DictTransactionManager from pymodbus.client.common import ModbusClientMixin from twisted.python.failure import Failure #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # Connected Client Protocols #---------------------------------------------------------------------------# class ModbusClientProtocol(protocol.Protocol, ModbusClientMixin): ''' This represents the base modbus client protocol. All the application layer code is deferred to a higher level wrapper. ''' def __init__(self, framer=None, **kwargs): ''' Initializes the framer module :param framer: The framer to use for the protocol ''' self._connected = False self.framer = framer or ModbusSocketFramer(ClientDecoder()) if isinstance(self.framer, ModbusSocketFramer): self.transaction = DictTransactionManager(self, **kwargs) else: self.transaction = FifoTransactionManager(self, **kwargs) def connectionMade(self): ''' Called upon a successful client connection. ''' _logger.debug("Client connected to modbus server") self._connected = True def connectionLost(self, reason): ''' Called upon a client disconnect :param reason: The reason for the disconnect ''' _logger.debug("Client disconnected from modbus server: %s" % reason) self._connected = False for tid in self.transaction: self.transaction.getTransaction(tid).errback(Failure( ConnectionException('Connection lost during request'))) def dataReceived(self, data): ''' Get response, check for valid message, decode result :param data: The data returned from the server ''' self.framer.processIncomingPacket(data, self._handleResponse) def execute(self, request): ''' Starts the producer to send the next request to consumer.write(Frame(request)) ''' request.transaction_id = self.transaction.getNextTID() packet = self.framer.buildPacket(request) self.transport.write(packet) return self._buildResponse(request.transaction_id) def _handleResponse(self, reply): ''' Handle the processed response and link to correct deferred :param reply: The reply to process ''' if reply is not None: tid = reply.transaction_id handler = self.transaction.getTransaction(tid) if handler: handler.callback(reply) else: _logger.debug("Unrequested message: " + str(reply)) def _buildResponse(self, tid): ''' Helper method to return a deferred response for the current request. :param tid: The transaction identifier for this response :returns: A defer linked to the latest request ''' if not self._connected: return defer.fail(Failure( ConnectionException('Client is not connected'))) d = defer.Deferred() self.transaction.addTransaction(d, tid) return d #----------------------------------------------------------------------# # Extra Functions #----------------------------------------------------------------------# #if send_failed: # if self.retry > 0: # deferLater(clock, self.delay, send, message) # self.retry -= 1 #---------------------------------------------------------------------------# # Not Connected Client Protocol #---------------------------------------------------------------------------# class ModbusUdpClientProtocol(protocol.DatagramProtocol, ModbusClientMixin): ''' This represents the base modbus client protocol. All the application layer code is deferred to a higher level wrapper. ''' def __init__(self, framer=None, **kwargs): ''' Initializes the framer module :param framer: The framer to use for the protocol ''' self.framer = framer or ModbusSocketFramer(ClientDecoder()) if isinstance(self.framer, ModbusSocketFramer): self.transaction = DictTransactionManager(self, **kwargs) else: self.transaction = FifoTransactionManager(self, **kwargs) def datagramReceived(self, data, params): ''' Get response, check for valid message, decode result :param data: The data returned from the server :param params: The host parameters sending the datagram ''' _logger.debug("Datagram from: %s:%d" % params) self.framer.processIncomingPacket(data, self._handleResponse) def execute(self, request): ''' Starts the producer to send the next request to consumer.write(Frame(request)) ''' request.transaction_id = self.transaction.getNextTID() packet = self.framer.buildPacket(request) self.transport.write(packet) return self._buildResponse(request.transaction_id) def _handleResponse(self, reply): ''' Handle the processed response and link to correct deferred :param reply: The reply to process ''' if reply is not None: tid = reply.transaction_id handler = self.transaction.getTransaction(tid) if handler: handler.callback(reply) else: _logger.debug("Unrequested message: " + str(reply)) def _buildResponse(self, tid): ''' Helper method to return a deferred response for the current request. :param tid: The transaction identifier for this response :returns: A defer linked to the latest request ''' d = defer.Deferred() self.transaction.addTransaction(d, tid) return d #---------------------------------------------------------------------------# # Client Factories #---------------------------------------------------------------------------# class ModbusClientFactory(protocol.ReconnectingClientFactory): ''' Simple client protocol factory ''' protocol = ModbusClientProtocol #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "ModbusClientProtocol", "ModbusUdpClientProtocol", "ModbusClientFactory", ] pymodbus/pymodbus/client/sync.py0000644000175500017550000003205512607272152017131 0ustar debacledebacleimport socket import serial from pymodbus.constants import Defaults from pymodbus.factory import ClientDecoder from pymodbus.exceptions import NotImplementedException, ParameterException from pymodbus.exceptions import ConnectionException from pymodbus.transaction import FifoTransactionManager from pymodbus.transaction import DictTransactionManager from pymodbus.transaction import ModbusSocketFramer, ModbusBinaryFramer from pymodbus.transaction import ModbusAsciiFramer, ModbusRtuFramer from pymodbus.client.common import ModbusClientMixin #---------------------------------------------------------------------------# # Logging #---------------------------------------------------------------------------# import logging _logger = logging.getLogger(__name__) #---------------------------------------------------------------------------# # The Synchronous Clients #---------------------------------------------------------------------------# class BaseModbusClient(ModbusClientMixin): ''' Inteface for a modbus synchronous client. Defined here are all the methods for performing the related request methods. Derived classes simply need to implement the transport methods and set the correct framer. ''' def __init__(self, framer, **kwargs): ''' Initialize a client instance :param framer: The modbus framer implementation to use ''' self.framer = framer if isinstance(self.framer, ModbusSocketFramer): self.transaction = DictTransactionManager(self, **kwargs) else: self.transaction = FifoTransactionManager(self, **kwargs) #-----------------------------------------------------------------------# # Client interface #-----------------------------------------------------------------------# def connect(self): ''' Connect to the modbus remote host :returns: True if connection succeeded, False otherwise ''' raise NotImplementedException("Method not implemented by derived class") def close(self): ''' Closes the underlying socket connection ''' pass def _send(self, request): ''' Sends data on the underlying socket :param request: The encoded request to send :return: The number of bytes written ''' raise NotImplementedException("Method not implemented by derived class") def _recv(self, size): ''' Reads data from the underlying descriptor :param size: The number of bytes to read :return: The bytes read ''' raise NotImplementedException("Method not implemented by derived class") #-----------------------------------------------------------------------# # Modbus client methods #-----------------------------------------------------------------------# def execute(self, request=None): ''' :param request: The request to process :returns: The result of the request execution ''' if not self.connect(): raise ConnectionException("Failed to connect[%s]" % (self.__str__())) return self.transaction.execute(request) #-----------------------------------------------------------------------# # The magic methods #-----------------------------------------------------------------------# def __enter__(self): ''' Implement the client with enter block :returns: The current instance of the client ''' if not self.connect(): raise ConnectionException("Failed to connect[%s]" % (self.__str__())) return self def __exit__(self, klass, value, traceback): ''' Implement the client with exit block ''' self.close() def __str__(self): ''' Builds a string representation of the connection :returns: The string representation ''' return "Null Transport" #---------------------------------------------------------------------------# # Modbus TCP Client Transport Implementation #---------------------------------------------------------------------------# class ModbusTcpClient(BaseModbusClient): ''' Implementation of a modbus tcp client ''' def __init__(self, host='127.0.0.1', port=Defaults.Port, framer=ModbusSocketFramer, **kwargs): ''' Initialize a client instance :param host: The host to connect to (default 127.0.0.1) :param port: The modbus port to connect to (default 502) :param source_address: The source address tuple to bind to (default ('', 0)) :param framer: The modbus framer to use (default ModbusSocketFramer) .. note:: The host argument will accept ipv4 and ipv6 hosts ''' self.host = host self.port = port self.source_address = kwargs.get('source_address', ('', 0)) self.socket = None BaseModbusClient.__init__(self, framer(ClientDecoder()), **kwargs) def connect(self): ''' Connect to the modbus tcp server :returns: True if connection succeeded, False otherwise ''' if self.socket: return True try: address = (self.host, self.port) self.socket = socket.create_connection((self.host, self.port), timeout=Defaults.Timeout, source_address=self.source_address) except socket.error, msg: _logger.error('Connection to (%s, %s) failed: %s' % \ (self.host, self.port, msg)) self.close() return self.socket != None def close(self): ''' Closes the underlying socket connection ''' if self.socket: self.socket.close() self.socket = None def _send(self, request): ''' Sends data on the underlying socket :param request: The encoded request to send :return: The number of bytes written ''' if not self.socket: raise ConnectionException(self.__str__()) if request: return self.socket.send(request) return 0 def _recv(self, size): ''' Reads data from the underlying descriptor :param size: The number of bytes to read :return: The bytes read ''' if not self.socket: raise ConnectionException(self.__str__()) return self.socket.recv(size) def __str__(self): ''' Builds a string representation of the connection :returns: The string representation ''' return "%s:%s" % (self.host, self.port) #---------------------------------------------------------------------------# # Modbus UDP Client Transport Implementation #---------------------------------------------------------------------------# class ModbusUdpClient(BaseModbusClient): ''' Implementation of a modbus udp client ''' def __init__(self, host='127.0.0.1', port=Defaults.Port, framer=ModbusSocketFramer, **kwargs): ''' Initialize a client instance :param host: The host to connect to (default 127.0.0.1) :param port: The modbus port to connect to (default 502) :param framer: The modbus framer to use (default ModbusSocketFramer) :param timeout: The timeout to use for this socket (default None) ''' self.host = host self.port = port self.socket = None self.timeout = kwargs.get('timeout', None) BaseModbusClient.__init__(self, framer(ClientDecoder()), **kwargs) @classmethod def _get_address_family(cls, address): ''' A helper method to get the correct address family for a given address. :param address: The address to get the af for :returns: AF_INET for ipv4 and AF_INET6 for ipv6 ''' try: _ = socket.inet_pton(socket.AF_INET6, address) except socket.error: # not a valid ipv6 address return socket.AF_INET return socket.AF_INET6 def connect(self): ''' Connect to the modbus tcp server :returns: True if connection succeeded, False otherwise ''' if self.socket: return True try: family = ModbusUdpClient._get_address_family(self.host) self.socket = socket.socket(family, socket.SOCK_DGRAM) self.socket.settimeout(self.timeout) except socket.error, ex: _logger.error('Unable to create udp socket %s' % ex) self.close() return self.socket != None def close(self): ''' Closes the underlying socket connection ''' self.socket = None def _send(self, request): ''' Sends data on the underlying socket :param request: The encoded request to send :return: The number of bytes written ''' if not self.socket: raise ConnectionException(self.__str__()) if request: return self.socket.sendto(request, (self.host, self.port)) return 0 def _recv(self, size): ''' Reads data from the underlying descriptor :param size: The number of bytes to read :return: The bytes read ''' if not self.socket: raise ConnectionException(self.__str__()) return self.socket.recvfrom(size)[0] def __str__(self): ''' Builds a string representation of the connection :returns: The string representation ''' return "%s:%s" % (self.host, self.port) #---------------------------------------------------------------------------# # Modbus Serial Client Transport Implementation #---------------------------------------------------------------------------# class ModbusSerialClient(BaseModbusClient): ''' Implementation of a modbus serial client ''' def __init__(self, method='ascii', **kwargs): ''' Initialize a serial client instance The methods to connect are:: - ascii - rtu - binary :param method: The method to use for connection :param port: The serial port to attach to :param stopbits: The number of stop bits to use :param bytesize: The bytesize of the serial messages :param parity: Which kind of parity to use :param baudrate: The baud rate to use for the serial device :param timeout: The timeout between serial requests (default 3s) ''' self.method = method self.socket = None BaseModbusClient.__init__(self, self.__implementation(method), **kwargs) self.port = kwargs.get('port', 0) self.stopbits = kwargs.get('stopbits', Defaults.Stopbits) self.bytesize = kwargs.get('bytesize', Defaults.Bytesize) self.parity = kwargs.get('parity', Defaults.Parity) self.baudrate = kwargs.get('baudrate', Defaults.Baudrate) self.timeout = kwargs.get('timeout', Defaults.Timeout) @staticmethod def __implementation(method): ''' Returns the requested framer :method: The serial framer to instantiate :returns: The requested serial framer ''' method = method.lower() if method == 'ascii': return ModbusAsciiFramer(ClientDecoder()) elif method == 'rtu': return ModbusRtuFramer(ClientDecoder()) elif method == 'binary': return ModbusBinaryFramer(ClientDecoder()) elif method == 'socket': return ModbusSocketFramer(ClientDecoder()) raise ParameterException("Invalid framer method requested") def connect(self): ''' Connect to the modbus serial server :returns: True if connection succeeded, False otherwise ''' if self.socket: return True try: self.socket = serial.Serial(port=self.port, timeout=self.timeout, bytesize=self.bytesize, stopbits=self.stopbits, baudrate=self.baudrate, parity=self.parity) except serial.SerialException, msg: _logger.error(msg) self.close() return self.socket != None def close(self): ''' Closes the underlying socket connection ''' if self.socket: self.socket.close() self.socket = None def _send(self, request): ''' Sends data on the underlying socket :param request: The encoded request to send :return: The number of bytes written ''' if not self.socket: raise ConnectionException(self.__str__()) if request: return self.socket.write(request) return 0 def _recv(self, size): ''' Reads data from the underlying descriptor :param size: The number of bytes to read :return: The bytes read ''' if not self.socket: raise ConnectionException(self.__str__()) return self.socket.read(size) def __str__(self): ''' Builds a string representation of the connection :returns: The string representation ''' return "%s baud[%s]" % (self.method, self.baudrate) #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "ModbusTcpClient", "ModbusUdpClient", "ModbusSerialClient" ] pymodbus/pymodbus/client/__init__.py0000644000175500017550000000000012607272152017675 0ustar debacledebaclepymodbus/pymodbus/client/common.py0000644000175500017550000001262612607272226017451 0ustar debacledebacle''' Modbus Client Common ---------------------------------- This is a common client mixin that can be used by both the synchronous and asynchronous clients to simplify the interface. ''' from pymodbus.bit_read_message import * from pymodbus.bit_write_message import * from pymodbus.register_read_message import * from pymodbus.register_write_message import * from pymodbus.diag_message import * from pymodbus.file_message import * from pymodbus.other_message import * class ModbusClientMixin(object): ''' This is a modbus client mixin that provides additional factory methods for all the current modbus methods. This can be used instead of the normal pattern of:: # instead of this client = ModbusClient(...) request = ReadCoilsRequest(1,10) response = client.execute(request) # now like this client = ModbusClient(...) response = client.read_coils(1, 10) ''' def read_coils(self, address, count=1, **kwargs): ''' :param address: The starting address to read from :param count: The number of coils to read :param unit: The slave unit this request is targeting :returns: A deferred response handle ''' request = ReadCoilsRequest(address, count, **kwargs) return self.execute(request) def read_discrete_inputs(self, address, count=1, **kwargs): ''' :param address: The starting address to read from :param count: The number of discretes to read :param unit: The slave unit this request is targeting :returns: A deferred response handle ''' request = ReadDiscreteInputsRequest(address, count, **kwargs) return self.execute(request) def write_coil(self, address, value, **kwargs): ''' :param address: The starting address to write to :param value: The value to write to the specified address :param unit: The slave unit this request is targeting :returns: A deferred response handle ''' request = WriteSingleCoilRequest(address, value, **kwargs) return self.execute(request) def write_coils(self, address, values, **kwargs): ''' :param address: The starting address to write to :param values: The values to write to the specified address :param unit: The slave unit this request is targeting :returns: A deferred response handle ''' request = WriteMultipleCoilsRequest(address, values, **kwargs) return self.execute(request) def write_register(self, address, value, **kwargs): ''' :param address: The starting address to write to :param value: The value to write to the specified address :param unit: The slave unit this request is targeting :returns: A deferred response handle ''' request = WriteSingleRegisterRequest(address, value, **kwargs) return self.execute(request) def write_registers(self, address, values, **kwargs): ''' :param address: The starting address to write to :param values: The values to write to the specified address :param unit: The slave unit this request is targeting :returns: A deferred response handle ''' request = WriteMultipleRegistersRequest(address, values, **kwargs) return self.execute(request) def read_holding_registers(self, address, count=1, **kwargs): ''' :param address: The starting address to read from :param count: The number of registers to read :param unit: The slave unit this request is targeting :returns: A deferred response handle ''' request = ReadHoldingRegistersRequest(address, count, **kwargs) return self.execute(request) def read_input_registers(self, address, count=1, **kwargs): ''' :param address: The starting address to read from :param count: The number of registers to read :param unit: The slave unit this request is targeting :returns: A deferred response handle ''' request = ReadInputRegistersRequest(address, count, **kwargs) return self.execute(request) def readwrite_registers(self, *args, **kwargs): ''' :param read_address: The address to start reading from :param read_count: The number of registers to read from address :param write_address: The address to start writing to :param write_registers: The registers to write to the specified address :param unit: The slave unit this request is targeting :returns: A deferred response handle ''' request = ReadWriteMultipleRegistersRequest(*args, **kwargs) return self.execute(request) def mask_write_register(self, *args, **kwargs): ''' :param address: The address of the register to write :param and_mask: The and bitmask to apply to the register address :param or_mask: The or bitmask to apply to the register address :param unit: The slave unit this request is targeting :returns: A deferred response handle ''' request = MaskWriteRegisterRequest(*args, **kwargs) return self.execute(request) #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ 'ModbusClientMixin' ] pymodbus/pymodbus/exceptions.py0000644000175500017550000000464512607272152017064 0ustar debacledebacle''' Pymodbus Exceptions -------------------- Custom exceptions to be used in the Modbus code. ''' class ModbusException(Exception): ''' Base modbus exception ''' def __init__(self, string): ''' Initialize the exception :param string: The message to append to the error ''' self.string = string def __str__(self): return 'Modbus Error: %s' % self.string class ModbusIOException(ModbusException): ''' Error resulting from data i/o ''' def __init__(self, string=""): ''' Initialize the exception :param string: The message to append to the error ''' message = "[Input/Output] %s" % string ModbusException.__init__(self, message) class ParameterException(ModbusException): ''' Error resulting from invalid parameter ''' def __init__(self, string=""): ''' Initialize the exception :param string: The message to append to the error ''' message = "[Invalid Parameter] %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 ModbusException.__init__(self, message) class ConnectionException(ModbusException): ''' Error resulting from a bad connection ''' def __init__(self, string=""): ''' Initialize the exception :param string: The message to append to the error ''' message = "[Connection] %s" % string ModbusException.__init__(self, message) #---------------------------------------------------------------------------# # Exported symbols #---------------------------------------------------------------------------# __all__ = [ "ModbusException", "ModbusIOException", "ParameterException", "NotImplementedException", "ConnectionException", "NoSuchSlaveException", ] pymodbus/requirements.txt0000644000175500017550000000200012607272152015732 0ustar debacledebacle# ------------------------------------------------------------------- # if want to use the pymodbus serial stack, uncomment these # ------------------------------------------------------------------- #pyserial==2.6 # ------------------------------------------------------------------- # if you want to run the tests and code coverage, uncomment these # ------------------------------------------------------------------- #coverage==3.5.3 #mock==1.0b1 #nose==1.2.1 #pep8==1.3.3 # ------------------------------------------------------------------- # if you want to use the asynchronous version, uncomment these # ------------------------------------------------------------------- #Twisted==12.2.0 #zope.interface==4.0.1 #pyasn1==0.1.4 #pycrypto==2.6 #wsgiref==0.1.2 # ------------------------------------------------------------------- # if you want to build the documentation, uncomment these # ------------------------------------------------------------------- #Jinja2==2.6 #Pygments==1.5 #Sphinx==1.1.3 #docutils==0.9.1 pymodbus/doc/0000755000175500017550000000000012607272152013223 5ustar debacledebaclepymodbus/doc/quality/0000755000175500017550000000000012607272152014713 5ustar debacledebaclepymodbus/doc/quality/current.coverage0000644000175500017550000000451112607272152020113 0ustar debacledebacleName Stmts Miss Cover Missing --------------------------------------------------------------- pymodbus 15 6 60% 24-27, 36-37 pymodbus.bit_read_message 68 0 100% pymodbus.bit_write_message 95 0 100% pymodbus.client 0 0 100% pymodbus.client.async 70 0 100% pymodbus.client.common 36 0 100% pymodbus.client.sync 147 0 100% pymodbus.constants 36 0 100% pymodbus.datastore 5 0 100% pymodbus.datastore.context 50 0 100% pymodbus.datastore.remote 31 0 100% pymodbus.datastore.store 67 0 100% pymodbus.device 159 0 100% pymodbus.diag_message 202 0 100% pymodbus.events 60 0 100% pymodbus.exceptions 22 0 100% pymodbus.factory 77 0 100% pymodbus.file_message 181 0 100% pymodbus.interfaces 46 0 100% pymodbus.internal 0 0 100% pymodbus.internal.ptwisted 16 2 88% 29-30 pymodbus.mei_message 70 0 100% pymodbus.other_message 145 0 100% pymodbus.payload 140 2 99% 205, 224 pymodbus.pdu 72 0 100% pymodbus.register_read_message 124 0 100% pymodbus.register_write_message 91 2 98% 39, 148 pymodbus.server 0 0 100% pymodbus.server.async 113 39 65% 55-58, 65-74, 81-86, 151-156, 163-172, 180-184 pymodbus.server.sync 186 0 100% pymodbus.transaction 275 53 81% 63-81, 116-117, 259, 263, 403, 433-442, 577-586, 656, 733-742, 768-769 pymodbus.utilities 67 0 100% pymodbus.version 13 0 100% --------------------------------------------------------------- TOTAL 2679 104 96% ---------------------------------------------------------------------- Ran 255 tests in 0.981s OK pymodbus/doc/quality/current.pep80000644000175500017550000011747612607272152017213 0ustar debacledebaclerunning pep8 pymodbus/__init__.py:16:11: E221 multiple spaces before operator pymodbus/bit_read_message.py:26:19: E221 multiple spaces before operator pymodbus/bit_read_message.py:143:80: E501 line too long (80 characters) pymodbus/bit_read_message.py:202:80: E501 line too long (80 characters) pymodbus/bit_write_message.py:19:14: E221 multiple spaces before operator pymodbus/bit_write_message.py:58:15: E221 multiple spaces before operator pymodbus/bit_write_message.py:59:22: E701 multiple statements on one line (colon) pymodbus/bit_write_message.py:60:13: E701 multiple statements on one line (colon) pymodbus/bit_write_message.py:117:15: E221 multiple spaces before operator pymodbus/bit_write_message.py:118:22: E701 multiple statements on one line (colon) pymodbus/bit_write_message.py:119:13: E701 multiple statements on one line (colon) pymodbus/bit_write_message.py:160:22: E701 multiple statements on one line (colon) pymodbus/bit_write_message.py:161:45: E701 multiple statements on one line (colon) pymodbus/bit_write_message.py:162:20: E221 multiple spaces before operator pymodbus/bit_write_message.py:170:14: E221 multiple spaces before operator pymodbus/bit_write_message.py:172:15: E221 multiple spaces before operator pymodbus/constants.py:74:9: E221 multiple spaces before operator pymodbus/constants.py:75:12: E221 multiple spaces before operator pymodbus/constants.py:76:12: E221 multiple spaces before operator pymodbus/constants.py:77:15: E221 multiple spaces before operator pymodbus/constants.py:79:15: E221 multiple spaces before operator pymodbus/constants.py:80:11: E221 multiple spaces before operator pymodbus/constants.py:81:13: E221 multiple spaces before operator pymodbus/constants.py:82:11: E221 multiple spaces before operator pymodbus/constants.py:83:13: E221 multiple spaces before operator pymodbus/constants.py:84:13: E221 multiple spaces before operator pymodbus/constants.py:118:12: E221 multiple spaces before operator pymodbus/constants.py:119:10: E221 multiple spaces before operator pymodbus/constants.py:120:7: E221 multiple spaces before operator pymodbus/constants.py:121:8: E221 multiple spaces before operator pymodbus/constants.py:122:12: E221 multiple spaces before operator pymodbus/constants.py:145:9: E221 multiple spaces before operator pymodbus/constants.py:146:8: E221 multiple spaces before operator pymodbus/constants.py:163:18: E221 multiple spaces before operator pymodbus/constants.py:193:10: E221 multiple spaces before operator pymodbus/constants.py:194:12: E221 multiple spaces before operator pymodbus/constants.py:210:12: E221 multiple spaces before operator pymodbus/device.py:30:13: E126 continuation line over-indented for hanging indent pymodbus/device.py:88:41: E203 whitespace before ':' pymodbus/device.py:89:41: E203 whitespace before ':' pymodbus/device.py:90:41: E203 whitespace before ':' pymodbus/device.py:91:41: E203 whitespace before ':' pymodbus/device.py:92:41: E203 whitespace before ':' pymodbus/device.py:93:41: E203 whitespace before ':' pymodbus/device.py:94:41: E203 whitespace before ':' pymodbus/device.py:96:41: E203 whitespace before ':' pymodbus/device.py:97:41: E203 whitespace before ':' pymodbus/device.py:98:41: E203 whitespace before ':' pymodbus/device.py:99:41: E203 whitespace before ':' pymodbus/device.py:100:41: E203 whitespace before ':' pymodbus/device.py:101:41: E203 whitespace before ':' pymodbus/device.py:102:41: E203 whitespace before ':' pymodbus/device.py:103:41: E203 whitespace before ':' pymodbus/device.py:105:41: E203 whitespace before ':' pymodbus/device.py:106:41: E203 whitespace before ':' pymodbus/device.py:107:41: E203 whitespace before ':' pymodbus/device.py:108:41: E203 whitespace before ':' pymodbus/device.py:109:41: E203 whitespace before ':' pymodbus/device.py:110:41: E203 whitespace before ':' pymodbus/device.py:111:41: E203 whitespace before ':' pymodbus/device.py:112:41: E203 whitespace before ':' pymodbus/device.py:113:41: E203 whitespace before ':' pymodbus/device.py:114:41: E203 whitespace before ':' pymodbus/device.py:115:41: E203 whitespace before ':' pymodbus/device.py:116:41: E203 whitespace before ':' pymodbus/device.py:117:41: E203 whitespace before ':' pymodbus/device.py:118:41: E203 whitespace before ':' pymodbus/device.py:120:41: E203 whitespace before ':' pymodbus/device.py:121:41: E203 whitespace before ':' pymodbus/device.py:122:41: E203 whitespace before ':' pymodbus/device.py:123:41: E203 whitespace before ':' pymodbus/device.py:124:41: E203 whitespace before ':' pymodbus/device.py:125:41: E203 whitespace before ':' pymodbus/device.py:126:41: E203 whitespace before ':' pymodbus/device.py:127:41: E203 whitespace before ':' pymodbus/device.py:128:41: E203 whitespace before ':' pymodbus/device.py:129:41: E203 whitespace before ':' pymodbus/device.py:131:41: E203 whitespace before ':' pymodbus/device.py:132:41: E203 whitespace before ':' pymodbus/device.py:133:41: E203 whitespace before ':' pymodbus/device.py:134:41: E203 whitespace before ':' pymodbus/device.py:135:41: E203 whitespace before ':' pymodbus/device.py:136:41: E203 whitespace before ':' pymodbus/device.py:137:41: E203 whitespace before ':' pymodbus/device.py:138:41: E203 whitespace before ':' pymodbus/device.py:88:55: E261 at least two spaces before inline comment pymodbus/device.py:89:55: E261 at least two spaces before inline comment pymodbus/device.py:90:55: E261 at least two spaces before inline comment pymodbus/device.py:91:55: E261 at least two spaces before inline comment pymodbus/device.py:92:55: E261 at least two spaces before inline comment pymodbus/device.py:93:55: E261 at least two spaces before inline comment pymodbus/device.py:94:55: E261 at least two spaces before inline comment pymodbus/device.py:131:55: E261 at least two spaces before inline comment pymodbus/device.py:132:55: E261 at least two spaces before inline comment pymodbus/device.py:133:55: E261 at least two spaces before inline comment pymodbus/device.py:134:55: E261 at least two spaces before inline comment pymodbus/device.py:135:55: E261 at least two spaces before inline comment pymodbus/device.py:136:55: E261 at least two spaces before inline comment pymodbus/device.py:137:55: E261 at least two spaces before inline comment pymodbus/device.py:138:55: E261 at least two spaces before inline comment pymodbus/device.py:175:53: E225 missing whitespace around operator pymodbus/device.py:273:15: E221 multiple spaces before operator pymodbus/device.py:274:16: E221 multiple spaces before operator pymodbus/device.py:275:23: E221 multiple spaces before operator pymodbus/device.py:276:14: E221 multiple spaces before operator pymodbus/device.py:277:16: E221 multiple spaces before operator pymodbus/device.py:278:14: E221 multiple spaces before operator pymodbus/device.py:289:80: E501 line too long (81 characters) pymodbus/device.py:290:80: E501 line too long (81 characters) pymodbus/device.py:289:45: E231 missing whitespace after ',' pymodbus/device.py:289:47: E231 missing whitespace after ',' pymodbus/device.py:290:45: E231 missing whitespace after ',' pymodbus/device.py:290:47: E231 missing whitespace after ',' pymodbus/device.py:291:45: E231 missing whitespace after ',' pymodbus/device.py:291:47: E231 missing whitespace after ',' pymodbus/device.py:292:45: E231 missing whitespace after ',' pymodbus/device.py:292:47: E231 missing whitespace after ',' pymodbus/device.py:289:33: E272 multiple spaces before keyword pymodbus/device.py:290:35: E272 multiple spaces before keyword pymodbus/device.py:315:17: E201 whitespace after '{' pymodbus/device.py:315:47: E202 whitespace before '}' pymodbus/device.py:315:27: E231 missing whitespace after ':' pymodbus/device.py:401:12: E221 multiple spaces before operator pymodbus/device.py:442:25: E701 multiple statements on one line (colon) pymodbus/device.py:449:15: E221 multiple spaces before operator pymodbus/device.py:451:22: E221 multiple spaces before operator pymodbus/device.py:452:17: E221 multiple spaces before operator pymodbus/device.py:453:20: E221 multiple spaces before operator pymodbus/device.py:454:13: E221 multiple spaces before operator pymodbus/device.py:455:14: E221 multiple spaces before operator pymodbus/device.py:456:24: E221 multiple spaces before operator pymodbus/device.py:457:10: E221 multiple spaces before operator pymodbus/device.py:478:11: E221 multiple spaces before operator pymodbus/device.py:479:13: E221 multiple spaces before operator pymodbus/device.py:527:12: E221 multiple spaces before operator pymodbus/device.py:528:11: E221 multiple spaces before operator pymodbus/device.py:529:9: E221 multiple spaces before operator pymodbus/device.py:612:9: E126 continuation line over-indented for hanging indent pymodbus/diag_message.py:135:80: E501 line too long (81 characters) pymodbus/diag_message.py:174:13: E701 multiple statements on one line (colon) pymodbus/diag_message.py:200:13: E701 multiple statements on one line (colon) pymodbus/diag_message.py:224:25: E221 multiple spaces before operator pymodbus/diag_message.py:225:13: E701 multiple statements on one line (colon) pymodbus/diag_message.py:254:25: E221 multiple spaces before operator pymodbus/diag_message.py:255:13: E701 multiple statements on one line (colon) pymodbus/diag_message.py:349:19: E221 multiple spaces before operator pymodbus/diag_message.py:593:80: E501 line too long (80 characters) pymodbus/diag_message.py:596:80: E501 line too long (80 characters) pymodbus/diag_message.py:612:80: E501 line too long (82 characters) pymodbus/diag_message.py:615:80: E501 line too long (80 characters) pymodbus/diag_message.py:702:23: E261 at least two spaces before inline comment pymodbus/diag_message.py:705:13: E701 multiple statements on one line (colon) pymodbus/diag_message.py:725:80: E501 line too long (80 characters) pymodbus/diag_message.py:731:80: E501 line too long (90 characters) pymodbus/diag_message.py:732:80: E501 line too long (82 characters) pymodbus/diag_message.py:737:80: E501 line too long (96 characters) pymodbus/events.py:54:21: E221 multiple spaces before operator pymodbus/events.py:55:20: E221 multiple spaces before operator pymodbus/events.py:63:13: E221 multiple spaces before operator pymodbus/events.py:74:21: E221 multiple spaces before operator pymodbus/events.py:75:20: E221 multiple spaces before operator pymodbus/events.py:105:18: E221 multiple spaces before operator pymodbus/events.py:106:25: E221 multiple spaces before operator pymodbus/events.py:107:24: E221 multiple spaces before operator pymodbus/events.py:108:23: E221 multiple spaces before operator pymodbus/events.py:110:20: E221 multiple spaces before operator pymodbus/events.py:118:13: E128 continuation line under-indented for visual indent pymodbus/events.py:119:13: E221 multiple spaces before operator pymodbus/events.py:130:18: E221 multiple spaces before operator pymodbus/events.py:131:25: E221 multiple spaces before operator pymodbus/events.py:132:24: E221 multiple spaces before operator pymodbus/events.py:133:23: E221 multiple spaces before operator pymodbus/events.py:135:20: E221 multiple spaces before operator pymodbus/factory.py:44:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:45:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:46:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:47:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:48:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:49:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:50:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:51:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:52:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:54:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:56:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:57:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:58:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:59:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:61:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:62:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:63:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:64:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:66:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:69:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:70:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:71:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:72:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:73:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:74:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:75:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:76:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:77:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:78:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:79:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:80:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:81:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:82:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:83:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:84:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:85:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:87:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:94:80: E501 line too long (83 characters) pymodbus/factory.py:138:23: E701 multiple statements on one line (colon) pymodbus/factory.py:152:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:153:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:154:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:155:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:156:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:157:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:158:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:159:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:160:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:162:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:164:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:165:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:166:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:167:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:169:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:170:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:171:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:172:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:174:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:177:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:178:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:179:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:180:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:181:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:182:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:183:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:184:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:185:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:186:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:187:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:188:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:189:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:190:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:191:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:192:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:193:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:195:13: E126 continuation line over-indented for hanging indent pymodbus/factory.py:202:80: E501 line too long (83 characters) pymodbus/factory.py:249:23: E701 multiple statements on one line (colon) pymodbus/file_message.py:30:28: E221 multiple spaces before operator pymodbus/file_message.py:31:25: E221 multiple spaces before operator pymodbus/file_message.py:32:27: E221 multiple spaces before operator pymodbus/file_message.py:33:25: E221 multiple spaces before operator pymodbus/file_message.py:34:80: E501 line too long (87 characters) pymodbus/file_message.py:34:27: E221 multiple spaces before operator pymodbus/file_message.py:35:80: E501 line too long (87 characters) pymodbus/file_message.py:41:12: E127 continuation line over-indented for visual indent pymodbus/file_message.py:42:12: E127 continuation line over-indented for visual indent pymodbus/file_message.py:43:12: E127 continuation line over-indented for visual indent pymodbus/file_message.py:44:12: E127 continuation line over-indented for visual indent pymodbus/file_message.py:41:32: E221 multiple spaces before operator pymodbus/file_message.py:42:34: E221 multiple spaces before operator pymodbus/file_message.py:43:34: E221 multiple spaces before operator pymodbus/file_message.py:44:32: E221 multiple spaces before operator pymodbus/file_message.py:92:21: E221 multiple spaces before operator pymodbus/file_message.py:102:17: E128 continuation line under-indented for visual indent pymodbus/file_message.py:113:62: E225 missing whitespace around operator pymodbus/file_message.py:115:17: E128 continuation line under-indented for visual indent pymodbus/file_message.py:114:19: E221 multiple spaces before operator pymodbus/file_message.py:116:34: E701 multiple statements on one line (colon) pymodbus/file_message.py:154:14: E221 multiple spaces before operator pymodbus/file_message.py:169:80: E501 line too long (87 characters) pymodbus/file_message.py:169:84: E225 missing whitespace around operator pymodbus/file_message.py:170:41: E261 at least two spaces before inline comment pymodbus/file_message.py:172:17: E128 continuation line under-indented for visual indent pymodbus/file_message.py:173:38: E701 multiple statements on one line (colon) pymodbus/file_message.py:192:21: E221 multiple spaces before operator pymodbus/file_message.py:199:80: E501 line too long (85 characters) pymodbus/file_message.py:203:17: E128 continuation line under-indented for visual indent pymodbus/file_message.py:215:62: E225 missing whitespace around operator pymodbus/file_message.py:217:18: E221 multiple spaces before operator pymodbus/file_message.py:219:17: E128 continuation line under-indented for visual indent pymodbus/file_message.py:220:17: E128 continuation line under-indented for visual indent pymodbus/file_message.py:218:19: E221 multiple spaces before operator pymodbus/file_message.py:221:34: E701 multiple statements on one line (colon) pymodbus/file_message.py:248:21: E221 multiple spaces before operator pymodbus/file_message.py:255:80: E501 line too long (85 characters) pymodbus/file_message.py:259:17: E128 continuation line under-indented for visual indent pymodbus/file_message.py:271:62: E225 missing whitespace around operator pymodbus/file_message.py:273:18: E221 multiple spaces before operator pymodbus/file_message.py:275:17: E128 continuation line under-indented for visual indent pymodbus/file_message.py:276:17: E128 continuation line under-indented for visual indent pymodbus/file_message.py:274:19: E221 multiple spaces before operator pymodbus/file_message.py:277:34: E701 multiple statements on one line (colon) pymodbus/file_message.py:298:21: E221 multiple spaces before operator pymodbus/file_message.py:300:21: E221 multiple spaces before operator pymodbus/file_message.py:331:80: E501 line too long (83 characters) pymodbus/file_message.py:350:21: E221 multiple spaces before operator pymodbus/file_message.py:352:21: E221 multiple spaces before operator pymodbus/mei_message.py:43:13: E128 continuation line under-indented for visual indent pymodbus/mei_message.py:66:13: E128 continuation line under-indented for visual indent pymodbus/mei_message.py:91:17: E261 at least two spaces before inline comment pymodbus/mei_message.py:94:69: E225 missing whitespace around operator pymodbus/mei_message.py:108:31: E261 at least two spaces before inline comment pymodbus/mei_message.py:111:35: E261 at least two spaces before inline comment pymodbus/mei_message.py:120:13: E128 continuation line under-indented for visual indent pymodbus/mei_message.py:121:13: E128 continuation line under-indented for visual indent pymodbus/mei_message.py:138:40: E261 at least two spaces before inline comment pymodbus/mei_message.py:141:80: E501 line too long (80 characters) pymodbus/mei_message.py:141:77: E225 missing whitespace around operator pymodbus/mei_message.py:143:53: E225 missing whitespace around operator pymodbus/other_message.py:187:23: E701 multiple statements on one line (colon) pymodbus/other_message.py:188:13: E701 multiple statements on one line (colon) pymodbus/other_message.py:257:28: E203 whitespace before ':' pymodbus/other_message.py:258:28: E203 whitespace before ':' pymodbus/other_message.py:259:28: E203 whitespace before ':' pymodbus/other_message.py:260:28: E203 whitespace before ':' pymodbus/other_message.py:301:23: E701 multiple statements on one line (colon) pymodbus/other_message.py:302:13: E701 multiple statements on one line (colon) pymodbus/other_message.py:303:15: E221 multiple spaces before operator pymodbus/other_message.py:329:80: E501 line too long (91 characters) pymodbus/other_message.py:400:23: E701 multiple statements on one line (colon) pymodbus/other_message.py:401:13: E701 multiple statements on one line (colon) pymodbus/payload.py:36:21: E221 multiple spaces before operator pymodbus/payload.py:61:27: E225 missing whitespace around operator pymodbus/payload.py:186:21: E221 multiple spaces before operator pymodbus/pdu.py:79:13: E701 multiple statements on one line (colon) pymodbus/pdu.py:97:17: E128 continuation line under-indented for visual indent pymodbus/pdu.py:129:20: E221 multiple spaces before operator pymodbus/pdu.py:130:19: E221 multiple spaces before operator pymodbus/pdu.py:131:17: E221 multiple spaces before operator pymodbus/pdu.py:132:17: E221 multiple spaces before operator pymodbus/pdu.py:133:16: E221 multiple spaces before operator pymodbus/pdu.py:134:14: E221 multiple spaces before operator pymodbus/pdu.py:135:22: E221 multiple spaces before operator pymodbus/pdu.py:136:27: E221 multiple spaces before operator pymodbus/pdu.py:137:22: E221 multiple spaces before operator pymodbus/register_read_message.py:128:80: E501 line too long (80 characters) pymodbus/register_read_message.py:178:80: E501 line too long (80 characters) pymodbus/register_read_message.py:226:26: E221 multiple spaces before operator pymodbus/register_read_message.py:227:24: E221 multiple spaces before operator pymodbus/register_read_message.py:228:27: E221 multiple spaces before operator pymodbus/register_read_message.py:241:17: E128 continuation line under-indented for visual indent pymodbus/register_read_message.py:242:17: E128 continuation line under-indented for visual indent pymodbus/register_read_message.py:241:54: E502 the backslash is redundant between brackets pymodbus/register_read_message.py:253:9: E122 continuation line missing indentation or outdented pymodbus/register_read_message.py:254:9: E122 continuation line missing indentation or outdented pymodbus/register_read_message.py:255:29: E221 multiple spaces before operator pymodbus/register_write_message.py:133:22: E701 multiple statements on one line (colon) pymodbus/register_write_message.py:134:45: E701 multiple statements on one line (colon) pymodbus/register_write_message.py:155:9: E122 continuation line missing indentation or outdented pymodbus/transaction.py:9:24: E272 multiple spaces before keyword pymodbus/transaction.py:11:24: E272 multiple spaces before keyword pymodbus/transaction.py:12:24: E272 multiple spaces before keyword pymodbus/transaction.py:66:80: E501 line too long (85 characters) pymodbus/transaction.py:67:22: E702 multiple statements on one line (semicolon) pymodbus/transaction.py:146:31: E231 missing whitespace after ':' pymodbus/transaction.py:146:40: E231 missing whitespace after ':' pymodbus/transaction.py:146:49: E231 missing whitespace after ':' pymodbus/transaction.py:146:58: E231 missing whitespace after ':' pymodbus/transaction.py:147:21: E221 multiple spaces before operator pymodbus/transaction.py:148:21: E221 multiple spaces before operator pymodbus/transaction.py:159:13: E122 continuation line missing indentation or outdented pymodbus/transaction.py:160:21: E126 continuation line over-indented for hanging indent pymodbus/transaction.py:179:31: E231 missing whitespace after ':' pymodbus/transaction.py:179:40: E231 missing whitespace after ':' pymodbus/transaction.py:179:49: E231 missing whitespace after ':' pymodbus/transaction.py:179:58: E231 missing whitespace after ':' pymodbus/transaction.py:244:17: E701 multiple statements on one line (colon) pymodbus/transaction.py:253:13: E128 continuation line under-indented for visual indent pymodbus/transaction.py:254:13: E128 continuation line under-indented for visual indent pymodbus/transaction.py:255:13: E128 continuation line under-indented for visual indent pymodbus/transaction.py:305:21: E221 multiple spaces before operator pymodbus/transaction.py:306:19: E221 multiple spaces before operator pymodbus/transaction.py:308:21: E221 multiple spaces before operator pymodbus/transaction.py:380:14: E221 multiple spaces before operator pymodbus/transaction.py:381:12: E221 multiple spaces before operator pymodbus/transaction.py:383:19: E701 multiple statements on one line (colon) pymodbus/transaction.py:423:17: E701 multiple statements on one line (colon) pymodbus/transaction.py:432:13: E128 continuation line under-indented for visual indent pymodbus/transaction.py:433:13: E128 continuation line under-indented for visual indent pymodbus/transaction.py:463:31: E231 missing whitespace after ':' pymodbus/transaction.py:463:45: E231 missing whitespace after ':' pymodbus/transaction.py:463:54: E231 missing whitespace after ':' pymodbus/transaction.py:464:21: E221 multiple spaces before operator pymodbus/transaction.py:465:21: E221 multiple spaces before operator pymodbus/transaction.py:466:19: E221 multiple spaces before operator pymodbus/transaction.py:467:21: E221 multiple spaces before operator pymodbus/transaction.py:478:23: E701 multiple statements on one line (colon) pymodbus/transaction.py:479:21: E203 whitespace before ':' pymodbus/transaction.py:499:31: E231 missing whitespace after ':' pymodbus/transaction.py:499:45: E231 missing whitespace after ':' pymodbus/transaction.py:499:54: E231 missing whitespace after ':' pymodbus/transaction.py:524:14: E221 multiple spaces before operator pymodbus/transaction.py:525:12: E221 multiple spaces before operator pymodbus/transaction.py:527:19: E701 multiple statements on one line (colon) pymodbus/transaction.py:567:17: E701 multiple statements on one line (colon) pymodbus/transaction.py:576:16: E221 multiple spaces before operator pymodbus/transaction.py:577:15: E221 multiple spaces before operator pymodbus/transaction.py:620:31: E231 missing whitespace after ':' pymodbus/transaction.py:620:45: E231 missing whitespace after ':' pymodbus/transaction.py:620:54: E231 missing whitespace after ':' pymodbus/transaction.py:621:21: E221 multiple spaces before operator pymodbus/transaction.py:622:21: E221 multiple spaces before operator pymodbus/transaction.py:623:19: E221 multiple spaces before operator pymodbus/transaction.py:624:21: E221 multiple spaces before operator pymodbus/transaction.py:635:23: E701 multiple statements on one line (colon) pymodbus/transaction.py:636:21: E203 whitespace before ':' pymodbus/transaction.py:643:80: E501 line too long (85 characters) pymodbus/transaction.py:655:31: E231 missing whitespace after ':' pymodbus/transaction.py:655:45: E231 missing whitespace after ':' pymodbus/transaction.py:655:54: E231 missing whitespace after ':' pymodbus/transaction.py:680:14: E221 multiple spaces before operator pymodbus/transaction.py:681:12: E221 multiple spaces before operator pymodbus/transaction.py:683:19: E701 multiple statements on one line (colon) pymodbus/transaction.py:723:17: E701 multiple statements on one line (colon) pymodbus/transaction.py:733:13: E128 continuation line under-indented for visual indent pymodbus/transaction.py:734:13: E128 continuation line under-indented for visual indent pymodbus/transaction.py:749:31: E701 multiple statements on one line (colon) pymodbus/transaction.py:750:17: E701 multiple statements on one line (colon) pymodbus/utilities.py:64:15: E701 multiple statements on one line (colon) pymodbus/utilities.py:69:13: E701 multiple statements on one line (colon) pymodbus/utilities.py:110:17: E701 multiple statements on one line (colon) pymodbus/utilities.py:131:51: E702 multiple statements on one line (semicolon) pymodbus/client/async.py:115:17: E701 multiple statements on one line (colon) pymodbus/client/async.py:130:32: E261 at least two spaces before inline comment pymodbus/client/async.py:187:17: E701 multiple statements on one line (colon) pymodbus/client/async.py:198:32: E261 at least two spaces before inline comment pymodbus/client/sync.py:47:80: E501 line too long (80 characters) pymodbus/client/sync.py:60:80: E501 line too long (80 characters) pymodbus/client/sync.py:68:80: E501 line too long (80 characters) pymodbus/client/sync.py:79:80: E501 line too long (81 characters) pymodbus/client/sync.py:93:80: E501 line too long (81 characters) pymodbus/client/sync.py:137:23: E701 multiple statements on one line (colon) pymodbus/client/sync.py:139:80: E501 line too long (92 characters) pymodbus/client/sync.py:143:17: E128 continuation line under-indented for visual indent pymodbus/client/sync.py:142:65: E502 the backslash is redundant between brackets pymodbus/client/sync.py:145:28: E711 comparison to None should be 'if cond is not None:' pymodbus/client/sync.py:212:29: E261 at least two spaces before inline comment pymodbus/client/sync.py:221:23: E701 multiple statements on one line (colon) pymodbus/client/sync.py:228:28: E711 comparison to None should be 'if cond is not None:' pymodbus/client/sync.py:289:20: E221 multiple spaces before operator pymodbus/client/sync.py:290:20: E221 multiple spaces before operator pymodbus/client/sync.py:293:18: E221 multiple spaces before operator pymodbus/client/sync.py:296:20: E221 multiple spaces before operator pymodbus/client/sync.py:298:21: E221 multiple spaces before operator pymodbus/client/sync.py:308:31: E701 multiple statements on one line (colon) pymodbus/client/sync.py:308:32: E272 multiple spaces before keyword pymodbus/client/sync.py:309:29: E701 multiple statements on one line (colon) pymodbus/client/sync.py:309:30: E272 multiple spaces before keyword pymodbus/client/sync.py:310:32: E701 multiple statements on one line (colon) pymodbus/client/sync.py:318:23: E701 multiple statements on one line (colon) pymodbus/client/sync.py:321:17: E128 continuation line under-indented for visual indent pymodbus/client/sync.py:322:17: E128 continuation line under-indented for visual indent pymodbus/client/sync.py:326:28: E711 comparison to None should be 'if cond is not None:' pymodbus/server/async.py:218:5: E128 continuation line under-indented for visual indent pymodbus/server/async.py:218:5: E125 continuation line does not distinguish itself from next logical line pymodbus/server/async.py:236:43: E261 at least two spaces before inline comment pymodbus/server/sync.py:78:80: E501 line too long (81 characters) pymodbus/server/sync.py:80:80: E501 line too long (81 characters) pymodbus/server/sync.py:84:80: E501 line too long (80 characters) pymodbus/server/sync.py:91:80: E501 line too long (80 characters) pymodbus/server/sync.py:110:34: E701 multiple statements on one line (colon) pymodbus/server/sync.py:113:19: E701 multiple statements on one line (colon) pymodbus/server/sync.py:140:28: E701 multiple statements on one line (colon) pymodbus/server/sync.py:144:34: E701 multiple statements on one line (colon) pymodbus/server/sync.py:148:19: E701 multiple statements on one line (colon) pymodbus/server/sync.py:177:28: E701 multiple statements on one line (colon) pymodbus/server/sync.py:181:34: E701 multiple statements on one line (colon) pymodbus/server/sync.py:185:19: E701 multiple statements on one line (colon) pymodbus/server/sync.py:224:30: E272 multiple spaces before keyword pymodbus/server/sync.py:224:20: E221 multiple spaces before operator pymodbus/server/sync.py:232:13: E128 continuation line under-indented for visual indent pymodbus/server/sync.py:248:35: E701 multiple statements on one line (colon) pymodbus/server/sync.py:273:30: E272 multiple spaces before keyword pymodbus/server/sync.py:273:20: E221 multiple spaces before operator pymodbus/server/sync.py:281:13: E128 continuation line under-indented for visual indent pymodbus/server/sync.py:289:33: E261 at least two spaces before inline comment pymodbus/server/sync.py:298:35: E701 multiple statements on one line (colon) pymodbus/server/sync.py:329:30: E272 multiple spaces before keyword pymodbus/server/sync.py:329:20: E221 multiple spaces before operator pymodbus/server/sync.py:336:20: E221 multiple spaces before operator pymodbus/server/sync.py:339:20: E221 multiple spaces before operator pymodbus/server/sync.py:341:21: E221 multiple spaces before operator pymodbus/server/sync.py:342:20: E221 multiple spaces before operator pymodbus/server/sync.py:350:23: E701 multiple statements on one line (colon) pymodbus/server/sync.py:353:17: E128 continuation line under-indented for visual indent pymodbus/server/sync.py:354:17: E128 continuation line under-indented for visual indent pymodbus/server/sync.py:358:28: E711 comparison to None should be 'if cond is not None:' pymodbus/server/sync.py:370:13: E128 continuation line under-indented for visual indent pymodbus/server/sync.py:381:19: E701 multiple statements on one line (colon) pymodbus/internal/ptwisted.py:53:15: E701 multiple statements on one line (colon) pymodbus/datastore/context.py:9:15: E702 multiple statements on one line (semicolon) pymodbus/datastore/context.py:101:20: E221 multiple spaces before operator pymodbus/datastore/context.py:128:23: E701 multiple statements on one line (colon) pymodbus/datastore/context.py:131:13: E701 multiple statements on one line (colon) pymodbus/datastore/context.py:139:23: E701 multiple statements on one line (colon) pymodbus/datastore/context.py:142:80: E501 line too long (82 characters) pymodbus/datastore/context.py:142:13: E701 multiple statements on one line (colon) pymodbus/datastore/database.py:13:15: E702 multiple statements on one line (semicolon) pymodbus/datastore/database.py:83:80: E501 line too long (80 characters) pymodbus/datastore/database.py:85:80: E501 line too long (80 characters) pymodbus/datastore/database.py:95:13: E128 continuation line under-indented for visual indent pymodbus/datastore/database.py:110:14: E221 multiple spaces before operator pymodbus/datastore/database.py:128:28: E203 whitespace before ':' pymodbus/datastore/database.py:129:28: E203 whitespace before ':' pymodbus/datastore/database.py:130:28: E203 whitespace before ':' pymodbus/datastore/database.py:142:14: E221 multiple spaces before operator pymodbus/datastore/database.py:143:15: E221 multiple spaces before operator pymodbus/datastore/database.py:154:14: E221 multiple spaces before operator pymodbus/datastore/database.py:155:14: E221 multiple spaces before operator pymodbus/datastore/database.py:156:31: E221 multiple spaces before operator pymodbus/datastore/database.py:158:15: E221 multiple spaces before operator pymodbus/datastore/database.py:168:14: E221 multiple spaces before operator pymodbus/datastore/modredis.py:8:15: E702 multiple statements on one line (semicolon) pymodbus/datastore/modredis.py:80:80: E501 line too long (80 characters) pymodbus/datastore/modredis.py:82:80: E501 line too long (80 characters) pymodbus/datastore/modredis.py:97:16: E203 whitespace before ':' pymodbus/datastore/modredis.py:98:16: E203 whitespace before ':' pymodbus/datastore/modredis.py:99:16: E203 whitespace before ':' pymodbus/datastore/modredis.py:100:16: E203 whitespace before ':' pymodbus/datastore/modredis.py:103:16: E203 whitespace before ':' pymodbus/datastore/modredis.py:104:16: E203 whitespace before ':' pymodbus/datastore/modredis.py:105:16: E203 whitespace before ':' pymodbus/datastore/modredis.py:106:16: E203 whitespace before ':' pymodbus/datastore/modredis.py:109:16: E203 whitespace before ':' pymodbus/datastore/modredis.py:110:16: E203 whitespace before ':' pymodbus/datastore/modredis.py:111:16: E203 whitespace before ':' pymodbus/datastore/modredis.py:112:16: E203 whitespace before ':' pymodbus/datastore/modredis.py:115:80: E501 line too long (80 characters) pymodbus/datastore/modredis.py:117:80: E501 line too long (80 characters) pymodbus/datastore/modredis.py:118:15: E221 multiple spaces before operator pymodbus/datastore/modredis.py:132:16: E221 multiple spaces before operator pymodbus/datastore/modredis.py:176:80: E501 line too long (92 characters) pymodbus/datastore/modredis.py:176:14: E221 multiple spaces before operator pymodbus/datastore/modredis.py:183:80: E501 line too long (80 characters) pymodbus/datastore/modredis.py:185:80: E501 line too long (80 characters) pymodbus/datastore/modredis.py:186:15: E221 multiple spaces before operator pymodbus/datastore/modredis.py:201:16: E221 multiple spaces before operator pymodbus/datastore/remote.py:98:32: E701 multiple statements on one line (colon) pymodbus/datastore/remote.py:99:32: E701 multiple statements on one line (colon) pymodbus/datastore/remote.py:100:13: E701 multiple statements on one line (colon) pymodbus/datastore/store.py:144:13: E701 multiple statements on one line (colon) pymodbus/datastore/store.py:163:15: E221 multiple spaces before operator pymodbus/datastore/store.py:204:13: E701 multiple statements on one line (colon) pymodbus/datastore/store.py:216:44: E225 missing whitespace around operator pymodbus/datastore/store.py:225:22: E701 multiple statements on one line (colon) 4 E122 continuation line missing indentation or outdented 1 E125 continuation line does not distinguish itself from next logical line 77 E126 continuation line over-indented for hanging indent 4 E127 continuation line over-indented for visual indent 34 E128 continuation line under-indented for visual indent 1 E201 whitespace after '{' 1 E202 whitespace before '}' 68 E203 whitespace before ':' 154 E221 multiple spaces before operator 10 E225 missing whitespace around operator 29 E231 missing whitespace after ',' 26 E261 at least two spaces before inline comment 10 E272 multiple spaces before keyword 47 E501 line too long (80 characters) 2 E502 the backslash is redundant between brackets 72 E701 multiple statements on one line (colon) 5 E702 multiple statements on one line (semicolon) 4 E711 comparison to None should be 'if cond is not None:' pymodbus/doc/quality/current.lint0000644000175500017550000000353612607272152017274 0ustar debacledebaclerunning lint pymodbus/__init__.py:25: redefinition of unused 'NullHandler' from line 23 pymodbus/factory.py:12: 'from pymodbus.bit_read_message import *' used; unable to detect undefined names pymodbus/factory.py:13: 'from pymodbus.bit_write_message import *' used; unable to detect undefined names pymodbus/factory.py:14: 'from pymodbus.diag_message import *' used; unable to detect undefined names pymodbus/factory.py:15: 'from pymodbus.file_message import *' used; unable to detect undefined names pymodbus/factory.py:16: 'from pymodbus.other_message import *' used; unable to detect undefined names pymodbus/factory.py:17: 'from pymodbus.register_read_message import *' used; unable to detect undefined names pymodbus/factory.py:18: 'from pymodbus.register_write_message import *' used; unable to detect undefined names pymodbus/server/async.py:230: local variable 'handle' is assigned to but never used pymodbus/server/sync.py:16: 'from pymodbus.transaction import *' used; unable to detect undefined names pymodbus/client/common.py:3: 'from pymodbus.bit_read_message import *' used; unable to detect undefined names pymodbus/client/common.py:4: 'from pymodbus.bit_write_message import *' used; unable to detect undefined names pymodbus/client/common.py:5: 'from pymodbus.register_read_message import *' used; unable to detect undefined names pymodbus/client/common.py:6: 'from pymodbus.register_write_message import *' used; unable to detect undefined names pymodbus/client/common.py:7: 'from pymodbus.diag_message import *' used; unable to detect undefined names pymodbus/client/common.py:8: 'from pymodbus.file_message import *' used; unable to detect undefined names pymodbus/client/common.py:9: 'from pymodbus.other_message import *' used; unable to detect undefined names pymodbus/client/sync.py:8: 'from pymodbus.transaction import *' used; unable to detect undefined names pymodbus/doc/TODO0000644000175500017550000000352012607272152013713 0ustar debacledebacle--------------------------------------------------------------------------- General --------------------------------------------------------------------------- - reorganize code into folder namespaces - put protocol code in protocol namespace - make framer read header->read header.length - maybe just for sync - finish clients (and interface) - add all modbus control into server - add a frontend plugin system - web frontend (bottle) - twisted trial / twisted logging (for functional async tests) - twisted serial server - add daemonize code / init.d / config (or just use twisted) - add correct transaction handling (retry, fail, etc) - finish remaining functions --------------------------------------------------------------------------- Protocols --------------------------------------------------------------------------- - Serial RTU -> just use sleep wait - Test serial against devices (and virtual tty) --------------------------------------------------------------------------- Utilities --------------------------------------------------------------------------- - (tcp/serial) forwarder - (udp/serial) forwarder --------------------------------------------------------------------------- Client --------------------------------------------------------------------------- - Rework transaction flow and response data --------------------------------------------------------------------------- Tools --------------------------------------------------------------------------- - add functional tests - add tk and wx gui frontdends (with editable data tables) - rpm and deb packages (documentation) --------------------------------------------------------------------------- Scratch --------------------------------------------------------------------------- from twisted.python import log observer = log.PythonLoggingObserver() observer.start() pymodbus/doc/api/0000755000175500017550000000000012607272152013774 5ustar debacledebaclepymodbus/doc/api/pydoc/0000755000175500017550000000000012607272152015112 5ustar debacledebaclepymodbus/doc/api/pydoc/build.py0000644000175500017550000003637412607272152016600 0ustar debacledebacle#!/usr/bin/env python """ Pydoc sub-class for generating documentation for entire packages. Taken from: http://pyopengl.sourceforge.net/pydoc/OpenGLContext.pydoc.pydoc2.html Author: Mike Fletcher """ import logging import pydoc, inspect, os, string, shutil import sys, imp, os, stat, re, types, inspect from repr import Repr from string import expandtabs, find, join, lower, split, strip, rfind, rstrip _log = logging.getLogger(__name__) def classify_class_attrs(cls): """Return list of attribute-descriptor tuples. For each name in dir(cls), the return list contains a 4-tuple with these elements: 0. The name (a string). 1. The kind of attribute this is, one of these strings: 'class method' created via classmethod() 'static method' created via staticmethod() 'property' created via property() 'method' any other flavor of method 'data' not a method 2. The class which defined this attribute (a class). 3. The object as obtained directly from the defining class's __dict__, not via getattr. This is especially important for data attributes: C.data is just a data object, but C.__dict__['data'] may be a data descriptor with additional info, like a __doc__ string. Note: This version is patched to work with Zope Interface-bearing objects """ mro = inspect.getmro(cls) names = dir(cls) result = [] for name in names: # Get the object associated with the name. # Getting an obj from the __dict__ sometimes reveals more than # using getattr. Static and class methods are dramatic examples. if name in cls.__dict__: obj = cls.__dict__[name] else: try: obj = getattr(cls, name) except AttributeError, err: continue # Figure out where it was defined. homecls = getattr(obj, "__objclass__", None) if homecls is None: # search the dicts. for base in mro: if name in base.__dict__: homecls = base break # Get the object again, in order to get it from the defining # __dict__ instead of via getattr (if possible). if homecls is not None and name in homecls.__dict__: obj = homecls.__dict__[name] # Also get the object via getattr. obj_via_getattr = getattr(cls, name) # Classify the object. if isinstance(obj, staticmethod): kind = "static method" elif isinstance(obj, classmethod): kind = "class method" elif isinstance(obj, property): kind = "property" elif (inspect.ismethod(obj_via_getattr) or inspect.ismethoddescriptor(obj_via_getattr)): kind = "method" else: kind = "data" result.append((name, kind, homecls, obj)) return result inspect.classify_class_attrs = classify_class_attrs class DefaultFormatter(pydoc.HTMLDoc): def docmodule(self, object, name=None, mod=None, packageContext = None, *ignored): """Produce HTML documentation for a module object.""" name = object.__name__ # ignore the passed-in name parts = split(name, '.') links = [] for i in range(len(parts)-1): links.append( '%s' % (join(parts[:i+1], '.'), parts[i])) linkedname = join(links + parts[-1:], '.') head = '%s' % linkedname try: path = inspect.getabsfile(object) url = path if sys.platform == 'win32': import nturl2path url = nturl2path.pathname2url(path) filelink = '%s' % (url, path) except TypeError: filelink = '(built-in)' info = [] if hasattr(object, '__version__'): version = str(object.__version__) if version[:11] == '$' + 'Revision: ' and version[-1:] == '$': version = strip(version[11:-1]) info.append('version %s' % self.escape(version)) if hasattr(object, '__date__'): info.append(self.escape(str(object.__date__))) if info: head = head + ' (%s)' % join(info, ', ') result = self.heading( head, '#ffffff', '#7799ee', 'index
' + filelink) modules = inspect.getmembers(object, inspect.ismodule) classes, cdict = [], {} for key, value in inspect.getmembers(object, inspect.isclass): if (inspect.getmodule(value) or object) is object: classes.append((key, value)) cdict[key] = cdict[value] = '#' + key for key, value in classes: for base in value.__bases__: key, modname = base.__name__, base.__module__ module = sys.modules.get(modname) if modname != name and module and hasattr(module, key): if getattr(module, key) is base: if not cdict.has_key(key): cdict[key] = cdict[base] = modname + '.html#' + key funcs, fdict = [], {} for key, value in inspect.getmembers(object, inspect.isroutine): if inspect.isbuiltin(value) or inspect.getmodule(value) is object: funcs.append((key, value)) fdict[key] = '#-' + key if inspect.isfunction(value): fdict[value] = fdict[key] data = [] for key, value in inspect.getmembers(object, pydoc.isdata): if key not in ['__builtins__', '__doc__']: data.append((key, value)) doc = self.markup(pydoc.getdoc(object), self.preformat, fdict, cdict) doc = doc and '%s' % doc result = result + '

%s

\n' % doc packageContext.clean ( classes, object ) packageContext.clean ( funcs, object ) packageContext.clean ( data, object ) if hasattr(object, '__path__'): modpkgs = [] modnames = [] for file in os.listdir(object.__path__[0]): path = os.path.join(object.__path__[0], file) modname = inspect.getmodulename(file) if modname and modname not in modnames: modpkgs.append((modname, name, 0, 0)) modnames.append(modname) elif pydoc.ispackage(path): modpkgs.append((file, name, 1, 0)) modpkgs.sort() contents = self.multicolumn(modpkgs, self.modpkglink) ## result = result + self.bigsection( ## 'Package Contents', '#ffffff', '#aa55cc', contents) result = result + self.moduleSection( object, packageContext) elif modules: contents = self.multicolumn( modules, lambda (key, value), s=self: s.modulelink(value)) result = result + self.bigsection( 'Modules', '#fffff', '#aa55cc', contents) if classes: classlist = map(lambda (key, value): value, classes) contents = [ self.formattree(inspect.getclasstree(classlist, 1), name)] for key, value in classes: contents.append(self.document(value, key, name, fdict, cdict)) result = result + self.bigsection( 'Classes', '#ffffff', '#ee77aa', join(contents)) if funcs: contents = [] for key, value in funcs: contents.append(self.document(value, key, name, fdict, cdict)) result = result + self.bigsection( 'Functions', '#ffffff', '#eeaa77', join(contents)) if data: contents = [] for key, value in data: try: contents.append(self.document(value, key)) except Exception, err: pass result = result + self.bigsection( 'Data', '#ffffff', '#55aa55', join(contents, '
\n')) if hasattr(object, '__author__'): contents = self.markup(str(object.__author__), self.preformat) result = result + self.bigsection( 'Author', '#ffffff', '#7799ee', contents) if hasattr(object, '__credits__'): contents = self.markup(str(object.__credits__), self.preformat) result = result + self.bigsection( 'Credits', '#ffffff', '#7799ee', contents) return result def classlink(self, object, modname): """Make a link for a class.""" name, module = object.__name__, sys.modules.get(object.__module__) if hasattr(module, name) and getattr(module, name) is object: return '%s' % ( module.__name__, name, name ) return pydoc.classname(object, modname) def moduleSection( self, object, packageContext ): """Create a module-links section for the given object (module)""" modules = inspect.getmembers(object, inspect.ismodule) packageContext.clean ( modules, object ) packageContext.recurseScan( modules ) if hasattr(object, '__path__'): modpkgs = [] modnames = [] for file in os.listdir(object.__path__[0]): path = os.path.join(object.__path__[0], file) modname = inspect.getmodulename(file) if modname and modname not in modnames: modpkgs.append((modname, object.__name__, 0, 0)) modnames.append(modname) elif pydoc.ispackage(path): modpkgs.append((file, object.__name__, 1, 0)) modpkgs.sort() # do more recursion here... for (modname, name, ya,yo) in modpkgs: packageContext.addInteresting( join( (object.__name__, modname), '.')) items = [] for (modname, name, ispackage,isshadowed) in modpkgs: try: # get the actual module object... ## if modname == "events": ## import pdb ## pdb.set_trace() module = pydoc.safeimport( "%s.%s"%(name,modname) ) description, documentation = pydoc.splitdoc( inspect.getdoc( module )) if description: items.append( """%s -- %s"""% ( self.modpkglink( (modname, name, ispackage, isshadowed) ), description, ) ) else: items.append( self.modpkglink( (modname, name, ispackage, isshadowed) ) ) except: items.append( self.modpkglink( (modname, name, ispackage, isshadowed) ) ) contents = string.join( items, '
') result = self.bigsection( 'Package Contents', '#ffffff', '#aa55cc', contents) elif modules: contents = self.multicolumn( modules, lambda (key, value), s=self: s.modulelink(value)) result = self.bigsection( 'Modules', '#fffff', '#aa55cc', contents) else: result = "" return result class AlreadyDone(Exception): pass class PackageDocumentationGenerator: """A package document generator creates documentation for an entire package using pydoc's machinery. baseModules -- modules which will be included and whose included and children modules will be considered fair game for documentation destinationDirectory -- the directory into which the HTML documentation will be written recursion -- whether to add modules which are referenced by and/or children of base modules exclusions -- a list of modules whose contents will not be shown in any other module, commonly such modules as OpenGL.GL, wxPython.wx etc. recursionStops -- a list of modules which will explicitly stop recursion (i.e. they will never be included), even if they are children of base modules. formatter -- allows for passing in a custom formatter see DefaultFormatter for sample implementation. """ def __init__ ( self, baseModules, destinationDirectory = ".", recursion = 1, exclusions = (), recursionStops = (), formatter = None ): self.destinationDirectory = os.path.abspath( destinationDirectory) self.exclusions = {} self.warnings = [] self.baseSpecifiers = {} self.completed = {} self.recursionStops = {} self.recursion = recursion for stop in recursionStops: self.recursionStops[ stop ] = 1 self.pending = [] for exclusion in exclusions: try: self.exclusions[ exclusion ]= pydoc.locate ( exclusion) except pydoc.ErrorDuringImport, value: self.warn( """Unable to import the module %s which was specified as an exclusion module"""% (repr(exclusion))) self.formatter = formatter or DefaultFormatter() for base in baseModules: self.addBase( base ) def warn( self, message ): """Warnings are used for recoverable, but not necessarily ignorable conditions""" self.warnings.append (message) def info (self, message): """Information/status report""" _log.debug(message) def addBase(self, specifier): """Set the base of the documentation set, only children of these modules will be documented""" try: self.baseSpecifiers [specifier] = pydoc.locate ( specifier) self.pending.append (specifier) except pydoc.ErrorDuringImport, value: self.warn( """Unable to import the module %s which was specified as a base module"""% (repr(specifier))) def addInteresting( self, specifier): """Add a module to the list of interesting modules""" if self.checkScope( specifier): self.pending.append (specifier) else: self.completed[ specifier] = 1 def checkScope (self, specifier): """Check that the specifier is "in scope" for the recursion""" if not self.recursion: return 0 items = string.split (specifier, ".") stopCheck = items [:] while stopCheck: name = string.join(items, ".") if self.recursionStops.get( name): return 0 elif self.completed.get (name): return 0 del stopCheck[-1] while items: if self.baseSpecifiers.get( string.join(items, ".")): return 1 del items[-1] # was not within any given scope return 0 def process( self ): """Having added all of the base and/or interesting modules, proceed to generate the appropriate documentation for each module in the appropriate directory, doing the recursion as we go.""" try: while self.pending: try: if self.completed.has_key( self.pending[0] ): raise AlreadyDone( self.pending[0] ) self.info( """Start %s"""% (repr(self.pending[0]))) object = pydoc.locate ( self.pending[0] ) self.info( """ ... found %s"""% (repr(object.__name__))) except AlreadyDone: pass except pydoc.ErrorDuringImport, value: self.info( """ ... FAILED %s"""% (repr( value))) self.warn( """Unable to import the module %s"""% (repr(self.pending[0]))) except (SystemError, SystemExit), value: self.info( """ ... FAILED %s"""% (repr( value))) self.warn( """Unable to import the module %s"""% (repr(self.pending[0]))) except Exception, value: self.info( """ ... FAILED %s"""% (repr( value))) self.warn( """Unable to import the module %s"""% (repr(self.pending[0]))) else: page = self.formatter.page( pydoc.describe(object), self.formatter.docmodule( object, object.__name__, packageContext = self, ) ) file = open ( os.path.join( self.destinationDirectory, self.pending[0] + ".html", ), 'w', ) file.write(page) file.close() self.completed[ self.pending[0]] = object del self.pending[0] finally: for item in self.warnings: _log.info(item) def clean (self, objectList, object): """callback from the formatter object asking us to remove those items in the key, value pairs where the object is imported from one of the excluded modules""" for key, value in objectList[:]: for excludeObject in self.exclusions.values(): if hasattr( excludeObject, key ) and excludeObject is not object: if ( getattr( excludeObject, key) is value or (hasattr( excludeObject, '__name__') and excludeObject.__name__ == "Numeric" ) ): objectList[:] = [ (k,o) for k,o in objectList if k != key ] def recurseScan(self, objectList): """Process the list of modules trying to add each to the list of interesting modules""" for key, value in objectList: self.addInteresting( value.__name__ ) #---------------------------------------------------------------------------# # Main Runner #---------------------------------------------------------------------------# if __name__ == "__main__": if not os.path.exists("./html"): os.mkdir("./html") print "Building Pydoc API Documentation" PackageDocumentationGenerator( baseModules = ['pymodbus', '__builtin__'], destinationDirectory = "./html/", exclusions = ['math', 'string', 'twisted'], recursionStops = [], ).process () if os.path.exists('../../../build'): shutil.move("html", "../../../build/pydoc") pymodbus/doc/api/epydoc/0000755000175500017550000000000012607272152015257 5ustar debacledebaclepymodbus/doc/api/epydoc/build.py0000755000175500017550000000170212607272152016733 0ustar debacledebacle#!/usr/bin/env python ''' Epydoc API Runner ------------------ Using pkg_resources, we attempt to see if epydoc is installed, if so, we use its cli program to compile the documents ''' try: import sys, os, shutil import pkg_resources pkg_resources.require("epydoc") from epydoc.cli import cli sys.argv = '''epydoc.py pymodbus --html --simple-term --quiet --include-log --graph=all --docformat=plaintext --debug --exclude=._ --exclude=tests --output=html/ '''.split() #bugs in trunk for --docformat=restructuredtext if not os.path.exists("./html"): os.mkdir("./html") print "Building Epydoc API Documentation" cli() if os.path.exists('../../../build'): shutil.move("html", "../../../build/epydoc") except Exception, ex: import traceback,sys traceback.print_exc(file=sys.stdout) print "Epydoc not avaliable...not building" pymodbus/doc/api/pydoctor/0000755000175500017550000000000012607272152015637 5ustar debacledebaclepymodbus/doc/api/pydoctor/build.py0000755000175500017550000000144012607272152017312 0ustar debacledebacle#!/usr/bin/env python ''' Pydoctor API Runner --------------------- Using pkg_resources, we attempt to see if pydoctor is installed, if so, we use its cli program to compile the documents ''' try: import sys, os, shutil import pkg_resources pkg_resources.require("pydoctor") from pydoctor.driver import main sys.argv = '''pydoctor.py --quiet --project-name=Pymodbus --project-url=http://code.google.com/p/pymodbus/ --add-package=../../../pymodbus --html-output=html --html-write-function-pages --make-html'''.split() print "Building Pydoctor API Documentation" main(sys.argv[1:]) if os.path.exists('../../../build'): shutil.move("html", "../../../build/pydoctor") except: print "Pydoctor unavailable...not building" pymodbus/doc/api/doxygen/0000755000175500017550000000000012607272152015451 5ustar debacledebaclepymodbus/doc/api/doxygen/build.py0000644000175500017550000000204512607272152017123 0ustar debacledebacle#!/usr/bin/env python ''' Doxygen API Builder --------------------- ''' import os, shutil def is_exe(path): ''' Returns if the program is executable :param path: The path to the file :return: True if it is, False otherwise ''' return os.path.exists(path) and os.access(path, os.X_OK) def which(program): ''' Check to see if an executable exists :param program: The program to check for :return: The full path of the executable or None if not found ''' fpath, name = os.path.split(program) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file return None if which('doxygen') is not None: print "Building Doxygen API Documentation" os.system("doxygen .doxygen") if os.path.exists('../../../build'): shutil.move("html", "../../../build/doxygen") else: print "Doxygen not available...not building" pymodbus/doc/api/doxygen/.doxygen0000644000175500017550000017371412607272152017144 0ustar debacledebacle# Doxyfile 1.5.8 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = Pymodbus # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = 0.5 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, # Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, # Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, # Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it parses. # With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this tag. # The format is ext=language, where ext is a file extension, and language is one of # the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, # Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), # use: inc=Fortran f=C EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen to replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penality. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will rougly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by # doxygen. The layout file controls the global structure of the generated output files # in an output format independent way. The create the layout file that represents # doxygen's defaults, run doxygen with the -l option. You can optionally specify a # file name after the option, if omitted DoxygenLayout.xml will be used as the name # of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = NO # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = NO # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = NO # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = ../../../pymodbus # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = *.py # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER # are set, an additional index file will be generated that can be used as input for # Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated # HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. # For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's # filter section matches. # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to FRAME, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. Other possible values # for this tag are: HIERARCHIES, which will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list; # ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which # disables this behavior completely. For backwards compatibility with previous # releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE # respectively. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = YES # By default doxygen will write a font called FreeSans.ttf to the output # directory and reference it in all dot files that doxygen generates. This # font does not include all possible unicode characters however, so when you need # these (or just want a differently looking font) you can specify the font name # using DOT_FONTNAME. You need need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = FreeSans # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Options related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = YES pymodbus/doc/pymodbus.pdf0000644000175500017550000174243012607272152015573 0ustar debacledebacle%PDF-1.4 %ÐÔÅØ 5 0 obj << /S /GoTo /D (chapter.1) >> endobj 8 0 obj (Pymodbus Library Examples) endobj 9 0 obj << /S /GoTo /D (section.1.1) >> endobj 12 0 obj (Example Library Code) endobj 13 0 obj << /S /GoTo /D (section.1.2) >> endobj 16 0 obj (Example Frontend Code) endobj 17 0 obj << /S /GoTo /D (chapter.2) >> endobj 20 0 obj (Pymodbus Library API Documentation) endobj 21 0 obj << /S /GoTo /D (section.2.1) >> endobj 24 0 obj (bit\137read\137message --- Bit Read Modbus Messages) endobj 25 0 obj << /S /GoTo /D (section.2.2) >> endobj 28 0 obj (bit\137write\137message --- Bit Write Modbus Messages) endobj 29 0 obj << /S /GoTo /D (section.2.3) >> endobj 32 0 obj (client.common --- Twisted Async Modbus Client) endobj 33 0 obj << /S /GoTo /D (section.2.4) >> endobj 36 0 obj (client.sync --- Twisted Synchronous Modbus Client) endobj 37 0 obj << /S /GoTo /D (section.2.5) >> endobj 40 0 obj (client.async --- Twisted Async Modbus Client) endobj 41 0 obj << /S /GoTo /D (section.2.6) >> endobj 44 0 obj (constants --- Modbus Default Values) endobj 45 0 obj << /S /GoTo /D (section.2.7) >> endobj 48 0 obj (Server Datastores and Contexts) endobj 49 0 obj << /S /GoTo /D (section.2.8) >> endobj 52 0 obj (diag\137message --- Diagnostic Modbus Messages) endobj 53 0 obj << /S /GoTo /D (section.2.9) >> endobj 56 0 obj (device --- Modbus Device Representation) endobj 57 0 obj << /S /GoTo /D (section.2.10) >> endobj 60 0 obj (factory --- Request/Response Decoders) endobj 61 0 obj << /S /GoTo /D (section.2.11) >> endobj 64 0 obj (interfaces --- System Interfaces) endobj 65 0 obj << /S /GoTo /D (section.2.12) >> endobj 68 0 obj (exceptions --- Exceptions Used in PyModbus) endobj 69 0 obj << /S /GoTo /D (section.2.13) >> endobj 72 0 obj (other\137message --- Other Modbus Messages) endobj 73 0 obj << /S /GoTo /D (section.2.14) >> endobj 76 0 obj (file\137message --- File Modbus Messages) endobj 77 0 obj << /S /GoTo /D (section.2.15) >> endobj 80 0 obj (events --- Events Used in PyModbus) endobj 81 0 obj << /S /GoTo /D (section.2.16) >> endobj 84 0 obj (pdu --- Base Structures) endobj 85 0 obj << /S /GoTo /D (section.2.17) >> endobj 88 0 obj (pymodbus --- Pymodbus Library) endobj 89 0 obj << /S /GoTo /D (section.2.18) >> endobj 92 0 obj (register\137read\137message --- Register Read Messages) endobj 93 0 obj << /S /GoTo /D (section.2.19) >> endobj 96 0 obj (register\137write\137message --- Register Write Messages) endobj 97 0 obj << /S /GoTo /D (section.2.20) >> endobj 100 0 obj (server.sync --- Twisted Synchronous Modbus Server) endobj 101 0 obj << /S /GoTo /D (section.2.21) >> endobj 104 0 obj (server.async --- Twisted Asynchronous Modbus Server) endobj 105 0 obj << /S /GoTo /D (section.2.22) >> endobj 108 0 obj (transaction --- Transaction Controllers for Pymodbus) endobj 109 0 obj << /S /GoTo /D (section.2.23) >> endobj 112 0 obj (utilities --- Extra Modbus Helpers) endobj 113 0 obj << /S /GoTo /D (chapter.3) >> endobj 116 0 obj (Indices and tables) endobj 117 0 obj << /S /GoTo /D (section*.421) >> endobj 120 0 obj (Python Module Index) endobj 121 0 obj << /S /GoTo /D (section*.422) >> endobj 124 0 obj (Index) endobj 125 0 obj << /S /GoTo /D [126 0 R /Fit ] >> endobj 128 0 obj << /Length 272 /Filter /FlateDecode >> stream xÚ½MkÃ0 †ïþ:&°h’çøº¯Ba°u¹•²&+eù`KË迟Ҙµ¬÷mÉæ}^I&ØÁLQŒ,'ƒ×²Ö­¢øò¡%‹ûkç×ÅÉáÿUŸ¿]d3cßËA%/s‘‡ààû¨iÁ¸\b¯êåTçÏô™ŒŸÅùo uýxã@ô¹g(ÞAk‡F,Ó¨ÙCQÁ2y>´}õ–jJöCšiŸ'÷ýzßÖÝ®Ümû.]sññÀ^kŽ>гQc.~GŸEÝÔåPOŒAwfz,,=²µh Oä¬lênâîú¦ÙvCd0c°V¬ ¨ÅEd½‰EŸÊƒ€–WS¢‰yÄ/þè¡P?xˆ endstream endobj 126 0 obj << /Type /Page /Contents 128 0 R /Resources 127 0 R /MediaBox [0 0 612 792] /Parent 134 0 R >> endobj 129 0 obj << /D [126 0 R /XYZ 72 744.907 null] >> endobj 130 0 obj << /D [126 0 R /XYZ 72 720 null] >> endobj 127 0 obj << /Font << /F36 131 0 R /F37 132 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 137 0 obj << /Length 19 /Filter /FlateDecode >> stream xÚ3PHW0Ppç2ÀAc(á endstream endobj 136 0 obj << /Type /Page /Contents 137 0 R /Resources 135 0 R /MediaBox [0 0 612 792] /Parent 134 0 R >> endobj 138 0 obj << /D [136 0 R /XYZ 72 744.907 null] >> endobj 135 0 obj << /ProcSet [ /PDF ] >> endobj 171 0 obj << /Length 1393 /Filter /FlateDecode >> stream xÚíš]W£H†ïó+¸L.ìé/úãrFã¬svf\“ݽpçÌAÒ*ç$!DÍ¿ß"Ð ñ¬‰{cH¤«ªú­*°sã`çs矟†§L8”#©$q†×Ç IÁ0bð¯áȹìÿ6ìz?†_:Q¬ø¯äð—+áD`­ôãÅg°Í…£‘T¤¦#KÛGÃÑ™qÒƒïwÏ“ptÕ£¸;{GÔÅÝ߃«È‹Ù—þƒ7™Mœ­!¿çòˆQÎt—í¼4¦×–F8ÒœÃÂÒ®›¯ ¥+cOS[uŽLqA\¹]Ô;rá’äƒkݶÍÂ=#’ÊÃÕ3Z›%œ^GmàN£pš˜éh39AÙÿÈj#£€Œ¸ ™CÃÊ)p$*ãHë†çgÙÁIèÏ'fšxIN‹Ë¥Ò•&tûq‚Bœ£pEjíLê"̾<ç*H~FÆýœ˜8önL6à™i­at5฻ÙU} ’ìàÆgG_ ·ãkf´•oóQª~°V v0ž#ÌÜ"$Z Ò}$f %‚9"d ¥¿S;bÂü?g°+Nšóe +òa•|üqÞüp2Éü»ÀFaD´.³öîÞqbrÿù/¦¾ÑñrŽâ%kwß5gÍya¦9/±âÅëòZÞì2-¡…PZMkãoaïý¨3ÅÞtð³Râ)iˆK\Ê­ ÉÛ@IrD)} ŸRo|WªM¼Àµ dÑÒV%ª©…Ó8ñ¦IlAæ D•% ±œ˜ëÁ]o>η®¿z„®7ž[¶(&K̾*V’PÓ"U™'%ÝõˆÛ5QÎÁK¼8 #“sñžRÈWÒ3’2®ßyrbe¦ZJ!)R•>8 ¼›-JqSä¶–¢p> endobj 139 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [71.004 525.699 212.841 536.578] /Subtype /Link /A << /S /GoTo /D (chapter.1) >> >> endobj 140 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 513.609 205.15 524.513] /Subtype /Link /A << /S /GoTo /D (section.1.1) >> >> endobj 141 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 501.654 211.247 512.558] /Subtype /Link /A << /S /GoTo /D (section.1.2) >> >> endobj 142 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [71.004 479.871 256.278 490.75] /Subtype /Link /A << /S /GoTo /D (chapter.2) >> >> endobj 143 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 467.781 335.75 478.685] /Subtype /Link /A << /S /GoTo /D (section.2.1) >> >> endobj 144 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 455.826 343.939 466.73] /Subtype /Link /A << /S /GoTo /D (section.2.2) >> >> endobj 145 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 443.871 327.541 454.774] /Subtype /Link /A << /S /GoTo /D (section.2.3) >> >> endobj 146 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 431.915 342.156 442.819] /Subtype /Link /A << /S /GoTo /D (section.2.4) >> >> endobj 147 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 419.96 321.563 430.864] /Subtype /Link /A << /S /GoTo /D (section.2.5) >> >> endobj 148 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 410.008 276.363 418.909] /Subtype /Link /A << /S /GoTo /D (section.2.6) >> >> endobj 149 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 398.107 238.883 406.954] /Subtype /Link /A << /S /GoTo /D (section.2.7) >> >> endobj 150 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 384.095 319.859 394.999] /Subtype /Link /A << /S /GoTo /D (section.2.8) >> >> endobj 151 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 372.14 289.822 383.043] /Subtype /Link /A << /S /GoTo /D (section.2.9) >> >> endobj 152 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 360.184 283.795 371.088] /Subtype /Link /A << /S /GoTo /D (section.2.10) >> >> endobj 153 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 348.229 259.566 359.133] /Subtype /Link /A << /S /GoTo /D (section.2.11) >> >> endobj 154 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 336.274 311.511 347.178] /Subtype /Link /A << /S /GoTo /D (section.2.12) >> >> endobj 155 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 324.319 305.354 335.223] /Subtype /Link /A << /S /GoTo /D (section.2.13) >> >> endobj 156 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 312.364 292.193 323.268] /Subtype /Link /A << /S /GoTo /D (section.2.14) >> >> endobj 157 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 300.409 270.296 311.312] /Subtype /Link /A << /S /GoTo /D (section.2.15) >> >> endobj 158 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 289.076 208.975 299.357] /Subtype /Link /A << /S /GoTo /D (section.2.16) >> >> endobj 159 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 276.498 250.849 287.402] /Subtype /Link /A << /S /GoTo /D (section.2.17) >> >> endobj 160 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 264.543 351.56 275.447] /Subtype /Link /A << /S /GoTo /D (section.2.18) >> >> endobj 161 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 252.588 359.75 263.492] /Subtype /Link /A << /S /GoTo /D (section.2.19) >> >> endobj 162 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 240.633 343.65 251.537] /Subtype /Link /A << /S /GoTo /D (section.2.20) >> >> endobj 163 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 228.678 355.157 239.581] /Subtype /Link /A << /S /GoTo /D (section.2.21) >> >> endobj 164 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 216.722 347.017 227.626] /Subtype /Link /A << /S /GoTo /D (section.2.22) >> >> endobj 165 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [85.948 204.767 272.587 215.671] /Subtype /Link /A << /S /GoTo /D (section.2.23) >> >> endobj 166 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [71.004 184.887 164.333 193.863] /Subtype /Link /A << /S /GoTo /D (chapter.3) >> >> endobj 167 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [71.004 161.066 165.439 171.945] /Subtype /Link /A << /S /GoTo /D (section*.421) >> >> endobj 168 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [71.004 141.051 97.355 150.027] /Subtype /Link /A << /S /GoTo /D (section*.422) >> >> endobj 172 0 obj << /D [170 0 R /XYZ 72 550.325 null] >> endobj 169 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F39 133 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 177 0 obj << /Length 113 /Filter /FlateDecode >> stream xÚ3PHW0Ppç2@£ ¹ ´‚¡‚¹‘‚©‰ž‘™¹Br.Wt¬B PØKÁ@ÏØÒB¡¬(WÁÄÌHç(sr9…pé»›)XêYš™)„¤Œ01Ò344RIQˆÖÈÌÔŒ ñ‚[åÂ~ endstream endobj 176 0 obj << /Type /Page /Contents 177 0 R /Resources 175 0 R /MediaBox [0 0 612 792] /Parent 134 0 R >> endobj 175 0 obj << /Font << /F36 131 0 R >> /ProcSet [ /PDF /Text ] >> endobj 180 0 obj << /Length 221 /Filter /FlateDecode >> stream xÚËnÂ0E÷þŠ»L¤2?jÇ,Ki%<Šwˆ%¡B"‰ªUýû: •(ÝteÍxæžc3ÞÁxAÜ?k OÞ*‹p€~0$‡3¯rl’åWYço©âäܤ#å²ä©ÞŸË¢jwí±®î†Þkq*vM1’8݆™˜ñ!d„1$œŠ¹†;ìK±Ù2òØŸIû ŸýT c³xž°+Á¿Dý•h—%™X»ÁqRWmjÆ=ögï_§ì¿é¿'FERª t1ÓyXw̸©ÈØÖßÉ[‘oz“TÙ endstream endobj 179 0 obj << /Type /Page /Contents 180 0 R /Resources 178 0 R /MediaBox [0 0 612 792] /Parent 134 0 R >> endobj 181 0 obj << /D [179 0 R /XYZ 72 744.907 null] >> endobj 182 0 obj << /D [179 0 R /XYZ 72 720 null] >> endobj 178 0 obj << /Font << /F36 131 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 185 0 obj << /Length 195 /Filter /FlateDecode >> stream xÚ; Â@„ûûS& çÞæ¼KZŸ`áó:±PEÈQÿ½g¢…`a5ì°;;áÂXôœèŒ"ƒD&† Ü–au,uÁ¥XóGQ¥»)¸]Â6Û8Tû[‘•×íõT•­Æ[fy¶½dÍ $…7C'ÎBù?ÕäjiÉb_ˆõ†z’Qã^oÐ&öšc%‚Þ?ú•ÕÕ’ÍßQ?15K¥¸¡äWahI&ãÏj³?›ºáÔ­j˜O õ–~G; endstream endobj 184 0 obj << /Type /Page /Contents 185 0 R /Resources 183 0 R /MediaBox [0 0 612 792] /Parent 134 0 R >> endobj 186 0 obj << /D [184 0 R /XYZ 72 744.907 null] >> endobj 183 0 obj << /Font << /F36 131 0 R >> /ProcSet [ /PDF /Text ] >> endobj 189 0 obj << /Length 1395 /Filter /FlateDecode >> stream xÚÅËrÛ6ð®¯`LJ3 ð’‡lGII×–§Í¸9Ð$$qB.@ZÕßwñ MIŒ·nO `w±oì.¥ƒ7l׿&Vì'ö˜`ÔÉד›OØ)þÖÁ(M©³ÑTk'¤ ¬•s5ùmr:Ÿ¿ò¥Ô§Î|á„)AIDšúˆàÈ™Î{öËÉÅ|vé}š¿Õä$DAH}EOb„)q¦~€RB ù‡_gŠt2›ï*G£ÅAøTåüÅIL´´ Fiœ0Aahµ»øøþëS`÷úÊ›úqâ¾;?½<¹ô"ì~4€Ù'ï/ÞÍ®ŒQ20xJ#Írª”KBÃò÷UÖÀM`°àUÅ7ÒJ»ffÉÇò¦äµ9ó…Y™çc÷ïl}W1{¡•e½4ÛfÅÌæn»æÅ­"m-UUÞ Dn&¶Vže|Ÿ‰’wT›l+µ{»Àˆ€)ù4€ßDCâxùf²<ìLƒ¥¾1š ²Ë÷Éü›i„±;3¶ÛX€Q™ð¦ž­œñ‚ýGAÆ‚Ò(ê SÒò\¦œÈm«ÔZ )^›0(+ª’ÕM/kðP$¨˜Ò 4„<¨Íٹ΂˜º™°=cêæ=cê.ÚZç—FÅ&Üt·e¶¶»R2ÛvLwèb÷ B^Â! Ü÷üÈÝ0õû^% ^ˆ]õ„í«ï¨ÚJ¦ÓRYª#C¥Í¦” +€(^\­Ä¬iEm`[0!X! jÁ…¥Õr5±¼0†S[5È›8tß¶²1DUùYy€íÝÝU0Ö> NUö‘[6£=ÈÅg«G¶ôà%–µ´èùÙ…GÑ^ôõ«ÁÑw%eVíØo#}ruv~þÒÖº0K®÷—ÅîüÚ „2gõÜ æöž²ÊKÅ&†jMöC˜&¿3¨>FÔõ `[ƒC‘™ÀêŽÛÂ#u뺄­.VŠÿÐ êlî 2VçÌ@Áš.m¢È…Є áù ·¼µB¹Ô¼1€U¦ž”¹¹ÃþÁ Ö>šXLÚ½)›•õð‚1ëÖ…`¶Ð꤃U´õNù¾S?(Âcl –MfLU%ÞB&š½‚>ôŠ‚ƒ~LöhÎ++°(45Ÿ*ç©Bõ¥Él í´Rš J'H"BbìµÓÊ`ßNcèqÔ·Ô y"?zŒv -" ,‡CÕ!))Â0ôR4ŒaTˆ“ž¯`Îb”ñAñ¶«ž ÂÄ딆ª¸Âl€ŒÈÎG?·Rß–õ1«ï½)ŪÛ6+èÓ]ÁÔ¥†úÅ‹cà *+h×ú¦ ]ý§ OŽñ™>Û¿Ž»€$ƒ>¢T0sŠž2Ô±´Zª¡VÖµduPsŠZW|c6ê…¨µ•–¢éØfv[¯ŒXš´sÔ-ÿ»â#~5¡@:z‡f½×Dóün˜Ã™"-fÌœ)îºø®â®t‡ýV‰Ã'õ½-çõ¢\¶bÿÉ ŸQÅ—Kõ¦DZ }:¦‡u—E¡ÛL–ù™¶éOaøOƸ¹aùóg´dÍ;Ø2ñ•ËH!»g•¢ë.¿š^¿^ú!ñ\q. ¦ž„Ôf“}!׿“¦ëì³UO>¤Ÿmv°öޱZÞ‘¯ÊŽx©ÇDdN¯¹èQÊG4é]’wÂúôйàû¡ïkScáË=/&¯/)\Ö{wÌ6>m}eþŠÂ‘$yúðt0ÂÈÚ§Lpø±¿þD!|ÄÆ²{ô?Q¢¾Cb¿8‚¯ZðÃ? {“ endstream endobj 188 0 obj << /Type /Page /Contents 189 0 R /Resources 187 0 R /MediaBox [0 0 612 792] /Parent 193 0 R >> endobj 190 0 obj << /D [188 0 R /XYZ 72 744.907 null] >> endobj 6 0 obj << /D [188 0 R /XYZ 72 720 null] >> endobj 10 0 obj << /D [188 0 R /XYZ 72 526.614 null] >> endobj 192 0 obj << /D [188 0 R /XYZ 72 489.498 null] >> endobj 187 0 obj << /Font << /F36 131 0 R /F58 191 0 R /F39 133 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 196 0 obj << /Length 1066 /Filter /FlateDecode >> stream xÚíX]oÛ6}÷¯ ‡ÚCÄ’%‘Eû°umb¶ÎoYÈc •-‡”êúßIYrd×ñÚ¦@á‡Ì{ï9¼”/s€À»ÑoÓÑó·~8ä! ÁôDD”AÊ}0MÁÕø¯í²Hg‚Æ•šx$bãß‹¤ZŠU—Y±º4sD.b%ÌC4¹ž¾½™ŽîFXûA»F(Értu@ªçß}ÎÀ¦ùÖÐé6ÿŒþ!‹ƒú#çÀt>¼ëX  ±"@†Ã£–#í< Zë= €}¢à¨ F`bkã>²£X9DwýÔó4ò!XkY p;hu)£ 0MYHe(„ŒSCÙÅÄ §™IYÈí¥Ò´ªŒeiº±iÖ Ç5¿Ís!? k µC#Èn-{_ïïbÈA’gZYÇ+ÓüÙ@{Ý<øè&DúƒŸé!vV†³ïÓFx!òµ°Q]ŠrQ¤¦_¶Êî%·BJaŸ'qžÏâä£z”k,T¬4×MP¾Ë>À:À/:¼æ ÝÔ²6Ô_AìËËÎ_ÇA³¤eÒë.ÛùiÀÁîªO|`“fþï–äñr–ZÉß¼°' …ô6Ε¸¥ÞôchK|Ž—ë\˜w•ÞîãèÅR™Æ³ÝéÃJ|!ºúWn6¶Âß•élŠ*·òϳ¢P*% éýºؾJ黿¸L¹œ§Öy¼um&òbc‰Î‹ÕÜBÉÊ…5\,EWvw ¡ÏæwN€V^ò®—üL>„™•â&)²¼V*¶§t*+qHÿRÙ‘"N3ÊØÁÏî\È;ë®{|¤=>ÞV«¤~ÐfSË—¦AŸYs¶'žôðb?'îD²q$HÛYî‚rrR___*e¥ºB×6,¯öBX1 §Çµ¾L®>Åy%†nž/ѧºü]ÕÞ¯38Š`àS½7ý`·öKó$€>Y¶KOX¿2pLÏ!ž=1âû¬wO…Ô÷O¼гtâÄõk¦ÿäÒÜPV/-õÒ ƒ„Ó½„sX/:™&Rh(Ùj]•?•³ÇAËè©Ò¡ìLéè_7˜?@:RÌ3÷BæÔƒÑWÅ¢ÈÓl5o >Ák£Ýú½»ÃFÔ£ä[ß{á·RÄÈåw¬’ ¸Ãèô\Ф€} ì)ê`/!8A0ˆdƒˆ¤?<3@o€œ"Ÿšºc"è¼TÖOK'&“Ÿ: !òcHè›ÿ¶¹pš(”ØCjë>Wr‚ *WŽhÊ—§T÷z%΀úP'ãs*‰÷*šØ'ªhº¡k{µ×€Bž\z, S1&&h´®ôbŽÇ¯ñº¬k`¦ð«Ù$d füG6“±þFã­™yc~à7%²çñÝ™ endstream endobj 195 0 obj << /Type /Page /Contents 196 0 R /Resources 194 0 R /MediaBox [0 0 612 792] /Parent 193 0 R >> endobj 197 0 obj << /D [195 0 R /XYZ 72 744.907 null] >> endobj 194 0 obj << /Font << /F36 131 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 200 0 obj << /Length 1478 /Filter /FlateDecode >> stream xÚíZYoÛF~ׯ`‘Ë…µÚƒg?ÔÎ ÆÊ“k4µ–ˆR\…‡õ×w–»¤y(Ž$²­c8FDjEÎ7óÍìÎp‡ØXØx;:›¦o˜mxȳ©mÌn f™ˆ8žá˜.2=fÌæÆåøÓf%æ×ÇóôxBwüJùŠÇ™Ÿ…">QcŸyÄý”«/áã«ÙûÑëÙèëˆ6ˆáPk";F°]^acãï Œ˜çwÅU+ô]8FÆÅè÷ÖŠVGD@OŒ¨Íä]61ø¡3øùÞ4BgYTÚ&á F˜9Ê,‚¢…–‡Ã— ãñoé&Ž /I“ˆEÉÔOŽ'ðí<Ê×ßüÕ:â \bÈ?ÀP' ·Á›í"›0Ãö,DÁ¤‡¸s€^ǪøkÈp 5ö3éC2°Á\ ˜–ÑU®;RÃñ&¤Ž#ÇM‡!Ïq+É 7n¶Šîx¢ò«…凭ÜÑ)]Îq!šmS{ÜvmĈ©<þâ—iž&Óë0žòøöxbƒëÖ›l)âfì‚[!¶<-z2Ü¿ýµQ¶…«µH2už-¹:¹õ“°Où%åI™úêˆW³:}Ô”` kšrR2¥Mc´P0k¡ÐÒí&«¶ÈÂ Žžï5ÙUÄÈå0O‘"ùÅäßW?-L»¨~÷ôéÖ4'.ED0Vš_d~’Í‚õ…ò\qãþtLJ©Š÷I°òe>,+O#V€’І"†B>%C†ËÜÏü4 ÿY±mÈŠ+ à þ5‡µ¨yœE"øëpv4Àðaó¿±ù·ü\Äÿ–¨ôPÒ&ãHÿ¢ ûqjhåå{Âêáô(Sh â›p‘'¼•Eå|ý%‹E/žtÆÜH{ô6†Zqˆ›¹¨q2 ]õGÔ)€Æ¨!®ý4 Î þ- ÿ‰¼¤m@íSØsÚ‚êy Ê ž}1<Ná.H ü–Gc¥_½>ûò¶Töÿ+A‡2[„ëI¿¹.œå"­W…-KõcZ¾3å4nMûž3¡›Dd¨¡ ~¶ˆ×ž‡C¢v{ >*–:Í=+Ÿêë#*Àq;Àu ¼ÜGqÚR®v·’b™ŒM#š¥_[µƒb®QApÇ49mO ÏL(<´»„5}ˆgßûæß8½]ã<虪ršPFc´½¨ÕŸ_†ñPí¹Hú&•É/ݧ¬:í”i2k7¥P£Eüpq»=2ŽgIηñù” »$·<Ü•»¢Pæ©“;?Î~ÚÒ®¹k(ã3¨Bµs¶57á¶‚ô¡®³›UCгD>¢žM³ÕzšerÔÞ¥™Ñè½X&CÄuiœt8R1Ù> œ¡`Ô±c×`l˜Øy‹ÕJu¸>ò4õe3lQt¾xß¾µ± ¹^}/‡ÂõÞÃÞ7ƒUnŸ¶×=L½ëU þïº^Ô¢›u½Š|`³X9²eNõ–ñIïu*Å.6q°LbÝA•#çQ¥:×A”ön>H•zNœ•YçFD‘¸“{1j¿AÛïëìÄõ*¾ˆu\Š;»„:æig§²`´Fhî@‘Û—ÎfëP›#÷¿›»Ù½¹z§ã ]Š<š«ókmg,2>/m÷Û]Ï EAI©¥¢)èÓ2îÂlÙ›— ~‘ûɼÚQN³$²¶®•Ãoý0ò¯KW‡qcÊçYeph©ùúåËýÕ-7c ŽuV( ¯U¶jþÉ\{D¨ƒ lD:±* ‘t ­ò<áiiVNëâQÂýùŸ£T*DNþ^m±—ŸÖIX‡ÆÞ[â ̼ÖÇç7 ¨ÈöOéÊD‡Eü¨k¼Üd™PíünS÷å+a‚FP®TåðGiU;…Ô‡ð:ñÕKE5r.æE]â)2mR¶ò¬Ñ@â?ùžr endstream endobj 199 0 obj << /Type /Page /Contents 200 0 R /Resources 198 0 R /MediaBox [0 0 612 792] /Parent 193 0 R >> endobj 201 0 obj << /D [199 0 R /XYZ 72 744.907 null] >> endobj 202 0 obj << /D [199 0 R /XYZ 72 720 null] >> endobj 205 0 obj << /D [199 0 R /XYZ 72 295.692 null] >> endobj 198 0 obj << /Font << /F36 131 0 R /F63 203 0 R /F64 204 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 208 0 obj << /Length 1617 /Filter /FlateDecode >> stream xÚíZ[oÛ6~÷¯Ї8@Ì’’¬K°XÓô†ØRo/Ya0í“%W—$Þ¯¯²(»‰,ËK“µAmšÎç;ç‡Ð˜Ðx7x=¼|k9†|ÇtŒÉÌpMõ=`û–1 Ëáo«E^›pXæÇ#Óõ†oÒ \¤ÀE”&'bî‚ÄçDü@™|œO_ˆòtmàB׃Ë/ÐéüGË÷Œ[þÔ°~ÇÆçÁï(eDûËæ†\¼«Qu<à ËpÑxȹ—²K™»ãŠºFÃCT:“>ïßGŽmy’Ħ`÷ŠêˆP ›¶] ø®WΈ1ÛJY“êÆ}8ì‘ÆÌ…\TÇ2<º¨Ž­:Àóm±¨/Fýý{Á×z-d‚Ø6ý  ÃåÓf¨|T8»&4Fì ÙfYºh’ä šôÃE’d}†’4.G„Ã%wÖ2˰ÜY,I#Z,Ó¬ÐÞ~ùÖöj#˦ø`®Äþ•³¼ _K’ŒˆšË—iBaÁ‰ín™‘âÔ¿‚8¢ù* h(×cËÖ 5 –gœyW«x€®«ˆâü^ €ü5惩$à|àŽ àSÄO‹ØDÐ áƒAšÌ¢y™ñ³¸–±ôb§óy”Ì÷gù”-~ºb¶Y¨ád ~ÚÀæ²ÊÎH›áŒ†@#O'®pg|ÿ‚cHÿ#öHÅÊ«XíÀ†ëóªÁjSÉŽ"ÏIñ‰’!ÙVý.o2É)rCbÆ£¡ßœ¿þã¶K<€O!dü«´Ìäl™4‘ðñ‚ä9ž“ï: tV¢ß,ãô–¹ŒÈ_¹øÎi€ˆWbŒÅ5X¨‚h«9žàU0År„ã[¼ÊׯÉ!rÖïç /U>GIÐ ç³2 X%®ø…DWÇL¬•’påɈTï6*®ôBÂhH'™á H³ÕI_ ×J¬Û(–†½ÒKÁpÞbADuÄϳÊÃU}“E•ê$¥„Üö¥Ì‚×iØt¥`’ hYF¥P.V‰E“†Ä‹Rù—Z§ÆÚ(쬀§)€“P7eÓ)¤ÍDÉôf‹c€g]91Îó®…Ã¯Úæ`‡MKpÚ»,Iò”:BȤ›FGÏ<* LyØ«(àÆ‘‹fI%µ™õ‹¨‹×µɬI‘–ní)_R¬Ï\l’’c‹!èM§QÓi3ï3ó¶Þðº#ž5iHôâ0¤ðÉ×ëÓp¢­[ÁnÕŠÒ¦ékEO  6vÕH¯—"V§õˆJiYm0ÿ:ûbp$ #€èˆ$YÇ¥~aX¯‡€Ÿ‘¢Ì’û´ågì)­ò"+ƒb/L/qðwÍ <]ry«–@ÕŒ r´-ë>ôÒÏïßwyíh3ìµ_0Q_÷,Oìgic ×;lÎuô3^ˆ ¼ê?Ù%zI_=Ä’2y‚ÑDs©ÎÀª§°áŠÜ‘ , ¬ ¥»Â»¢ÏŒͺï³ýkëDX3B›ÂecßÐ:?uAí«®6xÜ0Ñ‹®­ëHx熰×âjĺ/–ÿ`‰ÕŽô¦÷ùUÖ×2…éù]@–lÍ›fL«s‰Í+uÉ3>¢eì?$ßè( ÿLò³ýÎ#.Øégx¡itڸˠš¢?,£eŸë†yuÓa ŠÛ Õ´CûKP߀Þã÷½tG9ÙáÐb[·¥ÞHksX»=¶-€<¯ÃÍãÍ Ð&@÷“hÞ®\A~k7µÇ60Öµ·^"·©DÈÎà°{áÈGók¼ä·Ä5qº¦¹å†ù§è*Ãô)‡+1s~‡Ùµ =ÜRcþ ë_b endstream endobj 207 0 obj << /Type /Page /Contents 208 0 R /Resources 206 0 R /MediaBox [0 0 612 792] /Parent 193 0 R >> endobj 209 0 obj << /D [207 0 R /XYZ 72 744.907 null] >> endobj 206 0 obj << /Font << /F36 131 0 R /F63 203 0 R /F64 204 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 212 0 obj << /Length 1658 /Filter /FlateDecode >> stream xÚíZ[oÛ6~÷¯ÐÒ‡8Cº«@¡iš¢X±Îõž²<(2m DW”’øßïH¤d‰vœXVR£] 8Ê uÎÇsãGšX›iX»¼Î?¶æ!ÏÖmm<Õ ËDÄñ4Çt‘éÚx¢]¿.c6¹=Ññ0ç'gºã?° i’ùYÈ’S!шúœŠ_Â'7ãσËñàû€€1¬ÍÑA¯‰ìhA<¸¾ÁÚäŸ5Œ ÏÕîËQ±fÚ.üŒ´oƒ¿X%Zñ?iâatÕÐj»È&†æ ¹ÄÞªÙãŽUkoép‰®ÙØDx« L²,GªX¶ª‡0! 3…Øt ä9n­8¥Út£æÊé0dáâÃ.†(’‘Œ¬mh.DÖ6‹ÈÎÇ6r=SõÍYÿÞ”±Þ <#y–'ÑœœÙé òŒŠ_²¹|Hé÷œòLürfsñ´dy*ž‚(„dÜÄ!º$ça2S²òAÀ’Œ>d§Êé98ŠÄ“Ÿg,†’ ü(Z Ñ-­õ$4Èè¤/Ø~2© 0Nåóýœ&â)”à eÜQ®¢ÏÓ´†Ï¶ è c‹ ³¦ ЇJ4-  Z4µ¨`+ÃÙTyþÑtÃõ¢l=øK9ú ôâœ_”þ[¢ÏJ”&m£á’JR¸äzx¼ÉO½DtÁ”éòrÛ"²T´»ßˆá!·là¥'|¾ÍkÄFĬ†Š‚x[ŒßÛšºÕBÕd4³"?Jh-I9·2=ß)ó+…9‡J ½HÁÔdªDènI„ßðc~S“'¥<úÝl£ÏR5D*JÙÀ‹Ù¦õÄ;Ç»šª+¦ºHC¥Ë+!7t¤;u¡4<óœeºEU,Ó@Äu;0‚5bbc¹6Ù…˜Ô5V×zõe‰bØUÚ(ÂÑŠÑ$ϲt¹ð[@<O& A™íRÞÙ $Œ óE¡…²Ù ÁÃY¹b‚Ë?^D´eí ÏK¯Y@Dõ½ÈÁ:„ÅØ;Óg@íÁævšt®Òü4[‹BGzg™¤¿¬•7¿ç<=¿ “sšÜ‰¥t±Ìæ,Ù½‹+ex||Ü]‡×ÜRär凼™Õ,Gæ ßÛF þÞPÖ]¶/gãKUÊ‹Ë4Ñáѵے¬Ú$¾+¦Aæ@#šòÝá=>ïŠàÓfËO R}à:.qx#1ž‡\Ýp`µà´¢ìôަÐ*êŠ,âTQøŒÕΔÎ~X¼\áŠÝVt$;·>ƒ –LÃYAxÖš[›ê…žõyF³¢ÙÒt#`¯ àu#Œ@E…>@¸|ÿÏUƒGþ:åY—d½7¦iÊä©GL9÷gÕî9L”‘|AƒpV…›ø0~á´]Ǿø1 £>6ÙÕT¤ÿÎ#ÿ6¢ ò¢Eü´z[ ¦,ŠØ=ûÓï¥dž?À )ÿ½–ÃȆ¼òR&­8Ÿ%Þ†-‘â4q%?S¢’2–©9±ódΈ†ˆÝÓœ8MaÑSsm„gÃûPð„¯Ï̯ê%jòHµ<îXt·~”ÖÌ<â‡ûBlÀ/º£ö<ÿEÊ2°HxÀ³‘k8h°;{€‡õ˜¹²+ßö—=ä‚ûó³ïÀY΋Ÿ*M¬×SÒC§f—£Ñ_£ÿ©ÙvjVq­Ç¨Yµÿ}ûêœbÂè·å±béì< oS?]ž×;ó,Ž:ôÀbq*ÉÉalô~ñ¾ôë6$u'Þ>hÚ3!®k¦¦ú§ÝͬOdÄŠÛÉì#ì̤Â]³æ¨Kä!Åf°Ë«Gêä×Ò±ÿð~™ÑüñNð¬/ÍÖ•o6žcLw-PFÖænæ-d´¶]k`dŠ/!u^'ä2çÛ—ñ××̘øÍÏPÀâž²æùˆËü:Ú§;þÑ3öUoxQ§ó|Q½÷‹þæu\ÿu…éèñ’Â/QKK+v£š¦~Fa¶Ü§—½\VL|wù†ïÕýúÁÏüYêÇûÔq·•-ð£¢^z!D§;Üæ(K…è†im[60‰›M¸Þ—sù“IÃïó½nä`éÄ©®\LYºõ"“˜¢<ïn´ØßH-aòÔ% ceQ¦îÍ~—B:Ü,X»ßPè0¶ëxêVHëbmq]Â~ö½Úõ‹¿ ÂÔ!úê’,eº^_á<Êýá ¤ãp)$lBÅ´­#Ó&•«VTÀÿatÏ~ endstream endobj 211 0 obj << /Type /Page /Contents 212 0 R /Resources 210 0 R /MediaBox [0 0 612 792] /Parent 193 0 R >> endobj 213 0 obj << /D [211 0 R /XYZ 72 744.907 null] >> endobj 214 0 obj << /D [211 0 R /XYZ 72 589.724 null] >> endobj 210 0 obj << /Font << /F36 131 0 R /F63 203 0 R /F64 204 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 217 0 obj << /Length 1225 /Filter /FlateDecode >> stream xÚÍXkoÛ6ýî_Á5(ì 1#Êz04K· –5þ–,Ñ63YRI*÷ëGФ–ì<°¡k€Š¦ÈsϽ¼¼”VÀ¿Œ>ÌG§gaèÙ˜/oß  ÎÀ<·“ëí&OǶ5)ÙñÔöƒÉÏy\npÆ#NòìDõ}Á)ŽV?´ŽïæŸF—óÑ×v,€®}Ëñft{gDôœ…øVÚÇ Ä37£?F–æX?!-h{39ËC€Š½Î/ͪ‚¡ëÚfYÈ‚ÖÌWËBA·bùv3àvêZÖäjGŸ›˜F¦êÇåS´)Rܱ„€ü¨ª!:JyôÐ x¡YxP-_껵bŒ‰%‡ÐBXÀCÂŽ‹4DŸ[¿§e&„B-3²Ûñg0ôƒ˜b°Dî)ßJ'JÏÑÞóºH{ïè‡Ó’ÑÓÉNqöx<õ„Š-_ç™Z€L…¯C7T£ÇãñP÷|M˜šjž‘z¤„sᲪ—\7–ê™DŸ¨æŠ<êQÜ`8γDµy®ž'ºE8Ÿ i)Zû:«æ ©\ö7YQßÙÍ"«-ɪ¤QóSjÞ¶öhÛ Ö™óþžd„ßßKF §Ë“p¨ðr—™Ý%VóÏb ¦1ª!Ó]rÆT[ÝC\Œll¤J1/©Öf¬÷¾ÒT u¦Úï™ú½9/‚g&*•°K§çb]gHZùâÇüÿèÝuθnÊ\ ›Q’PÌØA%÷¤üß„¥äoÌšóÄðÏ„ 5y‘é£8­©9³ÌGñ×3Άbå§jVÃ]µæÆFÓe_mµî ÜÆlbÙtÕK‡Ð£É*B(’˜Å8mZÚ4ÊVx· ‰ê:â¬:Þ^~îV·¢û|ðÔ“± ;߉‚sù_o·¶±kµÑ«EHÌjBœ¹ž˜ ]OÏý±z㙂V÷-Q\*›Ý¨ÐgwÖ‘²B”`rÎøl,gÝÕt§Èq¡íº{öAAóX…v{È ê-Ñ~Ã#Êw]Wqí‚o ’÷•Lš§!hÊDz[Ýç倿•³?F±¨Þ¶5®qáA?]ã Ãp™_\×h­R›d²l\ó\ÞT5ꇒ¤ÉëçÁz½e¡.á{EúB5w Úßæ2BTí=¤ú3õt¯ÐQ5ý¢Ë^çjê Q¼á^Ù»Þ 7xÕív÷šØù¶à:ânþâO ƒŸ="qg® äí…hr±Ž n.ó um{à›Èg² ‘…¬É¶sñg›¿ó”ýi endstream endobj 216 0 obj << /Type /Page /Contents 217 0 R /Resources 215 0 R /MediaBox [0 0 612 792] /Parent 193 0 R >> endobj 218 0 obj << /D [216 0 R /XYZ 72 744.907 null] >> endobj 219 0 obj << /D [216 0 R /XYZ 72 720 null] >> endobj 215 0 obj << /Font << /F36 131 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 222 0 obj << /Length 1241 /Filter /FlateDecode >> stream xÚµXmoÛ6þî_Á)(š1GÊz-``H“-2¬kÜOY`0í‘%M”Ò¦¿~Ç)’,;^º%,SäÝóÜ=¼#MÐt99[L~ý0óPˆCÏöÐb…f®ƒ©"ß °ÎÐ"F7ÇŸŸ6y|wb“ãZœLm?8>Ï£zóŠUIžê±/<åLpý…brr»ø4¹XLþžPpFE¾ vìE›ÉÍ-A1ŒBÏÂ}S³6ÈñøLÑõäÏ 1@)’ÿåé‡/—«^€=:C>uq@½½–}pî»­õž€:æ‡ûLä93lÓÀ˜Ø¶jˆ ¥7rØñg8ôƒÖpÉÑjÔ2éfÍ PYó™µp†] I#{^¨“óÕÉÔ#äx¹L²¤Z.ÿ".<]êá¼®Šº‚AúNf Ù»6šR‚Cטxûö­zEúÃÁ\ÂÒäÚTuÏõÃû<«ø÷ê¬NÒ˜—zŒe±~ˆîyô`VĬbúé‘¥5Øqó[ÁJ¶Ñ³VIj\,_ÿðõ*/˜/,‘Æ7æmת|z„¸÷RF?ûž`Ïd¬5@më›%C­LMÇlñï/ õ ;kÄu §—,MÐÓvb»Fº¶¾fì®Uåϸ†áºy#n-ýøf( DIf¡ÂÒ` ­;™èFW/JI-QÚêf•d,ÝN«¨ò’!¿&Q%uZõ²¤r·aÜhV²hYlIôr×›BNÓÖN;v$º‹Ë$3®­sþ˜D.ë(âB¬ê4}2#QÉ ÿbý¼JƒTò1ƒÝ—ÄÏ[ò¶\q”悆¬Ñ'gdC ‹Þ¼©ǨžùøIUí|-[hS†už L´/“lX­vˆïH¿ýºd-®•Ûx.b£–h."óx?÷æ1™‹d—|J^Õ¥Áa‰ Ä´¶vïÌ£éöw´(kB#*Vš¨¹úØ¢ÿ Ô¶|’¬ÛîMъ溣”Õ4¨roš¡!‰fŠ)#¨ÝôY½Ø[BÔ Ìâx™?oÀin5ýa:Õ•vˆýž§ÅÜjûŸ®B ‡ÙU‘1^ c.ª¹%W7° ´ ¼Ì-m«Šlmm昦E^ÎSNî72ØËÚÁ}\Œ§—د! :te¡ŒR袆ÄËy¸ÏÅv‹Ú>&ðO_•„²Ã¡dÙšœ°‚3V-ÒaÙ>2·>òÎ%äULÎ;Lb~W¯_brÑ9©É"YRG¡C ‡sK©~Y•u‡$Õ8î‘úÀRÁ÷RR‡Ã¢9@°r­fw«Š!­>–0A K‹ªù~¯&ó!Áu[’ êÀFÝÕÆh&«æ€Xaµrg¿Ý{ÈŠÔ c™æk,xuÅy*aÃw‰Ÿ_œ}½l9ŒhfB{N"蟫dÝ?:¨Žp"2¡åìØ²î©ì*ïÆÊh<Ë›Ch]È*²}`hfŠ'QñiÊžƒ]¨ý¦lÎ(|ÝvM¹ù¥KyE>ä Ù»F»@ˆÁ+n«Û—fÓý&†wææÚ|ön÷®ƒmïàËýö¯`ÂDpW£˜b˜mCfÙ¦HÍo WÉ]É œRrü¤GÞç1שölìx‘2ª7P!ˆÿÇ&YÐ endstream endobj 221 0 obj << /Type /Page /Contents 222 0 R /Resources 220 0 R /MediaBox [0 0 612 792] /Parent 224 0 R >> endobj 223 0 obj << /D [221 0 R /XYZ 72 744.907 null] >> endobj 220 0 obj << /Font << /F36 131 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 227 0 obj << /Length 1583 /Filter /FlateDecode >> stream xÚÝYÛnã6}÷W¨ÉC fDY×úÐM³[,vÑmb éBeÚV+K.E'ñßwx“EÙqâºØ°dZšž™93$mkbÙÖÇÎûAçúCß·"ùŽo ÆVàX"7ê[ƒ‘õÐýºœ•£á¥cwÕeÏ Âî¯eº˜‘‚%,+‹+9vGr’TD~ÁȾü6øÔ¹tþí`Ðc[XÊuQ`V:ë<|³­Œ²lÔBëI<5³\?„knÝwþèØÊFlñ:±äÍÝdžT?D>î[öPˆý­’PxµtCFˆËó1Âa¸M<í¹ÈuB%cݲ­¶FÈÆ¸©‡»AEAXK¦Äom7]æ†V.ó]<Y-òHºÑå;îËÁÈs¬¶Qä©ßÎSö|Ùóm»û³¼Ü”#Ïìý"ËG„þe{v9ghœåî±c›"*óý<ƒx¸Oi2_½>-+v%ç_ç%m~¥I1yQ:%IÊJŠÒ$Ïÿœ’ânQY1á’+T±„²×Þ¤‹‚?]?ÖÛyNÉœ5gp+FdTóQBé‹ ÎiV°ú1ÖP=‘ЊP$Œ§$Ÿ›¹}d÷#aY„Õ+ç½ãýoÂç\Zü%É y÷÷b>Ûøè©mÉÆÒ„8.’‰cR*¦ÎâxVÆñÙÊä†þ@Ô7%¤ÁJx'ãÉ¿FBž¦…Ñ.$¤sº¾" ,l#Çïó7}aùúàÝŠ¸1D±)™ÛsaŠ@f„|Ëþj¬‡žÇã¥Uî³™øš'lrèö9™Ísbè{|vBÞÏGÛÝçº09ìÂÝ+5MêÖ‚_¥îu?80'äÙü×N0F´£Á%+ºç~¶û(ÔyúÓõ¢¢×ì¸&Å£Lžù’MËÂô½”ìºðâVºô÷v+C/..–ñ‹"¢âCV•ú)ð5f—ßy/ò|)o3þ ï5ÈHð^d¡Š룤®ƒ |ÊØT cF×O4cÊÊQ”I‰RùDòÜY€j.e1Î& š õ,‡¢#ª%ß¶­ßÀJ†12 tÜFèØf¹x¡*·…Štà#P¼`ŒHRA–¥ÿ´þ-&µË䘖³}M€NBTÙÝxÐñÕ Õ[a §(^”Æþ.Úƒ¯¢²Ë×vwBOË4¼{ ì)« E4„„Ï"ô4ªÝÚ %ΈÊC˜/%C I(©–EzB8Bð$0¿‚ãž·¦ƒt~ß`¦}PQR5*œ‡*pØ)Æ÷¡T×|Š% j™q¥Æò䑨!ÕëÕ°U {ZÛ©ºèóM¬½ÕÂ:îÏåd ÝÁâN7·}ª‰ÑÏTMò µB«¥Ïx¹ÂVõ!#BQâaJt–ÞˆÊm¬Ît¡ ÔrWÄo wÐy,–ȦÊõÉîiú„0TrÁÝL íMÅC÷l÷Nä¡M¬ûˆ8kÏà¥Eüœ–¬LË|W¼ýnÅQ߇jíïTƒdoŸUûǼ&ªWÿ°(RÞ¶UßûoÖƒ)Ñ댄½Úª‘à;Oz2®‘}ã4Q[X£²¸P·C’—zE¦w‰”¨4OªïÄÿ·†GÌ×Ú}I4"ã¶ ® óí7R2š#wkµRm—é>½,YÌHÅ60‚(zïÞÌhë´Ð^£ (î³Õj_(–w¥^º«ŸôàS;€©Üò5Û|òî8kasç˜-h±‡Pö~€|¯†}@dçtiìø(Œj‰çz§bV>ê=‘qIu:kÀ³™Þ‚ k f ¬ªSžÇ¼Ø¶Ø9µ{¾‹<[3Á–Íœ@på{%u—enörÏží¡^ï ïíÿo ¾á,Jªy)°©P¼™EoôF¯Tõ‘Ìö] ÎNõ®D£C{˶»Y‡ê—öfÆv"„±ÿA®”šIœ6g_Ÿ/é_¸µÖ8ü8ΊŒÅñ¦J³‹S*’Û2Ô±\Ũ< TÎÙûÔg÷Sƒõ“gáè 3ãˆÜs‘ã¿ù„|ãé½ aGÙ—=LJÔèÞL“9#TÐ޳álÿs6¤ <…íîÒ8×1³ÐüŸÙ7Õ endstream endobj 226 0 obj << /Type /Page /Contents 227 0 R /Resources 225 0 R /MediaBox [0 0 612 792] /Parent 224 0 R >> endobj 228 0 obj << /D [226 0 R /XYZ 72 744.907 null] >> endobj 229 0 obj << /D [226 0 R /XYZ 72 547.538 null] >> endobj 225 0 obj << /Font << /F36 131 0 R /F48 174 0 R /F63 203 0 R /F64 204 0 R >> /ProcSet [ /PDF /Text ] >> endobj 232 0 obj << /Length 1560 /Filter /FlateDecode >> stream xÚíZ[Oã8~﯈@#@¢;÷ íà £Íhg¡o,ªLâÒÌæÒµfØ_¿Nì„Ä-¥¹-Ò‚Ô6§Í—s÷wœ@í^ƒÚÇɇÙäý¥akðlÝÖf ͰL€OsL˜ž¡ÍíæøÛcœw':<ÎÙÉTwÜãßR?I’á,L“S!»"ÁŒˆàÉíìóäb6ù{‚øÅ †4Gç¸&p £ùñäæj—Ö 0\}l Ú.°‘¡9È.²·";üâŽU£·0\ĵÓùï½mP³MèÈ•ëŠmUÕ¡Æe ±éÀsܘm±¹r:·˜&±â'ŠäJF–tydm³ˆ,âþ,´ë™"²GGG'SÂãOI˜…8 ÿ!L²%ÎÓdÞç´ õÅOŸ¬Šâ»0aN|R†º‹zü‡S›[êq¶ÂÇ”e4LîÏÄÁ¬R#&Œá{y¥â¯V$ Ú²ZqRëÚG;gyO^jc@X$FKÂ1Þ_šnÃû âÅ3º™%Š|)q[’B·›cÐ:‰ æó‡o>ÿZ¢êvQŸÇH´P±N›Aà€hw@ÅÆ5ô¾æ ]¤»@”ü¢À4ÌÚÙ$^Bf#ˆEÊZz;s¢ õâ|»@ò$lS²ž ¼ùœë8Ÿ«>#´E4Ï$ÈËE ´ %QzôCFìX¼Q²¢„U«ƒ¥ ¥:Ó»ïÄÏú7G6J²œ&L¶‹_›Ùû:Ú o­S…yS2Ìp€m¹UŠu·ó¦½ §]PšÒ3µÄåY¸º¬Ï¦d=±raÙ•rïØPc¹ûû9[wnï:²j1{i{=zô8§1ÚÝÊ0S w 8HB7%O–µS  ²3§ŠAïž3uùœ|½PcÝëô¼tCiTΈB 8a²-øëI¿#ù%®˜‡O ÎTaÁ˜s6XÓg˜e)%`p«¬©Ô"¥1Ξé„[M«Fïg9Ž¢Gqô=gYµ µwõ˜-«ë®Bÿ¯ˆHBòcúKE‹Jã¸èðuc§TªäñJAÓx°vµ—˜OñŠPðY1öKH¹Ü#9mæÜØôdhhfô‘µ;C”â@I½Å"ÍŠH¦SO¥”¤ÃéÆ"Í«vCÚƒÙ‚Wguišæ÷ËQ'4a¶:¡‰r•úÔ}«¿‘î¾8UƻöÕËæ(žºxíã0Ü=@eÞ €:0˜”Õ›ª]„ýtç•p}è&ísÒÁ¦‰gа‡¼v@E™ Ý hÓñÍ{ROU]¦p{’À!#ÛòÞà¤Í©ó~ó¶O£é÷ˆÈŽ']¶š¤¤,E§8iTŒxÔIê=v0Ψ!…Okbÿö,àzûe‚,¿•ÍŠo…¶l#ã}Ý·)'#ôÐúôŒüï`ûâ’ë7tIB±nw€Z_ž b48èCÖÆ!u¢ð^ì  ®U\Dzá®$v=š›‡2# «Û­h](vØ%¥¬äwi|·MËé: X0JºˆÌ»é² ôÚp Â>g©Ùq[Ôêñߘ/ü=úbùÆ|±¤ûóEøÆ|Žè‹ÿ9ò[âÈŸ’…ÁÉ ³C'¾ÀQ·`Y„Æá_Ë]Òëï\ð¨Â{]Šp ·Xèª}¥A8~³„³¬qÂA8¼9Asž¿±´µe¤JâÛU™ l]%DòÎ\JÜ&pڻ݇ÓÑþ7•ÔVUgK‚÷‡IuƒSÉõVi˜dï°?s‡fÚH#'ªÛn\¸qÃȹ—‘ñº1êE8OüeuÔm`+e:JÓü½\fÊY–Vvob3/¹>[á ˜§¯·ªMýQV¯×Ù¢œN‹ƒñÖ\@ÇQØ9‰Vý»ë>c5ÛéŽaëÖÉx±Ýå±»Ö£‡–iäº=žð[ÐPh;„úœam³|o=i™@·w~ rý‰Mar.¸Ëusf¯"ù<æ—ðŽbÞ¸<~’ó4(9œfZ6ðôzG¡–˹ÿ¬‚R~ endstream endobj 231 0 obj << /Type /Page /Contents 232 0 R /Resources 230 0 R /MediaBox [0 0 612 792] /Parent 224 0 R >> endobj 233 0 obj << /D [231 0 R /XYZ 72 744.907 null] >> endobj 230 0 obj << /Font << /F36 131 0 R /F63 203 0 R /F48 174 0 R /F64 204 0 R >> /ProcSet [ /PDF /Text ] >> endobj 236 0 obj << /Length 2057 /Filter /FlateDecode >> stream xÚÍYoÛ8ú=¿B³}ˆ ÄŒ¨[æa›È  Ì6îÓìÀ`dÚÖŽ,j(*iþý|,/þ¾À€Ç¶°†ë¡Ð­dñÇŸ¶µ†ùß,¹qd=ªU{Ë "xfÖÝÅ/lC#¶ä¾µôà˧Ô Bv­û(ÂÁw!‡€<ôè=v,7öQ ¾ö\ ’òã²ïÒ#ã.9ï….ŠÃ¨Ì©µ9 ÚîªÌ‹¬TxRe8Œ‘Vh(Š=­·5-…Ò‚<ÀÓüƒ€ ëÕ‹ 9¾|ëÁŸÀÕ›z3zÓ¿NÙ´I3:LüÏö±œ³Q€ì0´ØF±k„—”" ëõŠÒÌ•m@ý<Á,ÞŸE,W#]¶}"±‹5½¯¶ç£Ø:¦ÄÍŠ×iÅËŠçs%>fž‚é'ÍÉ=X¸žã$IósJÉî ˆ$Ò<_§ˆJÁ8] ^Ñ3ñ¯DúzÛ9„auCªl·¶]cÃoFoúH²’Žó®Ò!‚k4„¾­—ÿ ¡jÛ¯œjò ~[XQ¥$³C™’½/7ûAGõ €»þZiC+œ6£Gšë7‡'\)}+ϸú™æÆ÷¤=”#4¸½>î®ÄÓMà0Hz)(f’üOo ˆÑtûÈú¤ þôín\¯ðvÙƒQ(˜ÃãÔúHzF;-xš‹Ñ|H¾ƒœ°±æ“ÉgÖõ(¥~æLèAYã‚®éÇ®^Y>•‚îÏöÆøËE %ˆõšÊ+ëaÂòMº­¸ö“jJU:˜ä†3^Û‡þ€#òžõC†¤ý,î¦+O'j)Âç<Ñ \,“âNyQ‰ øôÛ„äb¼,ž‰ÔužìœÝÑ1WӜ՞B¿SûÎOö4 ázñìÐÙs$ü:3Óç®u9+YoMÍ™ž‹lÉmÏ,Î÷ïÍh/5< Æ#ý‡¤Æûü¿Úµ§Àý‰\þüü±ÍÁV«œìéj5ÚÏRlÞƒX­ö ÁšÌ‰5×óˆ©:gL¬”´õ©{.ìHN'ócÉ_zø( &©Py¿\µa¼†Gk e• 4_¸¶;»ÝèIR³;^3©HÚÉ÷CÊ*"-[2RN×òÖÆõfE}i¯*íTÔPJ2óÎ\ê ð„ñ,é3•¢l.…JIcÀù2›ŸÙ+Ɉ|Í:ã™wd;÷Ayi`/o~ŸcŒgWúõ×÷Ÿ‘l ¤$Ó¯þ}ws{kæU!'Í‚#¼}™ölù–ù²3w|YBKÔ,Qìš{0Å‹k;}^äD-9¦sìϾ¬¤æ¥¨·íY)´æ VTáfšŠ[›Åls°©½z“¿ë~ð袓4?jÊ%fHa<{fÞ¦Ú«ô@›XnÈíê–dµ;©ÄQ#`@×bjè•?zšòmc,=a^é‰Rˆ'œaÆD?ÚãáËZ)HžÔ+”}Y>–mZ£c´CV:Õé;ši êƒCrý<8Q")ô 1y×6gJòº‹P5Pã \äÍ1îÉÈÕË5tC9Õ\J”P–úþlpÄ•*¦Ÿª æ «=ð­ f§0Íó¡†S±3ªÚPš™§´/i;z+¤Eõ/ˆý3GÊÌl)+Ò»Rñ5µ-ÁZÁXf®Á§%‚Aqø¦²y!šèxÂq8éÞ8„DŸï†bøS®[4Ý[ãð‹·Æ‡1õåêFí^i1Ûq}È»LÌ~óËuUòëû4¿¦ùƒió<ÁyËÇ'Gv¿<¾¼¼<FÜýv¡2IÛ]÷X«Œ9Írl²r2Îó€'‘Ò­¹UÈR½5–eìñ £HLVKM&¤Û‰ýܱÇþµfU7ïD ¶ ´+mã*§Š³ŸXv8Û×¶¦Q¢É²º­›«&·ë¦ÿ&·Ó¼1BÒ±¢¤)ÉJÖâ30¤¯,—ý¶"|ÝtU!U‰ÒÚ(ü¤Y{uU7?̉Ucùƒ.lU¼};žÜúz@É8lƒŒi¼(­éó'‹ÚK섊H„/U}Û+¶´€O¤aPíêznš²ð’’õ*ai¦î ñîTÚô¤K]]4jÜ£!6ý´.Ü¡{<¹”¾¿¾‚GàL)${Ÿ‰ùu?ü•ØÑ/Ø<aͺ®t IןÝìH¡ó õ‘hÅqŽ|ßö9½ç2=—©M¯Tìæ?‘üŽ endstream endobj 235 0 obj << /Type /Page /Contents 236 0 R /Resources 234 0 R /MediaBox [0 0 612 792] /Parent 224 0 R >> endobj 237 0 obj << /D [235 0 R /XYZ 72 744.907 null] >> endobj 238 0 obj << /D [235 0 R /XYZ 72 381.339 null] >> endobj 234 0 obj << /Font << /F36 131 0 R /F48 174 0 R /F63 203 0 R /F64 204 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 241 0 obj << /Length 1480 /Filter /FlateDecode >> stream xÚí[ÛnÛ8}÷W؇:‹š%u£l¶—(ºÀnë>e‹…"3QYLI9©ÿ~)‰t%Ùq#KI•V "Ërf83<‡”M–7“óɳS7ÒÀ Àü¸¾1¡€x!ô¨ æ p6ý{³‹óM×êdæpúJÄëK³(ã"}ZÊÞ³„EŠ•o0D'Ÿæo'¯ç“/¬!€q´^D@¼šœ}B`¡åo‚. ÁMÑj¼ Ô¯ ø0ùg‚Œ£ä¿r Ê›÷o*ZƒØû0ÄÁAÍD'þV{MGˆµwŽnO©@ ð\èàШØuì «"Œ+fr±G\HI¸U,¸Ø«ÙAGú(¿y“†ä½Élà‚Pg6ðòÌæÁG ©W&õ·Y?¿¹nãŸn8ÃRŸoNfBS¾º2+ï³KVÞ\G’‹¼òò7ŠÉk&·­¶-CÕ݉{ ÊÍzž¾èZ-¼©I´7:_^%_ ß.¤X5UÃqô…`£²*ÉxVÄ骘¿kã„ëhAµIãÖî]&CÕÞÏN½°â8&ú®îX:þWaz_½,Œ—=Û‡# "Ä*ÔAˆökÆ¿_¹£!BâãF¹9¹=Â;]ºòqaCQ#UýgÕÏ.•|œk˜äQr/Þ9R nÃ…žX¤|¹–¬>q%‰X.yº<Þd8| Ù3Á[@;5fgÃ^­ô _Í¥0T“†¦°¦^ Î#Åã—EÿE>Ò8o²ÇT 3Åxž7LíòH——,{§Õ0ٟûF”6®Y’ÛèÃéW¯_||c=H\ ¡¡ÀF¬Ë››¨¨b VÑg3põ …‘d‘e(Ý·«7¿ä¶ñ2â©Ê`ùîTHb®ºj#3±õlm3iì+WY>Šw vÍcö´‘lÉ.˜diÌnO}2!ÕWø\²8rc<³±ª†¾ÎîÕÅ1pM悸ºJë†ÑÕKUÄ*¸0p+±°’÷žì à÷:a‡@M‹ÓùIive_£|VÙùöeÍT¦~N,U9~l,.%I8V,» e¥ÑɈu²0ËOþ¹ÅÞ žþ™î ¬bJåÕ×î\YxRWI´aÆ­s–ˆƒJ‰°ÐzóKã…XYV¡2K³,þ¬à€×Ò‡±J~é§ZCÞîªìFòŒý žT€N/õMØ\³b¯I f5U=½óöܸ?—kvvíDL%b’E‹"`ª·ˆ×»ó]÷mfn(`סö¹H>òV5¼Æ‹uçK"Êk›Û?3ÿ.,:¢¯!ê“.q€¡K}ƒu™&¤&&ßX³KâT4ÂLJ!»nÈg˜º0 ^ù•ݶ՘<„D÷SÌ2î•^¬[2¼Ž’†,}Ënu‘6D6é ¯ÎZ°ÊÞ<¶(#ûôy†uZÍÔù½=ÔøÐ AEEx+ÉÍpèèùt`|ÚöîƒQu4ïR]H(ýEw¦’-¹ÊÊÏÍ }‹!`ôHŸt^ŠdÁÓå6tãSÏ‘¹Fæº}ʘYò£}öL~ðI†M/½ïÚî¦uÚ²Ù-ÔmÙöMØQ_±SûQÄ7nÙFâ{ŒÄ×ï¾í€;’`ÐA="÷×Û³å <buºÔ—ƒ( µÒ‡ûLÑÁ>Ô™©u¤Ö‘Zo=º+©7õ5æê5ñ§:w—:z:Ò6gK·é0¯µS°¾à·`wOéjžö;e 1Äù·@gúÚ~û3?ƒûŽŸËHžèÚžnJÉKS]ÀóHÇf »µHë(þù/` endstream endobj 240 0 obj << /Type /Page /Contents 241 0 R /Resources 239 0 R /MediaBox [0 0 612 792] /Parent 224 0 R >> endobj 242 0 obj << /D [240 0 R /XYZ 72 744.907 null] >> endobj 239 0 obj << /Font << /F36 131 0 R /F63 203 0 R /F64 204 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 245 0 obj << /Length 1650 /Filter /FlateDecode >> stream xÚí]oÛ8ì=¿Â‡=Ô9,ªdY²ü°—v¸aôÚîi·×QcŽÉN»ì×eɉíi¾v놶@,ÑI“")‘ÂÎØÁλÞÅmïü-åNˆBîqçväžøù!un‡Î'÷j1͇w}»ó¢?ðá¾ÎãùTfeT&yöÒÀ®e*£BšA¸ÿùö}ïÍmïkìƒ×GœxÚûô;C€¿w0¢¡pªQSÇçž©sÓû§‡-ÄÑÿjì˜Æõ»V.'Ô C‚ð­˜ °%öA<‡ÃWÏ߆;<ÉxÅ:c[Y &¤IFÃý€¢05b%ÑF̵00Â0 ù>üÀ‡ÃäÚ*•ûŽ¥ÂÃ*s$Bß(5* ©ÊJI5âó·¾hÌ ò‚ÞTãÿÅ +eÆ#¿"g*Z`f r(9NŠRªb,ޱûêUUýdÑÀcÄô$J šÄ0ãá.7Ÿwç‚4ÄAŒ‡F¶ðíÄCT8 ¢ËȘX®hÕ½×O xó-šÎÒ¶1>¢+>  ÍŽ }¸8lW" ñúVdš‘¯Füxè[SÇãVkB!m‡B&BD}«îœÏ u~—dç2»7.b¶('yö„]Ä.1™ÎrÕ ÷‘JªÅ©;°5¨Ö¥Êåf¯xÒ"y|cÔÙux©|ÚEY}Ž?5ödµI™U»äyŒðP¡-_ö,.«¡-!bÔ¯ãýM©ò6žÝřа·45ÖßH(‡ÏBY H$‰ÒSÉ¥¹<‰\†Qe®ä” ç—Rù»"|#¿ÎÁÑh^i9\:–€Y5¿…tÒè^^æY)¿•/Ml¨Å¦×‘}sèùe%°§¤È³Q2ž«î±B›sÛNšÇI6þ­Ã内´Ç‚Þ$¡íŸÖŒO>wQ‘Ä—•›çŸ ¤öÎRlJRœ‚å±,?©NÇð:‘ˆÈ{™j§`úõ›‹ïŽIh`ë ‚§¼“Î-’ïÖèùÜîšµ“¶^aƒ«þur î´„õ ¢—Zµ”AÏŒ„mÂÃä”T×{M|×âZþІÀ½²$8" éaù”ž: I0Þ”‡|Yifàá B; ¨üY7OU7õ¬›§ª›äY7?U7˽ÓÀ£QêuÝZós5NFZ;…ûÔ¿Ö°›ˆlUÀ.-•‡£ÛíÐèÞªN‘èÈZ~’‡<ÑÚÚ©y¶áxW'Ea£gÑ/\Hjg õúŒ—Kõå¶¾­…ÛHäѭ峬•èCꫳór:;/Ë9[#|píkÿªÉZñF£ p.þ‰µ/Dp¸ª}…?¸öui ˜UûªO±+Õ¨¢¦QÛ+$—kà—΂¦a㪊ö$>e†ù ™æ}¹€‚s7)Ì32¯ó$þbš²O˜k n ™ç¤9¿Ìí³*£W­>q'vÊ ¸^qÜÀaÉ•ñÌ4Z·g ?”š†Iéþ\»q;§F®sÂUˬ‰ÍTQ@™pÿ-s•J*ïD±ïsóÌòÒ4&‘fä^½Ô¦™­F6xÒýÒÎ4Ÿ®[I9ƒ¢D¸#)S)Ù™PùM݈ þ™^­ Dú¥ö¥Êô“¬( u/·ÐBÛ¶E?±ï”I%—C5 $«±¤Ræyª…Éàtž(C]ô9³©‹ÝJ­ÄÈsqT©5`Hˆí>Ã÷ƒ1GZ—DšuV‹öÿ+³àóCʬÕ„Ó±²! œãÊæ× cWMãÖ€MåùCÈœ.ÒŸ&„í»×/ÞAxGÅ¯Ö AæC,Ùù‚àÆË‹>DT°3ÎüÊkRñ$š•õe þ“{Þ†«’;™«‹ÖÅŒvådùnq endstream endobj 244 0 obj << /Type /Page /Contents 245 0 R /Resources 243 0 R /MediaBox [0 0 612 792] /Parent 224 0 R >> endobj 246 0 obj << /D [244 0 R /XYZ 72 744.907 null] >> endobj 247 0 obj << /D [244 0 R /XYZ 72 633.788 null] >> endobj 248 0 obj << /D [244 0 R /XYZ 72 198.035 null] >> endobj 243 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F63 203 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 251 0 obj << /Length 1667 /Filter /FlateDecode >> stream xÚíZ[oÛ6~÷¯ÐZ‘·š&©{±=lm C‹.õ[VªLÛDdÑÕ%®÷ëw(R¶$_Ù–KK:"Ïõ#y(lL l\õþõ†—–k(p©kŒ&†å؈xáÙ>²Ëóýj.ÆŸû›EÖPÏ7_‰¨˜³$s.’çŠvÍbfL=„ûGoz¯G½/=ƒaƒ¾6ò°gDóÞÍGlŒþÆÀÈ |cY¶š¶ëÃ56>ôþêa-(1ä:5ÔÍõU«ë#—X†Gä÷ g÷œ5÷ŸPà 0²C,°aQ×Ó,¶;(j€0!µa$Ùö,xþšqÊŒÉNΕ10¢ ²møq-Ù¤E¹Öž…[<ëÚÒ³Òø˜ × ”SG3p•‹±9q,–<™ªGž©k˜¨ë—‚G·êvÁÒ‰Hçaé®ÑŒUïÄD]óŠm¶J¢Y*1##¡‹ôÐp@0 -« ¿BËÅÌóââbË·Æ£¢ìàñtp¾¿§§K£8_ˆ4oy#a˲0]©G0h³ìQãÊD»´Ai ÂíZ„·d›¤bÞfYªCáÇ#še"¼Q‘¾R!‡t´É`î,žæ¥ýQï=¼´ýšàÄócAG%øÛrèQ´xY®zv7Ç b{6«ä|ÎÎ Pô*#lƺGX¶ n Y (Añs®&<çaÌÿa­|ÍY–?âìl¹®5nT[»ŒýSŠÝ l"ä·ÆšØÊ…¿±ƒëQqñä»O7&¡‚8Fä˜Î--L¹d´e$Qt³¡[F*sL‡€¥s·AÙX7åj “åazª05nÚ§7ÏYZ.³Ó¤ƒîÃ_çlj Öpâ?b8Ñ ´³b‰ÿøgúåŒÇì>YQØÔà#3Oñ×]±¾‰åe¨K©<·)\ʲ">vÆUµÑ(eáøÓLÄcXïJÙ”g BVƒÕû$$T›ïóÝUNžÌïCÚ£3eùµ6IW‹àƒ}©ê/­„¹—zÇÃå©P)Ò!Áw˜õ l‘ª•°Ùw»úÊr±8 Ü´gò3 õ"åIÞyYuÍŸ´sC÷ÃȧZç:e;Zdù³gãS…#.²ƒ5¿”})`ö͆‹D2>fAùäˆÜ/Ý÷l—Oµ?»¡=ÂÝe – †pÀýÚû?7%R“eí}‰Q¼Ôj¸OÕcra°‹Y¹@–¢­Å»Oå¯Qýtl ß?¢È¸Uë”,¨CºÔ:×ÎÛÌÔ•W&†åVîo¯7Ebb#Ëv©®%ZX¡ÊqÑf„tÌ쀙_ ç‹X×/SYrIÎ sJÊK1f'¤TyÉq¨( ž|²Ö¤½ï;V—«8³áÕèÏ}ÚTʶ¦é.Šl ÷r{âl€ömÈ5Ê{5î4 ç­IÄ ZýÁÁ®[U‡yYêwLuuËz°|f}â˜7¹fÆçEæ"UïóY˜7{.SžƒÖŠXde¥YÒË Š¤-ú›«i~«èŸy2æ}bÊvÄÌàõ–ÇùLÓ™fŸ«æQ‘¦°,ŽWŠ<LKžÝbJÜIÁõˆ¡z1Iµ?ª…ZºPV*äSY+å“•’•Ô>´‚¾¥1rõæ®Os €/OD¬$„Z$èƱ­–ê…H˜f*ÔUñ[ˆLÓß©nw<Í‹0^˳9‹‘Ü™äxÇ#–5™…ªsÂò¥4°Ho¶KÑLÝrÝ¥ÈØ¤ˆ÷›ÁQ»Hex‡y¨î"0=ÓMæ"áëV¹qV®²a’l+ñP`éó¥w"g/T³FLÒy=Ñ—’íÆ®k·7’0QÞ-y¬Û‰¤ IÛ£¤«kÌ“âë`oÀ¸†` ø°¶=å¸É³†eòÁ™€Ø6r‚à”ã¦Í0õ㦊ñ·›¶€ðÛkáÇOƒÚ˜§? ‹,B®Yr§Ë+ÈïäÇ<•ù°‚måü‡>pÙQâïp¸ ²îG gvÿ‹0û¯¥Xðè6f' qÒ1ÏLV‰$š>܉ñ‘¾†v9âÑÇ=·ÇXoð(p)t‘áªàÿ¾î‹¿¥,Á—+ ˜ß0 ¦F§ CXRÄH®eOÚâuŸØ·?g¡²Zq¯ñÙcÃæáÞ_Ýl,lØ‘Í~–ä.¥÷Ú° ,Šj§á 0Ü¿~ÊìÄ endstream endobj 250 0 obj << /Type /Page /Contents 251 0 R /Resources 249 0 R /MediaBox [0 0 612 792] /Parent 255 0 R >> endobj 252 0 obj << /D [250 0 R /XYZ 72 744.907 null] >> endobj 14 0 obj << /D [250 0 R /XYZ 72 375.042 null] >> endobj 253 0 obj << /D [250 0 R /XYZ 72 335.888 null] >> endobj 254 0 obj << /D [250 0 R /XYZ 72 308.534 null] >> endobj 249 0 obj << /Font << /F36 131 0 R /F63 203 0 R /F64 204 0 R /F48 174 0 R /F39 133 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 258 0 obj << /Length 1470 /Filter /FlateDecode >> stream xÚíZ[oÛ6~÷¯ZI€˜%uW>lYR h‡®ñ[WŒLÛjuE5ñ¿ß‘H)’¬:±di°±iFüΕçBk+ kï&¿Ï&¯¯ [ógë¶6[jŽ®9¦‹LÏÐf íËé§M”,nÎt|šggSÝqOÿHü¬ÈÀDg‚†áߨÂðG.™ðj/±Z3€õú øºgkS‚‘gy’» J.Z´Ì‚,^¢@›3%ƒSãB¶îÒùér°äIôŸÒ—K{ïÒµvø4cHL)]/¹)p-[iÍH¥ ¨©ÂšêyàV%âËéá~^vÔð0‡]3¼<+ÕrýçÇOjDyHk¯}DAÇúÿï·A&ر`ŠüTÇ&2 C™Áo†´¬ ò eŒÿ`ÑlûG´…«C43+¥|,i_—¤¯JÝ ÷P}0­U³ ‚fÀ;¢^lJ›^½\$°CîĹš 馦§–ŠÚ±rËÁRˇdµ âÕSJ'‡Î&㪩°GA×êЃ{Ô”%‘·ºr›òÀútÅDaaÆ‹ u>iÄæóF¥º·gã'Y.¹-Ÿþ-MÃÀ/;/9qyŸžiÅä‡4ˆº8»e°Êy©±Ë;Ÿ¥¥êv&|Œl£Nø…s=:«Àóý4$yÓtÍrm4 TÍô]ÝCÜ–fNNN”ÔDËKðˆrà7¥—S²‚(‡Åòñ¦jn™[v ‹ßâ)ÄæÌç-õ\aE‰7Ÿq æó]îe”Œ…Ë.ƹTB&8¢¦qöï(ûm>4®UBEÈÁ2n¡Oòr,Ò‚P—®vdã¿û¹ìîË™ÈyÜ—ER-É Ù–[‰~²7,ºØXe‚{Óu=µ #WW~Ӝ٠„¸È€Ü§˜{•ÔçØ?“õÑnýjhSpœí¸3 c¹À™ÜEÎ>hº¼œÊá»0¹¡¡_å±_8[ö¤k¨ÁRÏÖ¬8•/†”«»×Äš…)SåÀ²Ö„QÃ&à-²/UTAõ•iÏ«=Pr õ©O1,}ùaT½ˆR(^CÃ*7( ËQ¢Ê¦þ,åûm×uxÇAåTyJ!Æ!+Ê2S_Ö©U MaZûáðÆ2Ïd[Ù(™_ª¼ùÊ•/†¤ÌRûÖ½Ø{¯vc¿-‰ãþ¡ÿþ›Ù>ûH5ªeîÿ¦û]x’ç m’“›ȯí+oìÆ½½·FG—Ò[JÍ@TX´#fJy•^{zíePÅZ«…>gTt'åIñhNë“f4ªÙAÙP%DU,«Êà1"JiÔ9 Ãüô-¯’-oºëŠnøßC¦ÚÝÛuà¯;\TG)gYmLjEÅ·²ÌÉ£T ±}î¿wJ…µ–2ŸS(²Ðhyÿ?¯©}îÐÅÑXÓÌø&kG†0¡‹Žëõn%VÈDÖÙ]÷[)’á»ß;'ynXç´vgEš'ùjÝ¢õ˜;.­{>–i ⺮ÓlßêÑ٠ѽÔSkH½·®Y&ÒíGß>ê½eGD—%öÙT·mÃ:½XÓTm”¼üÊÔõž{S‚Ná)‚O7ræòŽõx;ôƒ6ÿ1ÚB& endstream endobj 257 0 obj << /Type /Page /Contents 258 0 R /Resources 256 0 R /MediaBox [0 0 612 792] /Parent 255 0 R >> endobj 259 0 obj << /D [257 0 R /XYZ 72 744.907 null] >> endobj 256 0 obj << /Font << /F36 131 0 R /F48 174 0 R /F64 204 0 R /F63 203 0 R >> /ProcSet [ /PDF /Text ] >> endobj 262 0 obj << /Length 1719 /Filter /FlateDecode >> stream xÚíZÛnÛ6¾÷S-Š8XLSg©À€aYR´[‡.õ°‹¬‰Ž…Ê”GIIüöãIŠD»®-)m2,A"™?þçEhÜÐx3úy6šžÛž‚гðÜ ²°3É‚£NýëΤ+n‘NQ’ã]vo[Àòk»?Ê.©Û5‰Ü Ð7ÝŸsc¦C²BÞ̳’ĺ'©I–r„æˆK3¬Y0d¡Û®8|•Lœ) ðÝZb/:xº`øFˆígÀ!Ks¡0ä% AdŒõ€-àq¨Pá6G6¹æ~aUL_±¬ ™•ŠûÇÍ-qRùîë½mMSõ–"¤úS›Ëû¢J\Íl6¯m•Ê"ŠQQMBò’cz‹i=½À÷ÊPÌVA×lµ%-7}3ˆ”iþ¨Íl–f(îm"}RiåVºÄy<ˆŒ¤¸/‰”G]"eœt™u¤‹äÓ6YDÏLÑ#ÊbñÌd± '‹ä™É"POºŽü¯Ï(Íèÿe¤œô–Ü¢4QI¼EÇ ½…1qmà‡zç—¢Ûaòíû,¾.óïTV\z‡øà.Ïs'ªxé…Õ8‹^8‹'é…ÃbSŸŠY‹t%%;m½å€JS¢4l¨J˜BÞ‹/Ñ«pC`úêüGêòdY¦¨Ø AO¶øX Zèý@N–+uW3´µ¸×„¡-²Ê8ô! J’¼w.Ÿc›¥5"eçBëK¹k/ãû¡Ã¶Ë äfW¸åd?:ÈÉAî„#Â\3í-Þ`Ö©ë¿ëºßx­‘²(¬®{Êwlõæq”5ÑQºöI^`2;ýÀ-‹3ª’F3ðž‹eÖß­½”"|ynwÕß@ö·¢ )úîZµ ´N5µTDBÔ¦ «¨äÖ@mñ=‹í¯ƒ%+6»dôA\¸N¹_©ûwPÜqIÇWø¡°ïh—§ˆ¬øM(N´ ýÚ§½IµBy~xÞ˜xpÝàqw(iIžM9òg^½?+2yåÔkÕIŸšdˆ(¡4ýkÉEIxø~‘¶–D# Á‹¢Àõª¨F6U!3ž¶Kþr2ÜÏËmq'…ºÒ^JMÿŽ‹»Œ~Vvs\T–Á´÷_å)ò<[$ª:¯®iBÊû*í¤k•)–h}­üã­r“EV¦qõígÜÚ¾¿‡Dÿ›òȥ䈔D#¤œ®Âœ')³=S ©^jÑO¢«Ç× "SŠcF ·êé]Bâì.Ÿ’ä¾ûòßÀªûV6R¦à‡‡z_•ü­‘‡¦IyÆ…ð‰Á0dUµíW‰ |&|§W,Ÿø¬Ø5ƒ¯Z8üàÃí7Œ´b±o•®[F#¼ÌˆnËõ„U•I²ù\Í+´Ô‚«W^Ùü᛾l¬hvS©ÒÏ!ÚâD o¼3W~R’HlÛ‰O\m•b”+Ü¡u½B_Š)“ãm06è¼MhQ¢´råÓ9ŠpþƒžÔY—Á÷x®’WWZRmzÂC†ëZ,(º‡¢s øHG‚X¬ûExü¡,Íh‰?(c=îÖâs*â/6Jvò…Êí°Ú=Ë{y@¾f­ßò›¼y™â"šrñTñ^ïT‘}àã]ÿáºÿ=ª Æ7e¢Þfm©–žRÅ¿ÏQ×Öq_—ià ‚§j7÷ZÀÜ ¡Ÿí­e¨®­SÈ®,oïCÈ›§¤„Ã(2-)sX€éвÆg÷ˆ•pê ô9åG¦ù;#^‚ñ‘Ó,fßÙ ­±é·¤Ë$÷/}Sµ endstream endobj 261 0 obj << /Type /Page /Contents 262 0 R /Resources 260 0 R /MediaBox [0 0 612 792] /Parent 255 0 R >> endobj 263 0 obj << /D [261 0 R /XYZ 72 744.907 null] >> endobj 260 0 obj << /Font << /F36 131 0 R /F63 203 0 R /F64 204 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 266 0 obj << /Length 1535 /Filter /FlateDecode >> stream xÚí[moÛ6þî_!$b#6Kê] ´Ýš¬E7l© È C–h[‹,©zYl ûï#EÊ‘dÕ¯LâN‹¦ÈçŽwÇãñrÒD‚ÒMëý õæZÑ% Xº¬Kƒ±dÈ’¡š@µiàJwí_³ÐudØÎ’NO6Ìö¡“ÍpÚ©]Öw‹}l'˜}Av¾>µ> ZßZˆÐb¸*0 !9³ÖÝW(¹¤ÿ“b™ÒC>j&©ºIž¾ô¥õ[ r‘Dã‰Ä·7%TÝ:R$iÀDúZdƒ7´%zÃD„;™Œ·ÖA@IW #“C¬2¶–U @„Jdh·j(À2Ì%pŒ¥q#r! d² Aú¡Ó!µž[®T]‘L¢T]-” u`Z*Sêy§§CØþ%Lq—5½1{.ÂŒ5ì³F–xÁ„5'é½ÌZ#<‹2@2GI§¼oìùx˜à”}K¼I`ûœPžA˜æF²ËÂÈÀ‚ÀÒ¬Ê2¼ C7s° :=$“žAX°1/ó’nm…Æ.ˇÛQä/ê+ }?|XŠ ²Sgz8ã=q?ç‡só1pñüªÐÕ,óí4ŒA´8¹øa7¬­§RKùj0Æ{ ñelI:œêåååZªa|¿4+'ŒbȾ}Ë{H3»k_"mò^À¶CŠI°ÎÂ`8Å~ô> †Žï9÷Ø=#G€J^Ö„ý1 CŠ÷Ýý©› Ô¿e^ººã‡ @¾iñIjÇ5úȬÑÏǤߣ ÈœÜÏ:S;˜äÄsã*/¿@û² }Þ@\ÑŸ–xYðÄ!»áÃÐÅ qý J®S»â¥ð¯0¤œÉ4ưÓqhgiè„A€”ºÛ¡áU"Ø)ÈŠÖÕ¹Só)œBŒÓ,æ¾ôÚöIPx8´Ÿ‘  Î;÷EìeécÌ"d‹˜jÓ JeJwÉyã`1šå +·0$B™<÷’4Y’¤|PzWâÔœãNb¼†Yn°¢ÖÃà‰Ì\Ÿ‡`}öøRœ£”Ù)coÒ¯¬î¨Ã2Hɪ*ù ÷•œ›Já†Äìj)f¯ñæøv’Ô1óõÈäÃ@³ÜCWxWÝ»(ª`¼¹VÍM‹Üƒ¼ÉIR9³Ñ[pOƇ£¿ˆóªà3”Âî˜òIºRRIÑÄP¾¸ô (ä^TÅÅÅE“ª×âÖÅ9˜&Xæ f‘éÕ6©]n~ÿXÜâúý€LŸâøñá9ùÅø`WW¹ UÔYó‚ NÍøGYéy´ þîëaÏö˜H.‡Þg^•X]¢I6 0·ÏýWN$͈Â7]¥çq¢ µµìÙlD ç¹ØAk™‰q§>7P€{”°·mëˆÔ "‚X±Ü³º[õœ o8ô/¼ÙN>‘Pu žˆ˜Ïüƒ|cƒ¿`Îþ+‹jÎj’yœ¸cûþÈvîùWrڲƃçNŠ´ ;ƒ9ÖwœîÆóµ¬¤#K{°…½ËmÖþ‰¯øÿH¬;V,r»íÞu3¦——]üE~3ï×P˜Yú¶» Ô*/üü™îL¾¥¶ß õB”lF;{Óé˜sHßÉqºv™ÿ}·ïüg{Û¤ÙWdUYÙËZWkRD˜- w¯vHýŠõ 54{üMi_ÖÊl^ƒ@›Ê‚žQ¢õÊ!!"Õ_T¢M…NÏ(Ñr­…iò’¢—gCõÔ3J³Z`%fÛdzáó—ÛÔoWjØ5UÈ4÷(_­X—ZQ/X_®Ÿ?+¥õš d}ëÊúƪ•p„df5Èìôd]W´öS;Jiy+ì'!Ž,7üOÀgoÛd‚íëù0·i5E5"Òüú{“, endstream endobj 265 0 obj << /Type /Page /Contents 266 0 R /Resources 264 0 R /MediaBox [0 0 612 792] /Parent 255 0 R >> endobj 267 0 obj << /D [265 0 R /XYZ 72 744.907 null] >> endobj 264 0 obj << /Font << /F36 131 0 R /F63 203 0 R /F64 204 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 270 0 obj << /Length 1711 /Filter /FlateDecode >> stream xÚí[[oÛ6~÷¯ÐZq€˜¥îR€>¤“µh›Öö0 Y!È6c ÑÅ©¸¶ÿ>J¤/¢Ϻ%Y±ˆdÆ<<<ß¹S‚ÒL‚Òeçí¨óúB5$؆bH£IÕ5 ›¶djÐlUM¥ëî—û šŽØMðqO1­îy4I—xQxÂÆÈG.Fìƒ àñ·Ñ‡NÔùÞ‘ébP’%S¡t5`BSšëoPšÒñªmIËì[¤½úÒ°óµ9£²”þÆ3‰Ý .·¨0dU2eX²±—²I7õ5õ K¦Ü)ôûö>P24(²ÅIì2¶—U@YÞZ&ÖLئµ&#é¦2ÜFM³$‹¢fh)j2Ý;T)jЖ­1Ôþ΀ÒE€¦Ñ?TJ”ž0B Sª=[·ÙLŒü6™¯H¿Má2"¹‘”Èuä&Ñ#ŨF{³Ðõ7!Ñ$ C4!Bº“Tí0½•Þãë *ªÄ„{â~ÚuÏ€°F$7U@F5¡§úœ-GqÂÙ.RöOǨ'ôÖn )ÇQìL=×f)sœ/E_qap¾r#ŒØ ƒƒ&æõ%©q4‹Ý€}LØÝ±kœ„ìfé‘9¢bäsQx§ÚðK.^ˆr89†ÿ(NHdK1³5õtªnlq±aŠ©n)LÏЀN5¦§ÈÀ¦Þ ƒùe¯¹Ÿ—ErÚË¡h,/™ì/ÝÌ‘OqÀõé>Ç]~^©ÚÄõý±;¹Å'\?þ£)ºgW2GAõõóÒî’*£. ¾gŠvœ¦’Î7RJ6'»=2ØñŽ)Yeåñu³ïÿH kLé©lR§œáRàÂÖ‰î°ËÈYFñí ÅaUGg Âê«Ø^º‘mò•ÌMS³¾L®˜kÚÞ¶K÷óæaìÎÿô=ñÈÓ$Ñ­þ×Ä›Üò–B´è% vÅì&“qN·”°Þ6H5­`Fnké÷'¶ó\ǧ°Î[¸1 Ë€ÝÓµ®K= §Á'9ßÀmðÆwg¸$-qþþìãÕ¥sÞŽW8¿¿ýê|9ô?ÊêÉ_-²÷éêüìãIå´ŠÜ/PL{F{úßÏ.ûN0¸œÅ\+â0v5«-vßþ6]}:ï>^ û…ìr¿äPŸ¸¤ÃI®laAWÚ§ç¹Ä#>*Ûy>ªÒóíg¾¼ÂÄ£=}Þ†Sº½éZ{J±@cë ¥õSš0.¨¡GîÝ XÈšNÐWòõÝ`¹È› Õ¦wâÆ^”àü Às–Óó<ìð,›(çE&V.FuY9$Xe+EHÙT¨¶Õœ:Ü9ws´jÊdHzáŒ} Y°„&”;i(þã÷/š8"™¥¥ú±·QM"¸‘бö‰ÎÂ÷È£Tà™W€µ›ú> ‹â{‹ïïìµÂcò‚êõIäêX _q{d×ke™èúÙ¾W¸4#"~˜ÆŠ¨Ë]&Ývx{Q©•GEþªÈgl´òšc_íy•o2PuOŠÒíÿpƒ…Ï_“¸ˆÓ·*¢ pÊFÞESú?U¶Ò•íTrÿzÌz endstream endobj 269 0 obj << /Type /Page /Contents 270 0 R /Resources 268 0 R /MediaBox [0 0 612 792] /Parent 255 0 R >> endobj 271 0 obj << /D [269 0 R /XYZ 72 744.907 null] >> endobj 268 0 obj << /Font << /F36 131 0 R /F48 174 0 R /F64 204 0 R /F63 203 0 R >> /ProcSet [ /PDF /Text ] >> endobj 274 0 obj << /Length 1916 /Filter /FlateDecode >> stream xÚí[[oÛ6~ϯÐZIИ&e]3tÀÖ&ÅŠXÓ{芀–h[­,ª"'ûõ£DJ¶Õµ.ÎÒ¬ ;„xxøû!µ¹µ×¿]ŒÏ'–æ×Ò-ír¦Ùºf0܉vékŽþ¼]Rz¬Ã£”tÛ9zE½tI"Žy@£9vAB‚‘ ?^¾98»<ør€Ä:PC’®lhkÞòàÃG¨ùbüÁÄu´UþÔR3,G|†ÚûƒwPñˆ´ì'™kòËÅë ª–,4ÑldY[)ÛbqÛ,©Wh8Hp§‹çÝm$ f #G‘¸ËØVV]ÚX&6ì pm§$œmÖH¹(æÃ¿ÄÆÅ#µ‘ %TÃÑ!TËÈ„Š&0MG³¡×’e$œårZÓ6rJêWN»2"h‹y 2I $¡É•àÎÿ†¦>ºY°e)F+#’Ø“;Mú=ºÆaà,Ç ¢¹ü û~Bû© Ù'õ‰Í ŸÕn¾ ûø\€½Æj#kºí„ð4‰jlíB´"ȉ ,AJšç86×°S3£bZ;-F$Ÿ“ ’#ÓH¨àHGÀ*˜S~*ÑôÄû,¿'É {D¡üS]¹Æhm÷ëÇwÒ±|‰­ÁR« ’„; rHÛàQºœ’¤ÅÞïÒ˜~ÅÉ Ï6‘©dö ­ fÛ„ˆ,A¤Ô¶lù.rü¥ uá‹Í(%ž 9©2²^ 5ñiW¸L0qªˆÍh² 2aD&*! º®+V´­’Jo·Qd‚£9i²Ü•žHï*íç5Âk]¨n. án²PäVÁýÔÏGöߖʳ®Ê¦›f•šyyK¿¯×Û%rå;EÒ1›Ñ8˜y4š*„¾€§u›U³ ptÅþæÈݨ‚ S¬g*÷ÌoÍ]›Å3 !GÖz•ºùÚâ §àôE!wÝ!¨[1}Ì=éàóLQ f¾(P~i*Ê õ5"…³úT„DáV ) µð+*¡¬Wf·Œ“eÆ…0è{¾Fh{Ïø¢»S3꜖€â ;Ðy>L>°Sú7RYv5ƒ*#åI ûÆ…=PSÚ‘Êi*pzå±5Ú=ÿºS8<Ž"UUùw:“Ÿ>¹<ò£TÝC©§\~!é«ÿ=J¦žq'Æ|Ñ‹¹ gÿYñ›¡ß®üj(¸ã†-èêjšrN#¦ªÞ¦¼éÔhP|e­}U©?Onw×GYzÝÅx#?$ƒÄö÷Á2 1§Iž.ɲbß®°9L°'F ¡?ŠNWé7ãÄ|›TñË\,i’wðÏòɲ•Ÿ—7-Ìu$“ÔG²Ï²{W¾nÙ¼é_“f½ µì†­¬ÈM+e‚VuýcrÑ—I:¨‡ë.öGÂü­I¯ò|X:!N•3ʃ‰ô)a*¦¼pV"ÁùÿfÐíUÓ¸EmKÊ>©k Ô3VFÊUt7G.µW/8]0¾òÂÀûLüÁbĘ T«ÀŸ¾™_îâZ+¾º&¶ÃÃCIú]ÅDLãQ«j‚ªOiQaÄx®Ô3›½û‰˜9$ášóϽ’’_³½¼*ÍükéOK~|;áW×$a"Ó¸w"”h0¿°7@"¼$ õVˆvÙÚ[êOSåAË4½ ¡Ã&ŒöŠNù‚&yNðaïšó‡$’0½¤aDlEú¸W„<ºÌ.å°{Ó¦ó aʾ'!ñÔw¼Ùܪs&uŽœtȈ+QLw¡H–Êsi‘qGC$ÙeÎ~Ø>é¶+ÒtT•ìó®‡y$Â…Ú² ˆÑ¦‹2,fmøJG²|4"«J²·À3oO?(¹&pÜ,¯¯÷Œ›ÍJÙ_¤ZÎy¶!ùá}c/bg¶"Spr/y‚óøt<ö¨OÀœÒyH€ð¥ãxç7S6ÞOá „—G£H(Ͻ`•‹Òœ ‚ÈIçË1y‡¥¼[âåÔÇ»w¿V'‰:Ï\õ~ø¤~%KÈÖÖ2b¨Î¡^=;ºòóBÊÈ÷]ú½Äa8Å…ç,+¾|gêBÞŸj®ù:Tùûêßø„ñ„Þ^•7Ù¶ò.!Ø+«®ž›Ó¸¦¥­øî„JÛV‡{_Ò ˆŽ«€qâ÷îƒ 3Ó s¿æ'\y‹ìºÝ㲆2GÉv˜Õ°Ål~³ð¡[ÆúP¸gëEßûnob_Û‘}g·ÚÜ{:îßÓÖøu*{~‹å@Õ©šT¦4ò²ƒ¨þ«<Ä=_.Õ{)>=a\De뫲(m DZ¤ù]užH` ]Vªn‘ 2’¡Xþ«¼ì¿ŒÃÛÍZ¢Êé< jÅGÓS…ë–ûk©BJã-ò¾§…•õéÐ×®„«x²»¼šTy=˾9N‡· î¾Œ%àí$êïb•À¨ÏÊ[c¦tkç—Æ_h3GH—Øéðx¤["ô½\à˜g¶|gM(£®7¼îöG0M°x Á£[9rvƒ…=Ôº"Í›«_ endstream endobj 273 0 obj << /Type /Page /Contents 274 0 R /Resources 272 0 R /MediaBox [0 0 612 792] /Parent 255 0 R >> endobj 275 0 obj << /D [273 0 R /XYZ 72 744.907 null] >> endobj 272 0 obj << /Font << /F36 131 0 R /F48 174 0 R /F64 204 0 R /F63 203 0 R >> /ProcSet [ /PDF /Text ] >> endobj 278 0 obj << /Length 1995 /Filter /FlateDecode >> stream xÚí[moÛ6þž_¡¹â5CR¢^ŠdCÛ8YפébuÅÐ †l3¶0Yr%9‰‡ýø%ʶ7^Òx]Z ¢éÓÝñžã½P2VÆ VNv^Ù;ûǪ®XÈÒ©®Ø—ŠÊ4D K14i–ªØ#åSûýbŒ{·çÑ^‡fû(Î§ÜØ üçéÜ÷¸ñôAxïOû—®½óy‡€0¬Å ÀWC6”átçÓŸXÁü/ Fªe*× ÕTÑt®žÒÛùuKE‰"þ‡c%\œ¬qÕM¤U1C&Ñïäl€pƒ-¹çx˜„*š©!͸“V¨Jшäq[³;uµ&d]Ž˜× Y†¹ärår#ëÌQXÒ4ø£«‚¤0s!±…¡ ØêšÀÖR#-6N­ÚÝÝݧ2œ°C0²˜äqæ¸þ^GǸ= ü8 ¼ôÃåÜ ÷¨Íßž¸QÊ’»ñ„‡éØs€ÿ„Ëo`>Œçn:$]8÷‹4Ã`:uü‘äãúrÚ™ÍåWÜû3 ¼Æ®_ßQ÷Õ‡`F})I6nnÙMH8‘;|ø—îX(žÉ«‚KGS.BÃo†|Wpµ‚*„+ffvžÅ- _<—!¢Aך…®—Eõ)¢ÆÒ[å#˧ö©Ä:Y` ý NÑ|6 ˜ËxøY´Ë(£EóiÁym•Ž¥#S5òFŠÜéÜsb·µâ—œìeÜ^ÎfÂ'%Ó{i»[e‰h¹4öœ¯Âd·èŒË­[ˆ“!w†åLu{×BB+ìÖD#&îbúšŽÙLêêë•@'‹(pµˆŒÏ:Íý{¶ÉŠwjX´Ô³ÔwOÝAè„‹}ˆRQàÉDmó(®/`;–kæ–û沸y³ëÐñ¼l‹_†ÁtYp ³ÈØw-íÅN(ƒÃTÔfÛlª‡¯Чóì>Ï^1}€~O€Y[ŸN÷¾yð„Ô’Lk>Õ|ðÖí驿ÓSͧ§šÞ˜[ê.^­üß~ªyįÜì°8È=Ýäµ´è =ÜTUÄ´bßOÓx¥¶©*ØOê’ ìðS}óõwÇÿ8n"Ú]ZÿñÖ¦ªu*ªÂ%ô©ÒyÜJ§ò;íå_c¾ý{d1S«ôF»¼æ~8Í4Dõ{ÿnzó«îðÉÄbQ)€Òv÷Æ™Î<ùÖûq(Þzü˜‹ŸÇŠ™×x-]¥Ø¢mJrÖËý ‰¶‰ endstream endobj 277 0 obj << /Type /Page /Contents 278 0 R /Resources 276 0 R /MediaBox [0 0 612 792] /Parent 281 0 R >> endobj 279 0 obj << /D [277 0 R /XYZ 72 744.907 null] >> endobj 280 0 obj << /D [277 0 R /XYZ 72 470.14 null] >> endobj 276 0 obj << /Font << /F36 131 0 R /F63 203 0 R /F48 174 0 R /F64 204 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 284 0 obj << /Length 1286 /Filter /FlateDecode >> stream xÚí\moâ8þί°ú‰JWc;qlK{+µ»´ûÒm{…û´Z¡@\ˆ66 ÝVÚ´PÔƒ$h­J Xf<ñ3žg<™ ."è¬rÒ¬ÔN-)¬æ æ  [b[Y¨é¡ïÕ«û~赩ŽâÃ#&dõcØõõ q?ü•µ]ë@»±Î¾PL4¿TêÍʯ …q¢™\ "P§_ùþƒ Ú¿ ‚-%Ñïq¯>² Ï5*ÿTÈDGŠÒ¿¨‹²×g¤:;ÔB‚r,©ó¬dƒ >“>'CRÐŽAõœ‚ÛŒʉˆ§Š=«ªÂ„ÒGäͶ°°r&8Òèf©äédL¨ïJ°ÍÓ. -×PI*å! &œgȾFáPGÉý¬‡O%Ùðϱ²æZ`€Ú©-Iç*Åm,vàöõßk‹„è[°ªø`á·ÿÿ¾ / ­¬™:ïçÄ-jŽ:ž}üÚººü|Ѭ_·¾]6?_^´¾7¾9„Tÿd%}>Á·åOþm6¡ÃÕu½Ñx¶Çuý¼~ܨg}ÆÚ®ÿÒœ+Ì ¾Ì»ÚÜ÷kO':rdº¬:¢+®&";=?ðÖ—¶ÁJ?‚…3_NÆÿí{]5VXhäôNàÆq>k=K~žúþÐ ÃXG'£$ 9$­Á÷ò)q4ï’Âk†1‚¹°–/šÕŒ†q†%g FSªQÅ} QÒkEú×HÇŧ¹rÖö50Ýy:u0µT9ëœJ†µw²[?öÛ. –À©­‹U3é¼`1Š9- ,ÜMùŽƒU&‡ ÃáesxìwnP ‡–`,){µbä¦Íž;ðå=´Æ t@JW{esh-‰Â.ÅtµZúäò©™¸q$Wš´ÜÊ‘E_æv~úƒîBLµ«;¢»!Xø¶vD§nç§>Kµê“; 8“ 0T@Tì3T6‡Éá;¾®†®ç§)'¤\­Ñ?8-þ¶  c?M4ÆJ‚Tµ2Vb6É4w8÷ñM„þµ _æ%r´›qA¹A Ù‘4–*?¥ÊIc}: ï^> endobj 285 0 obj << /D [283 0 R /XYZ 72 744.907 null] >> endobj 282 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 288 0 obj << /Length 1385 /Filter /FlateDecode >> stream xÚí\moê6þίˆî'* ã—ı¥»Ie¥½ïí€I“ª+”—f %¡·Hûñ;Ðʺâ¤m¸µ*5`̱ñsìóø9–±5´°uTkõjÍCÆ-‰$§Üê[̱q¥åÚÙ’Y=ß:­ŸÌF±¶Gq}šì5¨+êñ`:RQê¥Aý’•uT¨¼DeoÂ{ß{Ÿjí^íªF 1lË¥`×F.v­Á¨vú[>”²0bRX?µF–ÍTlŒ¤#sC ;Û›9­k´ »Ððj~þP¥kƨ3ÿ– ÿ8ËŒ­”€±æ¡-î`€™„o/ŒB/I~ÝÚ&|óÝQzÙQkš¦qôNÃDƒc\|½Ö}u Tï&}·õè®»dˆ»örD4ÐjPêNl ¯ñ$«I:+Š˜Ã+Ų‘7RzCþ“^ô'êjª’† »È‘îæa[ÿˆÝöŸ²lÝÔi’#rçºï›ËÖŒŠ$±+Ùug¡* – ±5V½ÉTé‚E rHY`9ŠSq°^Ô?Ú†KÀÏu݇‹–„ç Rm´Ô5°»âPá‘lû™utð¹rüñ[¯Ýé=î}<þÖÿºßý¼·ˆ‰ÿd u>À»Í[özPá¤Óîv­ÑiiïwÛYMgq‰LÊq.æôÖ­¸»xþßÓ$ï »Œ$P¾ýì&”8{PŒóWy9ÁËÏ5Aðsïây1L©M–t ÓfÆ` pq±ôopDÃdã¼êËÙÍØ‹ü×Zν0Ñ= ù¦˜ÂØó}pß×¢uTŸgûÒr ²œÊC'Á\*ÎéæË등råݲ¯¤ï!þ“ª™/ÂZ7éÚJMcÓ]<ØHl^dRhh?ëœàeM‰uÂôÒâ%¯¼vù!.[ñÍ‹k—g‹¦ÏâRxQl#›±Bê%ƒý:¡oI Û]me'øí«h+w”égWè[WBoOÓ~’ÎJ˜àSDŸ>Ãï¹Mo‰cëø¯~ÐÞ?Ðß(­dG ª) Kbò‘ÿÓ_)y¡Âq+-Ά 'ˆI§H,_&3MZˤµLZ«d¸y­ªÏ­‰¨àZ%}_{Ó°„D?…ž1¾³°-wHkC™M:r—ó™ µ‰N:ñ¢$ôR‚°^û3•”@wx~XqK¢>L/sÎ¥ë/«”ó9¸yÕÜeš(Ø]ŃKÃw#†'ã8Ì‚â]É‘`Û†+ ±çÛ¡U¬’`ya¹»Îä8 —Ê×^‰/¼ÈÕD¯ñ8êç[Ïþôcû¹Æ`ÌÙ­øÔÔZ…ƒ#¥åãJΚsÝUÕQ’Ô›¤å)Ù*_HH‘WþH÷ë)wL¬’Ya„CÂŒòÌBŠÉ=!Å)»/¤xãq8ÓVR²K[^\úf”Ä’b””WR–›Ï¥”ìX]!)Eb˜±Äm~3G›+wÜ_Tðª*à]Mƒ´¼ƒPvñƒPF¿ÛAý΄2ÔßèwF¿3úÝ3éwO¹ÒkåZ3¸ Bãö°‡—˜QD7±~‡Ù­äÏ•ÛÖQþäËÖÞ&lèÉo #ˆ"@†ÒzûÆÃü®·ÃÉüj¸8JUäg%¿Ç>|Æ(–´NÙÊèÂÈý B endstream endobj 287 0 obj << /Type /Page /Contents 288 0 R /Resources 286 0 R /MediaBox [0 0 612 792] /Parent 281 0 R >> endobj 289 0 obj << /D [287 0 R /XYZ 72 744.907 null] >> endobj 286 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 292 0 obj << /Length 1766 /Filter /FlateDecode >> stream xÚíZmÓ8þÞ_‘ƒ¤'êÚÎû‰C:8‰;8né7!oâ¶VÓ¤$Î.ý÷7ŽÝ´I—ÝmÚ•+šdj?3ž7§ÁÖÌÂÖëÁ‹É`|æøV„"ŸúÖdjÔ Ü¹‘cMë£ý~½Ì“‹!ÅvUG4í?ó¸ZòL2)ò쩦󔳒ë‚ððÓäÍàÕdðe@€¶ˆÆuQ€+^>~ÂVô7FNZWõ¨¥åú!\SëÃàß62Ký3Kßœ¿ÞAõCäÇ ˆ‡Bâ߈óÀkÐ[!¡–G|ä„ÑM0:Š¥¡ÁØ—ìFY#„ Ùå£ènà (ä‚[Ók¡7êÀS V‹‘ë©!ʹ1«ïZ!˜.`Vâ…`…‘«mûlUä+^Èum®-ê)$>|G3hQ€ÁøÌ wÐ=‘²\ ›±%ÿý`H˜ø(e<}ÔcêÈÇØ–ËÊ”Iv‘öä¿æe—ûíÊîjÚ÷£ÕÊxÞÂëê ™äŔż{—Ss«uàA]CÂ°Ï }¯S 0ÈÍÝNA£F/„* PˆJ˜é­š.ñ|Û]!”ãQÓ^qCŠ\ðßZWQDÛj?˜ T]T]“¿tûå¬PÝš”Ÿ%šòê+[®RÞ±î]á; "ÈÑÔƒ'-ÿ[&2Íä½f;+ز“L $mOwÚ¬2ª­äEöæÊžm>$žm¯¿(Ų‚Ó}^èG9g²=óª­§W¥Ú8ÌHƒ1)†Ô³/´¡É…/ó<]9$6Ž'´ÿHå<¯fsÃCêaqU<“éZ““œæYn¤™3¥‡]LOÆ& z§«½B«aZ¯*¤ö2OÄt­E‡G-zHìXÍ­5"õ7—j!,­x©ZpÖÂF$˜ÃÒ4W£®ôyÆ h®¯vC§§]ŠBVpÙȳmþ)t^+Pļlƒ1=9ãòjHÀŠ…¦_ÍE<×·ÂLsû´J[jðwÕà5x)ý°0Éô]ÌUJÑ÷Ë<à Í(eÀ)Tؾ:KAý_óx—Kþ›ÖrL!7µßDû ÞÑ<쬮áç‰z%R3.Ï´ƒm«Œš®¯©Èª¯u”Ü’úLÖrBR§Œcú›…ñ·$O«±Á1íÍ-›Ýîæøöîæ^¬« «§.ÚPšn§³Sl@–u@¦‰ýø—qUã ‘yv9¬©«5ÄwvxÖ©QŸèµa¼± 1ÖjQö èŽlb¹Ê Ù­¤j´Àìç-ʶC“—‹sZ Ú\±ò)ÂSH±ñb¯.:\°³/ûŠ çgÉ5‡ÜÛ¥Ð×h s~#$Dxx³µ)Žæw{PtRëhƒ6¢°q“ðg3UM¨›×•ø¡sÁ1þ7YÔÇ´z «Çšzv‹²ß!8@^¤Ž‘ }³¨_UÒ¨A $9A¤.ÎDÊÿ,Íï3Xê–æçCV.òϦÀY56Ý—CuQÿÀnAo”NÈÛ_þîgGrQÇ]ÒfÍ+;|‚jß…ªüíaºMFrQV«Íœoô¢Š<7wˆ#H»ØÄæØÃðŸ4‡¥6-QîÈËF>¶ÈJ EÅHɽaÖgkÀfOreøîí{s×´~n ,È >‰ÃôIü×Eåw© ^ýõàPD·¿f’ôÍz?Å~D1leŽs:3¬êwѪ•¼¸äbå:‹ïÑ!…læn”ò¶æý¡f}V;躿‡èÓ«F5ËJߣ^Ìë&×èå¥jS~•O -e—ܾÑíºcëÿðæÕþ;‚‘è¨ÆëeFÏEÔ¿ó»Œ×¾gé‚DÄt%©«šÄ¾ãÙ/çl¥›œõ«”°YQzÍ[˜‹‹‚Á(‚íu«ËßsÐæÿM½ID endstream endobj 291 0 obj << /Type /Page /Contents 292 0 R /Resources 290 0 R /MediaBox [0 0 612 792] /Parent 281 0 R >> endobj 293 0 obj << /D [291 0 R /XYZ 72 744.907 null] >> endobj 294 0 obj << /D [291 0 R /XYZ 72 502.34 null] >> endobj 295 0 obj << /D [291 0 R /XYZ 72 471.798 null] >> endobj 290 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F39 133 0 R /F46 173 0 R /F63 203 0 R >> /ProcSet [ /PDF /Text ] >> endobj 298 0 obj << /Length 1521 /Filter /FlateDecode >> stream xÚåZmoÛ6þî_!´(â Kê] lË’bÝt©¿e…ÁÈ´­N/ž^Úæßïø"Y’UÇ–ä!ÙÀ¦iñx¼{îî!M¬­4¬½›ü4›¼¾1lÍCž­ÛÚl©–‰ˆãiŽé"Ó3´ÙB»›~xˆ’Åý¹Ž§Ev~©;îôçÄ/"ç4’øBöݲьÉáóO³÷“ëÙäï ɰF4G¹&r°£ùÑäîÖÐÿ^ÃÈð\í«x*ÒLÛ…÷Pû8ùc‚•¢DãÿéJ“Ûw5©¶‹lbh±Kì½’˜Ü±*é .ítxÞÛ'k¶i ¸JÄ®b{Uõ&¤6 ï6yŽ[ N™¶ì”\#Ö€,Ì_lþH«çVyÖ64«i `N(ÓèeÀîfÍî-Õ‚h“¤y[¨X/QBë=|…wÂ@a‡^ߘî÷çƒòid =Õ‹˜¤Ñ³ämC|×ÌÉš¢–œéŠåÜÃ,ý[x>iÄæsh%õqG·0 kÕ ò hÆvûx˜þq³ _$JÙq¦IÚ_ú¿°Ö¡÷Cše}!~•ÄË`U¤Âb×ß|¶¦Ûƒxbad<­‹Ù9¸ _<ß=‡DÞÔ¡)ÆÙFÍAeÏ.4/‰î!õ¨a™³³3‚jRñq ˆ ¿¾zÙÅ^D“×U›]O…<-â=ƒX¼þ–§T6ß…É= eû¦ˆ}nïì)žñV=[3NËx“¦ªÁ¶–X³pÃÀ–•%ÄÇ|MsÙZ$ñ™jÞ³0B Ú¨ÕøOˈCãeÁ–m‰<ƒAœƒOI¬÷Üî¤+-*[¥I’Ïs–åÙÊ@­‚Q¥ž@AB1±l%ò=cê«`)ß¿¶¡“q”  )|§ÊQ)Ë‹to0d[•IflÓZªRd’¨ÔÊáêmWìõžù¢Ç@iýã^ Ný9Ø»<ýÔ¨/Bš—•ôê?Ÿ©‘°­µöD Hòœ¾´+¹ÿÌü|\ÎU¢±M¹Ž–-%Z„e +,šsCÓ²¼v°·ePæZ«~ÊhÞîä',E6XÓÍi~c¨—(ÆŽ´¡"§QÉ’eÉ Yr ŒFý¼ aø ?}.Ê¢C¯wó¯Ëy7ÿWÈ.TÙZþº¥E©q´IYVù1bQ’*ÕE´Q‹H“h°v••2?¥@²Ð`‰ãT×z¶‰Ñèe˜Ïƒ8Èçó.>sL~ÈX¸l˸¨cnr4b’˜¥Y33„ ]´ ×,jY!˳VtmC)îSá[.“¢L7¬µÿ„è,§N“bµœ=~€´H£ú²«ÍH=Z•:UÚz"ÒbHûNlRqÏ7‡CÜBF«Tí@¾/{°têwXÊ’°a]G'XzýâàD€‡³_E>ö_ 4Õ!HÝ¡2Êzàqwwã–ûîqÕ›Þª+77]4ÈØÞ=—Žt§Â}÷©]-ç÷ß<6覑#cቮIjF®®¡Þ³["tì! ¦VøêxrÖÎ@²·»Ô="],îU×ñü¶$ö°g!×;19dùÔ`l6 ¬úk›u2ÞGØ}“rf,ý¶'¼9ûv²Ã’ã«U½)·¯a5C+ˆê”e~ÐY §Qƒ!2¤”–aµ³ÙÊ£ØHšûî˜LyÖ'S.‚>£ÎÚ&ùÔe ÿ™ÙÂ?¡-ÖÏÌëôt¶ž™-‚mñ¤yä¯ìa÷—èÿ1ü%þBÃ@ñ†£î-´KË@Ž×Þù…ôË8õöwqŽø‘Ë»’Lƒ[ï˜Ü‘Ëë\yò2HŽ_ÉY’³®äƒä@nªó!¹—Ù,Ó@Äu{ÜÛ½º¦#²_DûæZ…FõÞ¸cg™H·¾b·{D˜ Ñ%6 ÒX[×aëIåO‡ü†ßMÊ/rˆqæË{®’|gÀ¾IŸêVÃØ`¹ûÑ endstream endobj 297 0 obj << /Type /Page /Contents 298 0 R /Resources 296 0 R /MediaBox [0 0 612 792] /Parent 281 0 R >> endobj 299 0 obj << /D [297 0 R /XYZ 72 744.907 null] >> endobj 296 0 obj << /Font << /F36 131 0 R /F63 203 0 R /F64 204 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 302 0 obj << /Length 1649 /Filter /FlateDecode >> stream xÚíZíoÚ8þÎ_mšÚꊱóžI÷eÝÚë´Ýv”é>ì*dˆhÁáòRÊýõg'6$†eÀ®›®U›`âÇ¿w?v µ©µ›Î«A§wmØš<[·µÁDstÍ1]`z†6ðµÏçWóÈ]èð NHñxq?xÛy3èüÝAl¨¡×t´ñ¼óùj>k«A`x®¶ÌŸšk¦í²k¨Ýuþè@!#Òøo<ÕŠ›þM Õv ÍAp‘]‹ì°Ák^Áp“NgÏ{uP³MèÈÛ‚ÕŠêˆPiÞl:ðw m²YÈúÓdÿ˜â쥥/œj›šËœÊ.Ì©ˆÙžæ@¸žYx6&iÓÜS½wmº¥n†l‹;$þ=‹ƒ,¹#ñ‰¯"š’Çô/hÁ$Ä$)`€™‹"þåÂUZ˜p è×ʘ¬!‡`XH |[EE¿®åÄÌØÕð˜sy}2© ÄPt`s(Oà–[ú[`p–.õ&Á< qŪüÜ Eçú’p²½ º%»Ø¤ÒRèo”ä„ZAàY^!éÙÙÙE׆ðü.Åqš÷éŒ7 /ÄÝZ¡ü#ïV Ê ‹ˆC7w{>„êú¼ñ³@aöÀâ} T TZ ÛYPWMw¿Oðý¢ÆD‡Å˜NI×I®Cö£‚\î…`í@h“7ŠK'JhïÈäÉtàþ?t\—¡5RP[„Í0*£&/®¬ÌBíjÆ«æãçCkPAb‚ÇëÊÐ,êÉ0HRBWydqE/‹¼,Þë|˜UëŠs¨ŒÃŽ^«!;ù—E ¦0š¶²F@'QIÍo×D^øu*ÌÐiaíˆ×M|oL&úAàêB‘rËv]FÿÖ’ÁóÂß[>5@h=#ŸàÒ\·»Ê0W¸Õ$ŒlËaàîÅ2ŽRVÖ4 ‡äqLisÒ%Hâ¸e>_aJ£ô]^o8Ú˦ÈTæ_œ$ûÏe]Û–ub¦gô‡¡HŸâ ‚W.½Â˜Úð¤cÌ c†Îíg”ªÿ®ú¯-Q*¹ás¢bÙ%WÈ–mW³°WÁçÝãý<ßUfk%Tö¼ðôï$]Fñ$!©Œ æUÿðQž²ÎƒY V ò4{”“c¸¬dŽW#‘·"MfQúòÛ/âK\\Fùö¿‡¼r ;baÑ1I'1'AÿXJ}"s8׉ç‘OF¦½˜øLÕ½e@ýh™ôhðØ~øz¸-Û/ü `æúðRïˆeH¥eà Dfôóœ¨)†cú†#'nðAž;­jy×a¹Û}oö§˜dû¥ •M¬ö-ÂU%¨}LæUcyÝa!g’h2ýRej!Ô—l¾i«Æ"ަ1žW§5>‡(ƒS¥¼ñÝ‘'óí½âw›ÌO‚aƒ%^­Gh+qÌìø Æ–œAœf8”©œ’x‚Ç$ÙÌA-FÞ›ƒóêTûNÀépxRJµ ›îP² ä>–œÍ1]ŽFX­{gü¾* âŒsߦ[Èÿ$^øþe§_anûq÷(i•ÉŠ-ýæûnb<;d£GÒqçð{B÷õކ¬ìÀ>«‰ÚŸ˜÷¿Ç’0ÞdAqsµƒ-ý$|Ð=¼“ ÃkFÝ*ëŒõWÖ}FD+^,Â`œ¿@=:©¯hI6¢’±7Û/DúÖ{›f¯+rMuhUKˆ"Íæ#?yP­4•Jyè[­=¤GHᓾò Þ‹´^`u8ZÓ£_]49U9f±¥rÝamŸÓª‡P‚­Õ×Ê¡5ËºÝøÌÚÎót&“éEÀèöEW·mÃ:¿šáEÊ aqdŽùM×wœ¶{ŒbÌžBð|U´¼yÄœVó›Yó_caÂÏ endstream endobj 301 0 obj << /Type /Page /Contents 302 0 R /Resources 300 0 R /MediaBox [0 0 612 792] /Parent 281 0 R >> endobj 303 0 obj << /D [301 0 R /XYZ 72 744.907 null] >> endobj 300 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F63 203 0 R >> /ProcSet [ /PDF /Text ] >> endobj 306 0 obj << /Length 1513 /Filter /FlateDecode >> stream xÚí[YsÛ6~ׯ@ãÛ3ðffúÃÊ1™45}I3J„dLx¨ hÇýõH)E•HJ‘+Ú3¦´&v{|\€ æ×ƒãÁÓ‘nº–fñ 覱íÛp áê`ìƒÏÂØŸ\jè"M.‡ší\¼Š§iH"îqG¿æ´O$ ^Bò/¢Ë/ãwƒ«ñàïÂÀÀÖ_ÚÈÓpðù ¾ ¿ê®î³»B`XޏàzðÇ)E1¿lòŸ^/qµhaØØ„¶6r¶…pÛ,¹Wx8Xh§‰ûÝM,° jØQ,VÛ¨ª ÆKb$Ù°uèÚNɘ0[Ë-{Íp€#¼fÒkXÌéÂkCËrs¯MRÎã(ó‚†TþLkÁ|Z]ü–P¢ñEÆê/d¢óB".©œ|ã;°¯±FP3å ñÇÒóAJ>èI“Aϋࡽ4APSÆaèE~›Ù"THÃJ~…’JH0«ÛJ¬ëpñ›i@§_‰¯&1‹£þÉ@–tAb]³i´­ê¼ð¦_eŒ%Ô'­ô|5«9/<ÿ[¯ Ï hÊ[tœªPòAجkPXi;¿×LÙYÜÜSNxÒÊ+ÞbA"_úeR @†®@¬S‡›7$Xt…6X{Œps+,УM6ûGûäÑæe'䤋›©´@7G7Y”e÷›ÖRÜÁáéHÄß÷•JͰg2&`pmögS=Ë/÷$¿úqtÎÉ‹Ô'ç×9#ŠrK“üSœòu)³Qõz…s6ìî笑6Óå{QN½€þCŠï‹TÙcDIà'íímÚµUlMn†Æ üHr’QµýCr Z-E~[U÷ãß?Ð*BúÃÞ¥·¯„–ó»³çOÒIDøÍ¤ÝDð5g4šÿé1#+Q¨ô¼ :÷^rz<ѵÜìöÉõ|îûŒ$I—Ò^‹‹]ݶosF³‡&‹ï÷žrè‡Ed†ÑO¸Y»ù”Dœ=t’ŠW’ÓºT¼óõ&yüî}“æ§J?Uϼ(V;,Žù ' /Ê…g]´ i†`5ZÆbvãS/ˆçRÄ“qÙ µ`ñ\`Tþ%L¥ÑDmY³4RíV”ß~WV%,¤IBã(ùå‰ ¼1KÉw[í2¡¡g‡Yޱ÷Ê©ô^½NiþA¶?vÌ­V-:Ì>nžzA0ñ¦_åâIÑh–&ÄW¤²ñŽ„GmÿF%‘»ÆRîŠ`4±[UÐ'uœBšoIN®b»Lù´Òó&ÙŠ;ðÝÆ÷7ù‚(Yƒ¨­Û×”ß^¾sÚcá1ò‚Z›²z.<[†Ï5ýæÞÑóós•†Œ<,µyXifÎ싲}â®vZSn·rÉuk²äñ¦œÞ‘.v4wNCCä­ ÝÚgý|¢±QFù’Yw–ªkP³Ý‚ tÂ×3ЂŽ[rläŸ&²–÷Š†Ø² mµŽÔ˜mrˆB35é¼LjìZfÉ‹F &´,zɯób0ƒ04uh»NÕh;4<šG3:OYö:£%йR–QÛ6®¸6E)…§Á¹æÕãÕÄ›YÔOW—S×Ê9pÓ€šµõ1ðÕsê‚…!4ÂZî9 5( ^Ó.®¾yá"P§ÐGLZkLQÁç”—±/þ§kÈÕ.4»bna¹?í¾â endstream endobj 305 0 obj << /Type /Page /Contents 306 0 R /Resources 304 0 R /MediaBox [0 0 612 792] /Parent 308 0 R >> endobj 307 0 obj << /D [305 0 R /XYZ 72 744.907 null] >> endobj 304 0 obj << /Font << /F36 131 0 R /F48 174 0 R /F63 203 0 R /F64 204 0 R >> /ProcSet [ /PDF /Text ] >> endobj 311 0 obj << /Length 1831 /Filter /FlateDecode >> stream xÚí[ÿo›8ÿ=·ijªk\›ïì´“º5ë6uë–æt:í¦ˆ€“°ÈÀ,ëéþø3`R 4 †$Ýi­TŒk??¿÷üü>Ï S çÃÎéKI `¨¢* '‚& š¬Ù„¡-|쾿ûöøX„Ý(<Þ=÷­hŽ=bÇ÷NÒºv±âôxüiø¦Óv¾t (¥+ j‚5ï|ü›Ö¿ ]X&­æ‚¬êôé ×ÈxDBüL…´0¸ÈQUu "IÐt¤n¤¬ÑÁ5eE½@CG”;‘¶76‘€‚*K@D:#±ÎØFV Ê WËš M_°0©¤œ HûY¦èÄi“RÍ€)U•*•>¨R (È4ˆ€ª©bm•d"yò«m±´ÓKÓ¥[É>þ‡È±nÒâÂ_ô¢EZžøAZHd\Ã=‹»¤¨l¼Lõ9%5\SrÓȾߦÓ8O8ÊÌ$1,º?„´0ºÉ×dri<™YkÞhéx¶¿<©ò ל†µ¦´3Eœ¿>»¼º÷¯‡ƒ«¿F¾¾½?ôß ëÚÉ¿;dïíÕùÙåÉö®¤$or»À:íiâÎì¿}}vÑõƒ«A¥Œ#B|¯–•(ò®Ø}þÇpxõîzôâòêº_É.óK#ê³æf­åŠî2£¹Úa{Uþ¡¶S«Øl1‡¸8·_}߷ǼdT[uê'Μ£ãQÕ^ÄlŽé6Ækñæ{_ŒÕ‚6,ßó°EêêâHiĸ ‹ óô}´©pêÉ @Éäëšó±mnÒ‡¤UYécÉ‚¢àéq+‹}n:Þ(Vr6ŠAE(Y’Šf‚Ë‘UE„‰Vdóúi[<¶¥¨ï×µ-ý>ÛZ6?Ãæe»Š»!6 W+<ûÌ_–PT"m%9ùg5ëA{%ŠzÜkïçq•…lä°ì½§Êyž„ iùÌŠÓbasÒq¢ÃYVpYÄ¢¥±iÝ„%u—.1ÇØ¿ aÖ•SíE_'µŒû“ ÅÈr)rÅö“b»Ž¹K:óÈ5If([‚j#'‡f˜:³. ¥ 5wQQJeÌ`fÛ ?+ÛìOIG®Mi"5°æ["‡‡ÌƒkWbÍp–zI4éxÓôÍKó|5,öh*üýg)H)㣯¦áF{íts>Ô·&a+ɧz‚®ˆ ®Cö‚õÀÇys÷.öª|ªìßÛ+œÛ5ÊeFîèõ’Y t‘˜¯Yw[Iô¹ŠÇŸ„µ)ëMÍȦÜ%ÒÝ ox2v±ÈŸTùŒ;«üÈt¿auaÙ¨>1@ÄA ­Ñº@ªŒµóª=î'N–b¯Zǵf)Þ;K~Öð¢¢l ‘Z × Véa‰•+TJê&‚xOZ‹—òÇ{I#½öhhæØU!ªiÛÃ_ZÉ0Åæô[Ó(ƒeGL¢Àki³œë§±<¹ÅHÓK¹œ5$°ú†"Çÿà:ˆpÕüp¦øæÆ^4ã ],q CŽxxEþÎ#ɶÒŤImq­‡Ê“ÒIÕFïìpç¨óaŸãµ‹PÓ›V}Å_Ê¢9^mÿZjfßî •2¨¿Qn”ö¾Ï­€açÁ„VÖÜ~ØÉ§Ópìx§ÎÄò½‰ÃöPLfðiST:À{b׿®Îà±dØÜ5ÚBß ´cAú1˜k©:Ì/ũГU>’•>çÎÍ Bjɯøa³ìØmHð<æ‚.è½î¾r&ÐÏûˉJEÞˆ#ËØV¶²b[ᎶŠÿz,Ì.ðm’{ ó‘Ÿ(õ (5…,O6ao௎…V5þ/Xu± Ç­ƒSãæž9Ç?à¡UjMíZíd6Ü©&™5"€¿9arL3[¨‡+ck:Ž?ݱ¯3\#øpßü'uÛÜ´*Ü6Sd ]ç¸Ôµ~·Lh3‰òÕ²ÕÔÙ³p N‘¨n}®ò~žL9BbªvQ?¦W•”î‹™¹ ±÷O¯àQÏ$Š·÷.q`ÒVvoÓšþ7s¾pK×µ¨4ÿo  endstream endobj 310 0 obj << /Type /Page /Contents 311 0 R /Resources 309 0 R /MediaBox [0 0 612 792] /Parent 308 0 R >> endobj 312 0 obj << /D [310 0 R /XYZ 72 744.907 null] >> endobj 309 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F63 203 0 R >> /ProcSet [ /PDF /Text ] >> endobj 315 0 obj << /Length 1787 /Filter /FlateDecode >> stream xÚÝZ[o›H~÷¯`[Uqµñ0׬ö¡›&]Uªv›ZêCZYcÛl1C‡!Nö×ï`âÚGé&RLÆÃ™sýæ0ª²PTåÝàÉàìjl).r-ÝR&selH³]Å6d¸ceâ+7ÿïWÔŸ½ÖÕaš¼é¶3|K½tE"Žy@£Ó|ìš„'$ÿGCê믓÷ƒËÉàû@ƒÅTESläÈVmÅ[ n¾ªŠãï]GYg³VŠa9ð*Ÿªª)â—-”üâú]Eªå K+¶f"G³¶J¶aqÛ,¥×d8 Óa¾»M„ªXÆéš#ElÛ ÕEª¦U–Æ=F®í”‚Qæ­’ e¨H…ûaÀxp˜Ò¹––µ ÅËÂXV[È4´,ÝÍ-ËÙ}f¦BôÙ•áTï±@œ ßd“ÏÅTÅDcGi*rM)d‰#?$¹dd@äŸ Zm )7#KU‡¿×–ƒŸ‚UbNÙÕT=̓År7dçAH"¼" NËЫB¢æ,Øb…¬Ó0ÍHש¹uhäÎ#1ßf1Ò,­s‘i+eYH^f7ç±)ôJîÎw0ÀìFMBÂy KWuÆ(›úéBèMÂÒÍ„%aÕFra/vºéR,Ø„.g¨ÈÑ%ôêHn¡q-x H‡V¡ëWÉÞHš‘¥"ûÁv/:¨6³ë«¶ "w{9#Ú0¯y$‹'KºžÎRÎi”‹'P5H÷¨Þé‘n†–’&’ƒb²³ÚÌ|$Lȶ•q]ÍŸ»á/Ò”ó³Gß[rx$OBœÊd”åx9èS’äWåE² þK—Õ^´Yü·®&oÔ FxÊ¢½}qkÒ¾ÂMê⚨8Š(#®&]Æ'MQu!À¢\)·:rÝê½ztIÂxê…÷ø-ZVw÷íw––:ßÙé¹a©“““Ü“>¦€7¿ŒiÀf,•I³$Ì]´éè¨zÂ)_R–Ñ€›£{Î; ä?WÓ à Jzq¤¯GÕGWbc<™7],‘9ðìà=yeyn©ßIîsä´ ®.ÝUæ ì¨_^}²?ié¶ "êüë׊ÍkI‹ðeá¶IÅ 0*9f<ˆò¿t9…ÎS#²®š19Ø`Ž)ªxß³²—k"§±w~VöŠÒÕŒ°º…¶…•Œ¿ÈÏ/2ÎU±|ÿ‰¾µýÐK2[“Ypò$#ŸžöH Àõ\5ÈËDî•f÷…¿|"Ÿ®“úKh(Àað/i8JÕ¯ºà©†ôyŒ^U5v¾á×bT"ÍpÖaÊ@9Ø·óþ ¶ÐˆïÓÐÖôÇŠÊ“´¯þ$á-á`¼^öÅÅ^F49râ7–9¸6òƒ5ý1Çí9D”­pØ[/ mG;g‚ÍôzªãŠI~‘‡Ài‘¸#ÞˆÍÒgkPšýfÁj«Ý/Gýý¼ì¾)ÐäºüPÖÌZiL#ï±ä¶ß†çy<³[{æÉ2‰¾øô*5a]vçJª¾A#Šû`{.ÆèªÖ‡œœÐôù3)JÞW»{u¤‹4h´Ûfñuá$'[É !¥ñ³6ù¡¯ä{"QG42ÊÙ žéyspLÎhøã8ßKþCH‘ÜL’•ƒüeq^cÓI aÔœ#^k•>é‡xmÓë.G3kÇSMÈòšãt8ºyUGÚvͳ¨å“ËÏÚ©YÓ@ºµó¡ÙÍS½ ÂDšžkJC:ýëúðòC’gv¯˜8â þD„¡ÄÈõá»±®ºúPwkÚÍý/}Ðb endstream endobj 314 0 obj << /Type /Page /Contents 315 0 R /Resources 313 0 R /MediaBox [0 0 612 792] /Parent 308 0 R >> endobj 316 0 obj << /D [314 0 R /XYZ 72 744.907 null] >> endobj 313 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F63 203 0 R >> /ProcSet [ /PDF /Text ] >> endobj 319 0 obj << /Length 1676 /Filter /FlateDecode >> stream xÚíZëoÛ6ÿî¿B[?Tj†Ô[ú¡$XÑ]ãa²ÂPdÚ¦×$ÊŽÿû’-ÅyXJ°´X˜ä™¼;þx<Ý…µ¥†µóÑÛéèäÌt4ùŽáhÓ…æškyÈòMm:×.õ/Û$›_ ¬Wåxb¸žþ> «„¦,`Q–¾’´¯4¦AIå€ <þ6ý0:Žþƒ5"ùZÈÅ®&£ËoX›ýƒ†‘é{ÚFÌJ4Ëñ µ‹Ño#¬t$ÿ/–šì|=ßãêxÈ!¦æyĹ“³ Â]»áÞâáC³‰¼îÔfûò}_ñ¸©Ùºú²/‡Ó-×D¾ë5œ ª-²ÆûGfyLw‹™o"›øš‹äù–<¶9½ª–â0²@Nó,;à­]NŒõ×r„3,øŒÅÂe·pZTôèµ'g þnX›Œ|Û—ˆ-†-†8ííõµÙ£åÚÈôÚ¢Y±½O¶é×Â…ÜLâìøuÔ’„’²tMã¿°×2J‡ñ{úö÷s`F„¾&ÓÕx°”« ŒÂwYºˆ–\ñZ^Ÿs™X&Â]TéuHsÖÃÔZ'hºÈ±½úOOðeÝÍ€oãÆMÑ´ò"J»êvÍÄQl[”êÈpüùhv°è£:k±Á¨”mš1Ù)«<Ï FçrȈ–­ê™å¶d4é#¸­­6ñÁg™n¤2Jª8`Y1Ì)âEÍíMžs›,²Œ5÷ ãtøwƒìŸE,¦\ŠâÒ“.úOåJÇE°A'r©ßÇSàÐ <îPnâSTiÇ/l¾Êvöt¬)òR™û~¡ö]ÐúDéöbòx/¡x§†]3z!åctUÅöüa™ÅT§´dÃ<Çíþºèº‰º ƒ8®É¢ÈE•°(/»Qø”,(”J‚(}ÖP=}Œ³‹3f³4Hèlv´O|ÝÃ)ˆ•½ÜÒlÆO­Vs 'º-¶â"Znä!x+±!¸ ž×'è¿‘|p8¢#’˜ Ù€XÀJ‡HŒºÄ¯»„€ó³mCel–g ì_ C Úø-LÀøãO™Ñ<ÌRFÓ¹¤œ^Iwƒþ‡²ïäœp- {w¼Ÿø½R¾H¹Ë"H:7.F{½¼Sá€LÛÖekéA*ÇtLl]i.»PC Ù*`rEÍaSD v-U)b&9“ʉù˜`}ËV™š³¹–íU”Îav‰À—–þ&†)Õr¥ôb² «¢€L:ÞJ^óŒ*±"ãUÀXs½•ÊAól& · ^µ>ÏÐ|?¼©{´Ø ¥ùP(Í;!Ÿ-°`’°À9®hÉz ùR%ÞžñY9ÌRÅ‹e²•üò¬TôÏŠsT°*ˆùÀÔ“1Ñy5ø²šÀPÎt…µœš_ ›”² ‡8+þ–„Í* W‡0àGfؼNAU,û è0xìJ`0X {! OÕ”$K#°„f˲¸ 8掹ù¦ª|ΘL:Viøà‰ÿ=ØÛ 0ØÛ]#7Ìæjê&ŠÕ¼,å6"h;0lšó6ŽÒêúWØr|Êg™AØ0L\0°ïv&v‘ç ª—ìÄì—KjÆ÷–KnúÀûcád[A'øX!˜¨¨â§“ª,Nà’ŸÐt-Õ\z£| àåË—ƒyp“¬ƒ±€Ý–5ùÞ"J£rÚéþýǶ·&¼?N0šðœ¿ËTlÈ€W=Í[”]°˜•Ç×U%eyPþ×ZäQøwLû+á5ó¤¯ l‰ú¼)ä_Ì=€@73!ž7›G—Ø;Ÿ¯¹=ÿ„þŒG¼s^Eÿû‚[-ps=øº›œõE<´ƒë ïí@ˆb×÷`s½W"ƒý¨o[kÍé]F‹Ò’A<>´”†ŸåóÚOÞÏŸ¾< ÊùÃ\½ïÒòÛ6}üO=¯Êw‰-Šþ8´Ø ʇWÕ~gƒÄO l! ÒÚG;†\¼> endobj 320 0 obj << /D [318 0 R /XYZ 72 744.907 null] >> endobj 321 0 obj << /D [318 0 R /XYZ 72 502.281 null] >> endobj 322 0 obj << /D [318 0 R /XYZ 72 471.739 null] >> endobj 317 0 obj << /Font << /F36 131 0 R /F48 174 0 R /F64 204 0 R /F63 203 0 R /F39 133 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 325 0 obj << /Length 1545 /Filter /FlateDecode >> stream xÚåZmoÛ6þî_!´(â +ê] lË’`Ýt‰¿eÁH´­N/%¥É¿ß$K´êØ’$]Ø4-wÏÝ=¤©kKM×®&¿Ì&/MGóïŽ6[h¦mèúškyÀòMmj·Ó/OIÞú´ÌO ×›þše‚ÓQ–žˆ¾kc”cñýønöyr1›ü;t2]ƒškP¹puW ’Éí®…´ÿ³¦Ó÷´oü©D³¾ÇÚÍ䯉.…û'KM4®¯R8ÐÔ\h:;%»tr×®¥·dxjgÐçý]"tͱL`@OŠØVl§ª>Ð!lLú-×¾ëÕ‚ Ö’+cèÀ k¶Î^öˆÒs-=똚G=ëXÌ³Ìøº<ßN}|êèúôl¹ŒÒ%wÖ!ÐO¡|Û—âNGû{¯(£³Y-‹¾P7seZ=TºT«±TEµ(Yg¤P…òõôÅ…Rh³‡­ð–(î0ÐÇKËûþ|t„xX\OùÂ'iõl&ùÔß5ó^²¦@‘3]â‚y“¿u[ŸÏS”àùœ¶¡”ú¼£ѵø§c»}Ëï=@(0ýózGÏM¢ã‚Œ¼&ˆŽñ FyÞâçYºˆ–%á»x ðš›nâ¡­Çd™”ÏÎÀ%õüBèóÝsAð¬ M>Î1ªz¶¡y @Z„e$BŽŽ$êIùÇEoÍÕ‹.Ìñ›lø¸®Zo{êà(d1hCÿ ÄâÅcAh^ÅÙ=ŠEû²LfïüUGeïUÏV˜1!ÖDD6ðÆ+¯±Ø¢¶ÿX¬P!Za–Éæ=Ž3Zx;’•¢ºÿÇJm!^¨Y£qN…øRb³çz+]Éh‘ÙŠdY1/p^td}PR4¯SÏ ¥ ±„ŸX´2ñžcùU´ïßTè2M£ H"…-cô%S ÁEIvVÓŽ]›”ÒŒMZëOUÊ\•AÙŸB}ꊂޓ#ßõ(¬ßcÜ»Á©ß¦EŽn^ꧨ/cTT•ôü‡ÏTƒHØÆZ;¢Î£Hòݾ´+»ÿŠƒb\ÎU¡QÍ{‡†bKnšÂvÆ\#R•×ö¶ˆª\‹R90 j';Ô(óÁš†¨@9õ½D5Ø҆šœ&È3ØgÉ‘4 ŠÅñ“øôµ¬Šê¿^©äú©XUó®£àŸŸÈ²µŠ‚•¢E¥q²&8¯ý˜à$#Rµ°LÖr$K{£¶RDI,qœêÚtóHŒÆ¨òÀ|¥Q1Ÿwñ™CòCŽã…*㤉¹±ÉÑP×ÌÈSÞÎ q†BzÁ"—ã"W¢kJiŸ ¯h¸ÈÊ*Ý`eÿI£³ššdår58{üDÓ"JšË®7#Íh•êÔikpB‹\Ðä°ëôÁ¡Rjîy¶?Äm`*¥j ò}Ù+‡Í S¿ý‚T”„5î::Ñ…×OöŽÐAx8û•äcoÿ) ©AšQÖÛ»¯BØw«Îz«.!ÜR (Ç;÷\0Ü÷ݧvœß?òÜ ËVŽ”Œ…%:%’ä x† „fÏv‰0txÔ`r…'gj¦ ${³K}×#Òùâ>tÏoJbû6ðü&‚,¿5x‘£’/LÛ¼“ñ>ÃîÛ”3ÇäoNx üX¼ÔîáÕª£­èj†VÙ)Êü ³F£CdH)­ÂJåaŽb#aîÛC2åQŸLF}F©&¹kÙ¢:nxc¶^Ы7f‹y9[DoÌш¶xÕ<òwü´ýKôÿ˜Fþ–> 8’E¼¥Ç¨{ íÔ6ë«;¿=ŒSoÿäçˆ7LÞ¹`Ìz‡Äà–\V窓—Ar‚ZÎjœU-'$‡æ¦!ŒYÉt{þLU ô§† Wq(äƒÖÅEÔ+ÛçVXëfœm™z^ hÛ÷à w‹P¯ÁÕq&ß[öl ÎÞ÷õ¶/RÕ€âÈ0覉EÙuÁKÂn20NÏzγ~gÒ¡15aËÜÔrÿF‡ëq endstream endobj 324 0 obj << /Type /Page /Contents 325 0 R /Resources 323 0 R /MediaBox [0 0 612 792] /Parent 308 0 R >> endobj 326 0 obj << /D [324 0 R /XYZ 72 744.907 null] >> endobj 323 0 obj << /Font << /F36 131 0 R /F63 203 0 R /F64 204 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 329 0 obj << /Length 1610 /Filter /FlateDecode >> stream xÚíZ[oÛ6~÷¯Ú‡8XLSw©À¶4 Z´[—¸ÛCW´DÛBdÊÓ¥Ž÷ëGФmÑ®&[J–sK¦¤çöR„ÚLƒÚMïçQoxm:š|Çp´ÑTs ͵<`ù¦6 µOýëENÎ Ø/²óázý×IP,0ÉQ%ä‚·Ý⣠ó:€çŸGo{W£Þ_=ö5ãZÀ…®,zŸ>C-¤ío5LßÓVå] Ír€º¾Ó k¶\ø®·N±6=ˆ,¤ÏË¢_Tqz‹Òr+œêXšGJÔ©¾ lÝ×\¨Çñ¹cC<-½´ƒl0‡ùy·…"¯-oÖÐMïàxã,Z1Ê“´K¯ü mM„§÷g8žÀÐ_ Ö8¤ÒRÊI[9¡6Ð!ðm¡ùÙÙÙùÀ°—£4Ïøy>Çü$#‹¥8Û(TþdíJ¥Céd™0h.°Uê(¾J+-¥Öe?*j—Ÿ µ6{ÆÔ9J¥…ÛΆ†jºÏGHÑÿA‰&K™á¯7’Ü€ô£‚\…`@`Ó8øÚ(.*¡}€ ”dpëÿSû5àù¤ˆÔõª;£ÒköêÔ~4 AÓ«j§ëæý—]k6PARŒ‚Mfh‰@õde9&£Ë,²˜¢œ—ïéøTdw8ý‚Óë²›uëŒsªŒã%J3̺gq'ÿ;Š¿e‘\³I.¬øË4€ánüuv4ÜÆ™qû'„·ÜÙ>é¼"Y¨©a^¤µd2]àØPx`û@w% <ˆ¼Ì› ¯˜¥Ã1NӖܹD„$ù»ÒqW íôÁo)‰²ìøqcàXÀ¦¦ÐÄéëÞãT%iA¾™räc†CQŒ$üȤWª“65IY8@qüǓۂ°ðßeÚ%v2liø²(°WÈ–}WðϯÆàËAwŸ—‡2b­„ªÓ^rOÿ‚óU’Þ‹¸ÀÎedP¯†í{yŽ:摨Îå1ŽHñ ‡x-FŠZO?ޚ̓"åÕ{qñ䜩²³ f™KØ ‹HNbN£8Æ-LìU”ŠúTr¸Ô‰ñ(Ä“‘aŠC* ‹êá*"a²Ê†$zxÖn[Ùp?(˜¥>,Õ»¢ä¯´l'M‚·%'j’¡O«jÓ•3ø¨äN«\>pi±«{ûýèBH1É6öw‚T6ÑÜ·Œ×• ^$DåÍK9’$Ó©x.W†LByËöJ[5–i2KÑ¢:¬±1Déœ(éÍÌO °•&þ‹¹Mò£LØ`…Ö›ÚJœR;~‘ cOÎ/Qš(–TÎq:EζcP‹ž÷æ-¯:_ãG$ÊÇão¦¤zCÅPý³ [^IÒP#Þüƒa©ŒºÇÔ`žäõv¼>µô^èÊêÍ13wVÌQ»¾.ó×±*Òw¹â3àáþÿ”Dðçvoò5F7I²V ÈÖt"»Ø±Z£…†§¬N q ß@8ºoV>ä8u싚¨ýŽg1ï‘,oŠˆŸ\¨ý¾“êÖë º½“ÓÜkZFáÚ•XèÛwv ÜÕC+š•]¶+=@×}ªò˜VŘ½šTßéÜ||£Lº6—¦ôñ9­h¹Œ£ |±Ùy5WŸ³bBäôåÔ÷EÍ^“”Ю:[‡‹ NŸL½V˜Jš}i`üÌâ1«p¹öŽRJ£‹#Ð8ïàåQã®+”“*ür‡e¢(–JV˜ÑÅv®:AÁ½ø‰ä$y…3YûÌikŒ³'N]$÷g3yì¸+ßKvkÀ«ßGãËw¿Þ]u£ÿ‘*w6÷ â$Ã『{÷8lS…Âo þü)ôy.×”ÒdõŒëÏÙf‚Ž;ÙfÒ’ ˜ ]2¡±6ƒÆyµâØ™5ÙÕUÙÙfÓÙ—îy'l ÛßÇf½BÝÆ¶Q[+îl Nãýv÷ZT"Ýà±fçÃqL»9GËœÍ|»¥‘aØ)ø.š¤ˆÞ¥Ãþš·\= V£W‡JjÍæ,ç8 endstream endobj 328 0 obj << /Type /Page /Contents 329 0 R /Resources 327 0 R /MediaBox [0 0 612 792] /Parent 308 0 R >> endobj 330 0 obj << /D [328 0 R /XYZ 72 744.907 null] >> endobj 327 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F63 203 0 R >> /ProcSet [ /PDF /Text ] >> endobj 333 0 obj << /Length 1524 /Filter /FlateDecode >> stream xÚíZßsÚ8~ç¯ð%™àJ¶1vfòßmïš\‰ï.×a  ‰±¨l‡äæþø“-lC©1&$¹´3±øÓ®vµûi% % ]ÕN­Ú‡KU—LÙÔ]²’ÚÒdØ6¥¶fÈš©J–#Ý5~§w €Fè4•¶Ñ8'ýpŒ¼À0ñy_¹ÈöÿepðÍú\»°jßk $(µ†«ÉmЖúãÚÝ7 9¬ÿ³dÕ4¤iü«±¤é{ºÒmík A¡ý§C‰7:W)TÝu¨JmØ’ ¨¯Dn³ÁÛ­zÀL:…ýÞ\$]SebQ°•¢š2€05LÔ­µUÙl3`ФÁRd¶šfH³š®EVƒLw 2«]6L[­GcYcÃÌþ0Ä\ƒ–îš:cþ‚GtN×iÈ9ˆÆ)y¼Åÿ ú7h ¡>Þt>ýusmüÆÀ`ô-û¦ l¶Ì2:/Žpâ8Èyñ" ‰í!÷P€1'r+zU…,Ó37Ì&J+Wct•#dzæõ B±'“‰ûTæÅz^ÞÈ<›ký¶¬ìÆÈ‘;Ù¡òÿ3²º#Ÿ¹„%â—¿’ãIÝÈ@·(˜eæ2É@‰¾±l-=¥oÒÃð>\2½çI”ɦ@Ùd8–m¿YÝ¿ýe¶X)[~¦öâ™e^ÍØ›„o2µ‘¿ù/CÝdò}ääÀA¸1J-&'±ºüŠgWG÷+rŸ9s§»ÅÖ/4ŒW§op+2\ ‘¿À=›mñÌ\5oi²¢¾i¾xžAhL"¨p‹@Y‘Ùœ)JãâÑO\qÑý’F÷â Û›zï9c)è ©*ÀTjö¤ÍÜ—ZPø endstream endobj 332 0 obj << /Type /Page /Contents 333 0 R /Resources 331 0 R /MediaBox [0 0 612 792] /Parent 335 0 R >> endobj 334 0 obj << /D [332 0 R /XYZ 72 744.907 null] >> endobj 331 0 obj << /Font << /F36 131 0 R /F48 174 0 R /F63 203 0 R /F64 204 0 R >> /ProcSet [ /PDF /Text ] >> endobj 338 0 obj << /Length 1882 /Filter /FlateDecode >> stream xÚí[ÿo£6ÿ=»éÔT×86ßé´I[¯­vÚ¤]Ûߺª"à$\ 00M»¿~ÆípÒìt:]K-üüxïùùóy¶¡4“ t9øíf0¾PtÉ–.ëÒÍT2dÉPM ZŠtãJ·Ã¿ž¡;9–á0MŽG²a?†NºÀ±‰'yÛö±àüàñÝͧÁùÍàŸ¢ã@ årU`@CrƒÛ;(¹´ý“b™Ò’½µTݤ¿}ézðy¹ŽHÊþÅ3)¸º\“ª›@GŠd ˜HU²A7´RzE†‰¨v2}ßzM”tU22¹ˆMÅ^UÕ¡µa²fÕP€e˜¥àKÓFÉ…1 é7U¥?t%{¥ÖrÅJMêT]ÍœŠ¨} bIÔi©¹gŽŽŽG:„ÃkbÇ$ÉŸÉ牷H}›„qþgöræÓBñ…j® ¥‚ÀÒ¬\t’IÌß*”V?˜¾•ª¯tˆø¹2Ô¯hÙk Ê;VZVQ¥c]—I—=UùºÍyÇw: ¤wéÙ83¦¯u*Z®.ëAm%#`ÑÈc¶ú1w¶3Ç΃̓^0Ëÿ 0Y†ñC‹H =úÂâ»·D¥eÕ1Áþ´Öw›A‡ ?·C’¤“vŠoʘarOðùjþg1Z7NèL'^/ûðƾê&‘ï1]¹”­Âï¨KÌ‚.Žêú&Ý*>hЫ/‡¨7mpµ˜G:m·ho&ÏÇAƒ¶0wöšÞÛ;¼sfT늜²0Ô€bP’,Þ€À”ùˆë-›Y‹.–P1 {¿OZ+R÷†^ˆ}µcÖÝnï:¤7fò÷M9c•·Ü÷ÛÖÖƒêî$_:ÈB²Ê\9¢€É*ãÔÜÿrâäåyÜê+忲»[dYÓ^EHBÐÚ˜U9,³vX‰2ð¥!«ê칸añ><%œn½ n¸]\ÂqÆ÷®gûá¬-„è˜mßs›ªíº1N’ú£i2~„2bLÒ8è‹4èZéû »*(ÀH³¢ô+D€N[Om§¤‡ÿâ6„°|ýí2ÌöEàÜ Aº˜àX(•Øö}×fÃwñã/] )ªOâÓ®æÚDÊÓ0Þ>9{]Ç­ >/KPb;˜á¦ÂR)s]½ý¡ i2ÁEèæd$g¥(« ï¾áÂÞÁ +gávéiœL¼`ìM0˜z| ÅdOû’ ¤jt<­äAnkíÚ ž™@…Ø–lÐ7ÌÝPA·7D¡ÿ‡r≪ÇóRV©8)«‘üéËŠ5ÊP£µ©Ã¤_qì9!x‘iA'ô^W_µ0è—ýd­¡lÔ¡È(ªXÙ°¬tÆ[Á¿GÙ»aK”ž¼áîÈw’ú&$•³€¼L6Í»øÑsðwªºª¥$œz>îÿ°ŠQÛP¦žëNd“y/øÉKØžÃÛßÌúíèWá¦M2—÷“”0H8Û!mèM§‚DCà‹Ý½"ñóöñøÒöÏÜ\ YÛ¯‹s .å´b×6°M›µ­mÔ°ô§Áúv°¸ÅøÉÁy- ªÉøŒ¹%Ù žsÖ9?ÊÃèßS‹é:ÊAê7²dŸgöf¾Š TK»¸òXf…ŸZ#u4çõßRо‰S¡zÄ÷§¿æV³ï#ÃÃüZÈ“[Lx£b~J-I‘¬(À9 mîA·M“šÀª!hoœ€‘3z&Êâr×[®£W.4c?ºw|ÏyÀ®ðÚÆª¥çÎ0YÇ—Û¤Öúñ·õ¹Sžˆüœz™ˆÂh”FœM§!íIX0ŒÈžá.G$]›ØB׌<ô%¿fßò±œæ/ÁŸ–ú6ävLîqœP¤±—tBƒHX^Ø™A{¹ÕwòîÏФ<ƒ–0]Øi¼ÚÉNÉ<Œ&¸Ýyä\ÚÙ>f¦³Ð÷½ Hw;µ.²CùÉޢ鋞¯±þl¯·ª‹9?RŽO: âÊ*&[‚¥r_š"î@è1»£öj$…é¨êÙ]7ó:y„Ì‹°MÖ²:Ñ_9(Ã8W*’å«^Vj”½fj¬<}P³4`Z쯗kÆÍÓŠÏ¿€—œ[ó¼øD_­E˜“ÙOà½à„9!Ñéxì„.³0œùÐ\:ŽÆ»ù”&ãÆN4xöb«'¥æXˆEN:Ža–òlo/&®½}õkyóýÌe/ÃÏ=×dQßf—y4U°“EUåÝ2?Ǽê‡)ÔÙ ó;³}b‰³$|ìÃøVžêBù(Ãê.ž¤û)Ŭ}.357µ vwš cl;%)麀0ªL–mîiVîªjªiv¸ºy3•fƒ×EÔ/¦–¦å¿+Wh5ÈúÖ7ho÷ªT#$çæVÔ㑬ëŠ6<›ÛÉÐF~—:\–îþþáMb›¾…àð9o9²‘_ƒˆÔšÿ,iE endstream endobj 337 0 obj << /Type /Page /Contents 338 0 R /Resources 336 0 R /MediaBox [0 0 612 792] /Parent 335 0 R >> endobj 339 0 obj << /D [337 0 R /XYZ 72 744.907 null] >> endobj 336 0 obj << /Font << /F36 131 0 R /F63 203 0 R /F48 174 0 R /F64 204 0 R >> /ProcSet [ /PDF /Text ] >> endobj 342 0 obj << /Length 1577 /Filter /FlateDecode >> stream xÚÝZ[oÛ6~÷¯ Ú‡:ÀÌŠº+@Ú4)Vtë–xÈCV²L;ÂdÉÓeNöëw(R²Dù¦K†¢ ÉŒüñðœïÜH)h…ôiôa:z{£™ÈÁŽ©šhºDš¡cb9ÈÒm¬;š.ÐÃø·çu´˜_¨Ê8K.&ªe?F^¶¦aê¦~þÄÇni@Ý„ò+ߦŸG×ÓÑß#“)ˆ K\[Š…¼õèᛂ0þ)Xsl´ÍŸZ#Ý´á »Ñï#EJûWˆßÜ~ª š66‰†,b`›˜G‘-˜Ü2Jô†MTD,‚Mó(†‚ ]ÃŽ! š‚ÕÁ !Õiظn eÀ1E˽ȅ2¬À×°®ÃX8<"Ü Ëš:²Á²pË:6Vas;ܰ ºÌ­TAV„ÉÀ\ä·7º]U 6à Ž·ô:óÝpE5`øßŸŠÁ)qŽøð|Bƒ¥ŒL3eLÿê¹€ªQ|Ý€µ‘\j¸î¤VЄ( l¡‡7oÞpä+7æ®÷ÿ´Œb~“>R1 ݵøÄWÊʓ«”T) ÕP|7rÈÚW–µÆ„Š: _Î; $Üú‹M[@5eïÏ %2F0+žÍ ‰ÑðEsÐ8è›kÍ Ü$‘-ÁÈÀXŠ-"«#»Ußùë,pÓ(~¿ÙÔ0$K9&¶5«`=[Çö©—Z`¾^|žXÖ@ûY=-è»výß¼ùÕÝlßËÃ8J/*ñ?ÊÄMR(HÒði÷¹\{©Ù_–·^&˜} ýô%ÂØ‹….ˆºùýü¹°ò½.¢m"ÂXį>¬Ëwÿ_*™·Ê†.®:âeLÓ,“Ký>غÏBŽiœÑÁ t<˜Ѫ—ƒ.è<[Ul†¼ãWíùÐôHÙZyö ˆÛ¼Ã,¯öq’ÉŠ‹©ë•þÞQy1]ùIJãû'çq }Â.c–›‡Hue&¸‰E¦j#ó¯QH›µKK¹&’]r{LY¬6Âg&‡¦=‡ù´HË;„Lø÷]÷,Ñ‚™mÝäå8¿†w^LiX-G†Ÿëî1Ú¶¥Q#NV´qÆK•”w4Fž\ت–ÂK:×r… vuXîè[Pk6 »È燲Nî@†Y!d1²§Î²©ŽUÏt¯'Ãý¼n-¡lã×Ü)K½ZE—…Þ¡ìÞn–ïqÍÓG_D âêUŠ¢í# ¥v®™kÅ÷âL ,ãh-žˆQ˜DJä{ZÖ›@TmIêÆ©$é*ó…áâðSé–åÙE£N¢hó]›¼s¼v#ƒ8E°È;¥=¥ÿÐ¥{ç*XÄž›1Ó8 Nûy«*{çRÔš‰>!pÿ‘&‡HZôàFò3^´^—üÓytï òʾ_Íx^:?Ôúô徿<¶Zbb¢¬Î×zÙ5GX“vÒøùÔÜZéRù¼{@ú¶f M¿@¬ ˜{ÖÊûá}¼þðǧ²aA–¸÷,s7ñ½«(\ú«aöÁ$­Ò'nÒa+¨ëSvÑʾì€ÔÚÄ~˜¶²ø*V­’€:›/ÂÖµò ŒDbM²Í&ŠË”•Fñdò y=@4{޵è/mÇ ÕóÖ»ô³úNåÝOÈBÉgÚò…_÷…ü»ñóÛ+Qxò4B“ôǬá^Ê.´¯–ß•å¢hKÅq£’ûÑjßÓù—ƒg3vð2›µŽï:„°îÛI³³Z!fÏý£Cu›¢FÎ9Ë­g³[bÛŽ§× B7­6§×¥^v;„*ënT“…gÍ,‚µ4x»;ö'û Ce4±TD ÙëŠÁ•C°Šõºú[O 0X‹{¡c:çïÜÄì5hU(ëØÈõ“ ­o½(>¡÷Bg»mÅêuâoYØ2Œã–ÃÙ†ÖçÀ7Kõ¼_àž>ïoXà´#ðóÿjÆc&6ÕšO§ú^H±ë{ÏlÌnnbaß¾øïÎû9A3¸5šo XØ8ŽpÊ…k¯Ê:¸ÓÙoÊ4_å]Å„¨;û¨jéqò*ZÀÿ4UqÔ±fÔ” Šû€âð• endstream endobj 341 0 obj << /Type /Page /Contents 342 0 R /Resources 340 0 R /MediaBox [0 0 612 792] /Parent 335 0 R >> endobj 343 0 obj << /D [341 0 R /XYZ 72 744.907 null] >> endobj 344 0 obj << /D [341 0 R /XYZ 72 157.33 null] >> endobj 340 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F63 203 0 R >> /ProcSet [ /PDF /Text ] >> endobj 347 0 obj << /Length 1500 /Filter /FlateDecode >> stream xÚíZmoÛ6þî_! â1Cê]ö!MÒ"E‡u®}È ƒ–iG‰^\JJæ ûï£DÒ‘dͳ%9IW·€%]¨‡§»ãsw¡2W ò¡÷nÔ;{¯™ŠS5•ÑL±TÅÒm ;š2š*·ýÏË šNNTØOã“jÙý«ÈM&8ñ¢ð”ˆÄ'8&üxòuô±w=ê}ë!6TÇÕ-Å z·_¡2eò šc+Où¨@ÑM›]}åKï×:"%ûOç ¿~( š60‘¦XÈ627"[lrËX¡—0lÄ´SÙxgTL]*²ĺbUuD¨0M&Ö- 8–½¦D™Õ"Kc@ ²oºÎ~L-R‘ …SÙ­Íœjêҩж£s§Žî<æL¾¼b~‰½`á~ÿD&üfF£0!á”?¥±Îùí$J9  änýu§ˆ>€<*vù6p€ p §ª·‹C¡ƒ˜î.в®"—Bxî Ñ/BÛks||\Á€P¾FIÂ0˜cô‚c*ˆÌÒA2SÂPÙ…dQ’©u[tÆ® ‰·™ß#š”Þ>{¯ÛUTYˆZìO¹ª4JrÊ-JÉ·”ĉxzWPdw‹ ä<ß«aî½ð«ã„°•„“Æv°M­([]›40†#Ƕy BèšÖyIc0%ž»OWÙZ5¤%~Îg½p]Ç—ŒÃhä7Ký?1Š0Ç;?r„Mrº3²Y ³@€RÂm¢ÕÚDEÀaÙ+Ç?t÷﨎£7ª¸bT© ç¤áõ—¿»ø|ÓÔÙÿ'¶M!®ã¸iLI¼ˆÂ˜l 'ËÚ3Ûý ¸õšbã£É=qËáÊQÐy1 7æáj°X™ö¹y÷ü~!JÈ÷™Î¬ØåÏÑLʃ@ʨ0œ¨Nf­TCå /¼Î«Š›*£ã4gÁY*PÏ}#~ò K’ç˜ø©â¬\ø×ÖÎ/4ðSšõiÜäÍã:¥e„±`Ë[Ë¡$yVZ…°çïL¸VºaÏO)Ùźªþc›×Ø`Þf… rÊ騊báýF&‹ÅÆ,‹TÖÉ™o‰tÃ*çÃ.xq½{\oúžnÆu|0#@Š]²zŠ*/ËŠ¨µ‚1¡YINˆ-áþMò/=£Ë(; d×8YVZÆ4ñ|/YJ-q¦­†~¨ZL4Q¢YüÜãG»Ô[$Y€Îãµíñ»¤]ÆØ7‹±;{Díi¬YíUäš)™U3JAfâÄ¢d¸æ„ŒÂTI"ã±zÉx\C ;‘KLüYC4ëÙR!´ÏÔDY}Ãt÷°ïýIJ[L!y’ ÀP(ù@VU+6Xqo£¸ÏÝbqÎ˜â ø½çüa$çrSJI˜‡Tt|½¢]Õ©Û­Ð>¨FÂX|pua j÷†’GÿÚÉ£ö=ˆ+Þ¬¿±V²ë&²k"›“d\³Íѕ٫í—S’¤4Ü”&5 ˜†-¿l‡J} ¶éʼÐ$o±2‰tR¶ëv¡nßzËé™gÃ-íë5Ã`©†œnïHØ §Ä÷/‘ßÒèàÇLp%ÚÀ™v gú^œp…þ²ónúÇõæ§Üª¿0£¾ðÚô¦¬Ì¿.|™oòœyS²iCÖ¬˜.³š!Ëù/n”f;ñ+†ÅÔ«Ý#jWA}ï¡uÉýò 1õð<ŒâÄs»Éâê*x³pµ2ì̳4@Kkþy§·èÂvûÆê~·rž; qmz9´Bo W›Nî8Ýgñ¸ïkí}Å’Gö5ña½v°^kLyè’Ú`\¯ úfÅ)ñIBÞÚ2{C;íû‰×'˜òÀ‘uU>d›Í¥C݆®dÛ ÎN¯áVÚ Q=Á½ò†¸–Κ:PÍ­š×ƒ×™FHå! ™'Õ45£y—Ÿ–'ÝO¦ªÖ’ÿäM(f£ì/¹äúœ­.Ç7³æ?=l® endstream endobj 346 0 obj << /Type /Page /Contents 347 0 R /Resources 345 0 R /MediaBox [0 0 612 792] /Parent 335 0 R >> endobj 348 0 obj << /D [346 0 R /XYZ 72 744.907 null] >> endobj 345 0 obj << /Font << /F36 131 0 R /F63 203 0 R /F64 204 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 351 0 obj << /Length 1352 /Filter /FlateDecode >> stream xÚíZ[oÛ6~÷¯ Ї8@̺+À¶¦)V`@—ù- Å¢ ’è’tÚaØEQª(;¾HNê NK>?žë§#Š<>Ž~™ŽÎ¯l„0ô,L`»Ä~|'€Nhƒi nÆŸÿÎi|j¡ñ‚ŸN,?_ÒÙ"'…ˆDJ‹³JvM2qR}ÁÞN?>LG_FXN†¾%qè#ÌòÑÍ-±”Úa¾ª«ràxM`¨÷ÈÃ…v`fÎ’À0ˆ8.3G9fHPVбÉxžÿbÙ4 rdÀ#n 8‚‘œ>‘# ®%Aÿå›@IˆižŠ:!Žƒ#}îBŸŒpr ÿÝÂüß•Šòsé×=²×a丢l·äz-¯%•ö¦7ÙÇß»U7˜µºu}úîTÒe$".(#Õן?ÿ:ùGYØðãþý=Øëáõ÷´HÒ‡SïÜùx|µCkwOœŽ›ö„‘‡”ËÇ™;F‚ðU”ÍçY:«74Th5¨MÖ››²N=w,;99ÑESI6—¬Îs"i\ jê ¥ºä´êœ&Õ1*ôñ»!»w”uï#N´FTÃK¾"E‰{Á·ëìb±(_Rû¢’LkkÛ)AZp3òŒ¯†ZÝV«íêtO…ÈÈV꘡[2æk*«\æÕ~k0a4ïB*J)ëÎDz-ùÞÆjçìªæsÊÄÚgdA ûMu—ÕP½¯1Ö+«ÂâÚñ ˆS¶aš7ö§¿¶¦Iš ]jídQˆ¡ã6‘É¢ü>Ž66a O½wØ£]›?FÃ_ ëøDÄ/køUÖ îö¶HpÖ¾éðAï;°ç©}u&P¶uô¯çüfŸ›V-sZ^-dM\úagQbž’á{yŠ7lì™k’?ÄK³•‹„„Ýëô›GõÍo {«HÝh,™'ºùµõÊTú¢®¾·µR»cá.ÖÅí*Ëw4w9d«AX;úYzöL«Øw¡ˆrùøÍÈs!çí’P ì•òçÃS¾¯ûþ¢iÙ ÒI o³³ØØ]í:6ÄAÐcóò^j âõÝ­Ô ôÑØôí:Ðò¶Þó½¼)]B8R#lU¹„¡eþZÖøÃ·(ŸgzËù+w¨ÓB"®$ïË؉m¡ÐÛ¾áué¹ÿXX endstream endobj 350 0 obj << /Type /Page /Contents 351 0 R /Resources 349 0 R /MediaBox [0 0 612 792] /Parent 335 0 R >> endobj 352 0 obj << /D [350 0 R /XYZ 72 744.907 null] >> endobj 349 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F63 203 0 R >> /ProcSet [ /PDF /Text ] >> endobj 355 0 obj << /Length 1131 /Filter /FlateDecode >> stream xÚíY[â6~çWXš©xmçŽ4³Ý­V]©;ƒ´Ó2Ä@¤Üš8Ý¥¿¾vì@¬Új@"áÄþÎñwn‰ƒÀ ðaðv:xó`ØÀƒžMl0]‡Çt¡é`êƒçÑo›(ñç·ŠüvLwô.Y‹9åAÿ¤d,d4gê†èöëôãàýtðÇ =`…kB9` ž¿"à ùG€ á¹à[9*¦íŠcžŸHÛˆüf+ N?ÔPmÚØ¶ ‹í£ÈŽPîX[ô†‹ – òŽa a¦X‚ÂØ·ì¨­D×õH¹éÐsÜ-rÆÀ²Õ]fºÀ.³Mé2,V 8Ȇ®g*¿-‹xQz X°û-‰ÀÏc¡Ñš !ˆÊ¦ø—’ÝÄÑÀ³ÆtaÃïÈB4MÃ`QÅŠ1¾N|q Ëñbì#èYž²;c« ç,;Ãöl+Î’‚3©=¥|ÝP{ì]öO–Í5ZL#Ö kÇ‚´Sz«bä$âß<ßïB`leŒ †v•>[¶J$€-¡<[—”¸ÐpöÄìp^¡?«y´Ëá9ËOr1½bU¶Ûz5 ‰ZQÓÚ ‰áp¨Hÿ……©ˆŒº;Ësž¨ã"c”3uNc}1ˆÐ ü«º ó„ó°XÑ)¦Kcë¤ORšÑHá**&êÏt­µÉz*kénDsi†•é9§ñBÏó)×¶/³$ºÈÐ:«“Œñ"‹smÞ}\§+ ˜}žÚN­c´¯­Y¥iЯ˜iá§’ûû4øÂæ÷iÚŒ×+•£ÃÚß–\J¥ÿ¤nVV°\Ð@לêbŸB±§OÆÀ1G´­mhÑQ:Ù’-»æöJ²ŸÙc˃Xt»FÊÜŒ¯÷¹é Ì£¶)¹Qd?qšquú©,5yä\gß@¸z¯x,b•lYsû‡{…ï4„_ÜRËejú ¬&5$j’…ŒC~|«Z'9ß/°.Âü›Ì2ÿòfäüךQÍPm3ËØ8@ ¯IûƒÊÀhQý³¬ƒ+T ™YŽŽÚ†d×Qtœ\x‡D»ôê.ôHDàèüª|ªghV”¤Ê5©±ÅíÝôì–{w›'të"ÖϽîÍ÷ŸYtjôí"´_É Ðë¹Aí—}n½à›«×†p¸!ä»ûmA«·†z¾v„׎ðã;Â~õ¯ÿsõÿw–é`yÔy¶€ÚVàÙLnXÍfg»æîüœQ3‡LfF4ˆ+3Ï›;l“>¹”m îõ²4Ý¨Š UðBšoªÙIZ—ˆ1+Ç«–«n:è‚'ÙF'±×èÝZ&UÎ6 OÙo¼ °Lb×½dË}oë_b |ÎÎÿ–v}l¼£°LHì“_Qt¾>1 Ę(B ÷vLlÛ°F?¯i*7NôQXéx¹òk0Ϩ…Ñh£$ï¿Ó( YsûAÐù7±ò÷2 endstream endobj 354 0 obj << /Type /Page /Contents 355 0 R /Resources 353 0 R /MediaBox [0 0 612 792] /Parent 335 0 R >> endobj 356 0 obj << /D [354 0 R /XYZ 72 744.907 null] >> endobj 353 0 obj << /Font << /F36 131 0 R /F48 174 0 R /F64 204 0 R /F63 203 0 R >> /ProcSet [ /PDF /Text ] >> endobj 361 0 obj << /Length 1066 /Filter /FlateDecode >> stream xÚÕW[oÛ6~÷¯à£TÌ,ï‡aXœdYŠ¥IC‘…b)‰ÛJui°ßC‘’-EËâ[Ћ¢ÉsùÎw>R]#‚ŽFÄ?¿Œ(< ¢H3¤)Á’+4_.>”ÀüD°1 Ý׫VH¨žKt>z7šÌF¯‡åÅš]!a(Ž$EÊ0L‰D³]ûìͧá§Ù›z9˜ Åìz¥Ð˜ql(uËg…Œ§vùèpÖ PI5»ÈÖ‘¦Ö#ã*‘" á#<ûprz0 ) ÞŸ‡c¦£àÏãÉtoJ|p{gÇ6" RE‘Û}pºÿþäðí,4$س?³ãÓ·.[m3fP‘m‘ðéÞ¤à]eËev¿X_»×8¹üe³È†Q’l^­Òu—‹lí¦Š2‡Ý…ßßÚ|RÿßU d«ž¹"«j7ótž%i yC ‚)„M0S~¹¢(‡?LNFý¢4æÔÓ¸!ç®áïp Ä¶¦1…"gÿrQ~ÎÓ8ù¼J‹"¾N‡yF±¦¬Ùò‘1íÊ:Y”n0 nt’%—–Uáß­Y‹%Æ"Zt²éטªGÌR ÆÖþÀdµôÇUy“å?‡cÆŽâeS¡}(ÿbí«÷‹óµ`n,À·¡þ$ daˆ‹›û,¿ýíz/–xµÞŽVüZªC56œ»ÐÎÓù†A/Ûwsb#eË=Ž#Å[î=›}èb,IÓyÀ‡ƒNç=#ì kˆÕÈÁwL‡”Nê—/UZ”¯§iq—­‹ÔÍ®e熥¢ïšCyù›/ã¢pm#¶É̶žnÕÝß+茪Àý†Ãn+ôèf+ÈìÕÍ^›dUø&q‘ø£”c©ÛÆþH$QJ·†ã$q:Ù$ÌÛ,å+&›µ? ƒó'jeažUërÀ à@¹z²™W¯nïz_¡ ÚȵÜJ’ö¥ã{̵EC×Z–+ϸðrâjçÜ N\]ü´Ç¼=X.kvÁàkÈd/«ŠÖ©d£^Ê’ö¤è§%¡íäj5k’¸Œ‡X‡#å;àƒÌ8x<¡ê }þ±{ä÷r—Tê0Ü/4´{M9³Mçñ*-Ó¼èéY­›ü:kè ¦¶Ž"åbj/wñÜÝ>ØÚŽ;·3?óð¸îU à°X ³ó•®Ÿ\»…ZÕ€ì‡ëÿöiZ•…Ê`]<7pMk;²…Ö! GÓór*°4ºáÿ)•NæÿQ+ –’îÒo_Û^ïë ÅKé›Ðp6ª.“vÑ·¼*iõ­iÀ~œ·èÎzÇT½Ë7÷ƒg¶Ü(t/¡tÞÿ„•è®û_°ÜDOüÄ–öÂ1$àRBý¡ÌMßå7GØÐ* endstream endobj 360 0 obj << /Type /Page /Contents 361 0 R /Resources 359 0 R /MediaBox [0 0 612 792] /Parent 371 0 R /Annots [ 357 0 R 358 0 R ] >> endobj 357 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 442.848 293.438 453.633] /Subtype/Link/A<> >> endobj 358 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 424.915 292.332 435.7] /Subtype/Link/A<> >> endobj 362 0 obj << /D [360 0 R /XYZ 72 744.907 null] >> endobj 18 0 obj << /D [360 0 R /XYZ 72 720 null] >> endobj 22 0 obj << /D [360 0 R /XYZ 72 496.727 null] >> endobj 363 0 obj << /D [360 0 R /XYZ 72 411.974 null] >> endobj 364 0 obj << /D [360 0 R /XYZ 72 383.469 null] >> endobj 365 0 obj << /D [360 0 R /XYZ 72 358.108 null] >> endobj 366 0 obj << /D [360 0 R /XYZ 72 329.65 null] >> endobj 367 0 obj << /D [360 0 R /XYZ 72 269.874 null] >> endobj 368 0 obj << /D [360 0 R /XYZ 72 210.098 null] >> endobj 369 0 obj << /D [360 0 R /XYZ 72 180.21 null] >> endobj 370 0 obj << /D [360 0 R /XYZ 72 120.434 null] >> endobj 359 0 obj << /Font << /F36 131 0 R /F58 191 0 R /F64 204 0 R /F46 173 0 R /F48 174 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 374 0 obj << /Length 2046 /Filter /FlateDecode >> stream xÚíYMsÜ6½ëW𶜔@‚ >DözW©”ãµµ'Û•¢†Ðˆ•rL‚–•_Ÿà—¨ñHåÄ—œ†l4€F÷Cw? ¶ þsv~yöã«( 2’%< .¯É§$΢à²Þ‡oîöuqµâ4ìÚÕšË4|Yoº½ªt®Ëºz†²·j§òVá #tõñòç³_ž}:c° ®Ie°ÙŸ½ÿHƒä?”DYÜZ­}')üî‚wgÿ;£ÎƇ~­íñØvI HF $à­Ò]³b"¬Zk•ŸçÎFs£Œ°˜ÃˆtycÎ"h¨ªM]¨_ùæ÷¡Æ÷½jÛ|«fË&ñhÙµÌH’k‘ŒáÚ[¥ÏKm&ùéØAÒDx>PAQIŒ•Édêuò¢hV‘ ÁŽ¥õ(Ixö`øô°Ÿ:ÕÜÍ|81síÖ\3I²ˆ õõÌÝ«Z=röÉpt~lTû"cIRþ)Nd‰ ÉcÙž„Èéúd,~/(Ú 4ãê(šØÙþ…©g æI¤Ÿ-,“,e^á³øs¶dXDxz:dÒ”¤ÌC&ùÆy,FN…ȇ5bƒ3›K¢âX‚)É4Db.‚ º§`nޏ`G`b2Mw2ñ³õŠ+3¹øÊ¥˜¥Æ‡£9>ɱìÍàq4K›]¾˜Ì8'1@Í™w°mRרë·FåÅo®9 8ur7™0Wú¹oAýE]îÚ·˜Ë6ËIböÄûùüu]-9X¤$J¿vGûô¿©»J?´Tœ‘4ã'¯õÃ¿ßæÖ¼íbÃäé—>†ÛÎĬP\Þ”ÐpDœî|ÄyhÚ9”ã8‡¾Ö\ró¤W,¬ñÑ„Ÿ®›z˜Óªñ•SJM$èYPk¤Û Òå¶«;·} rç¶2ÕÛÈ6&Ú(*+å~ß}­`(üs¹­<äƒ#À “£zØÄeøæåÿÍC2Ja-ޏë E.«-ê HL#/²°T(í•Ý0¾`÷15ë7~ksªùÞ…[7¯Š‰IXuû+LË~]\¡…ÃF /ª¥³öwÚ“†/Зæ1oÜ 3Ú'ïñaÐ%ƒ?TS›½˜M)º®ýôͰ$Zébk–ß*wóèš r¬>ÓibW_Ô¦Ój©c$£®¾nj+cû²”JxJ¤à§³‚˜$"ž®s4÷ÍH^Üó\3 ·+HÕeÕ¿ãO‘ë¼ÕÆï>Ô#—œûˆH6]Ua%w0RÜÂrPÞ:é>G’†Êmç×Ð76ò  ´lQhnäDkŸÁWpÊš¼Ú:Ì”²uŸ ´¤_+Óúc$æ`¾uYlÐÚyl9‹Ú}|)§”ª,–ÝOÞtMÄ}æ{òÔÆæäæ×ÒƒqÚøJ"²#%~ȬûyÁ&裤ÌnòdNÝãdeUêüý‡ê±ÞêªU“ÏÏÜÿ: }§ÿn€ŒÂÓÜ–­:ޚБ~ÇÖÄðþnŒ2’°ì1Êöû÷%Ðó³ïÔKDBB×™Í{ {Í£>½ÉØe‰Èg‰xŒ!†!, í? n E¹ñƒ¸jTmm6¯FéÚ[c.Šl©Ý&å¤4|ç:k¦Û¡¬Šr“ëùÆì9¾ÿúz)?aÑÎxHZÆÂ__½ZÁ¥èÿòî%ÖÄŒ9A0ôðâì…ñ«;íLj0©j6¯îô¡Ó(WØ &ó«ýîøU&Ƭ8Þ,Ž7 u|à@z]ïvµI˜·‹ý…²)­˜åÏ›r{ã.mSxJ©ªbþiÆçesL—r¯…í¤yòVÌôInq·²òEâª5IÐgs³UK–Êè…5ËÃF Bî«°aƒ§aäS—Cïªïð O‡U­Q5Ç÷}·ÓåaçV«Ýò ¬µ•ÝVÂt³\×yY(»5ññ i€Rå;”: ˜xº-wnôÊM90àKuµÔÆs<±=žÙ’K›—îE—û^œ¿Aäý BŒ®‘]ÏÔi¾ÎS@áM†1¡Ð]P”O;óaÍd.û-7õÜ­Gû¶K§öø3Þ#G‹ ‡Jå&O-yY¶ÀŸº¨tGø.BÇ))§òÝ£…eíT&~ý:ñNþÐuJ±JN'¾Pyì¾ÎˆLÓ1ïåÞË-å,>ùq使ÉfœžôÚ¹˜¥ô*‹¤WH/‘^óŒ0:ëbאּðÏø›{3óc>UfÕ{l^,7´Ü{|ÓŒ@{ýZòÝsà(B> endobj 375 0 obj << /D [373 0 R /XYZ 72 744.907 null] >> endobj 376 0 obj << /D [373 0 R /XYZ 72 701.903 null] >> endobj 377 0 obj << /D [373 0 R /XYZ 72 624.194 null] >> endobj 378 0 obj << /D [373 0 R /XYZ 72 566.456 null] >> endobj 379 0 obj << /D [373 0 R /XYZ 72 470.815 null] >> endobj 380 0 obj << /D [373 0 R /XYZ 72 414.979 null] >> endobj 381 0 obj << /D [373 0 R /XYZ 72 307.382 null] >> endobj 382 0 obj << /D [373 0 R /XYZ 72 213.798 null] >> endobj 383 0 obj << /D [373 0 R /XYZ 72 138.257 null] >> endobj 372 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F39 133 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 388 0 obj << /Length 2093 /Filter /FlateDecode >> stream xÚÕYYoÜ8~÷¯Ð£zfHŠºv7‹uâqÁŒµ½ ên¶-D-utÄñüú-²ŠÝT[vœL‹}âUbëüHñà:àÁ룗WGÏO£$ÈYžÈ$¸ZQ¬˜Hó USy\­‚wáÛ»M³ZÌ$‡n6—iž4Ëa£ë¾è˦~†sºÒE§q ŸýzõÓÑWG0ãR û*–ò4XnŽÞýʃÌÿpåYpk©6J2h«àòèïGœ}¨µPþD”2ÇA*8ãQJ˜ m±Ñ½n;+˜Û!x79—MÝëϽYäÞ†YÊâHíFï¥Là€1¯n4vVE_t}ÓÒ°o°mõÇAw=Öm³q=zŽLæ"ey$‘Õ…î‡v&â°î>‰åL(éäÛIUÖe_Uù»îœ<ݶ©;ZÞè®+®õ3iÃæóRo5G8húÝÞ–ðñcg\Á!Ò`.#– -«¢ëP«*ó¨¥dJ$Nì­u°¡c‹²ÿ­ÕÅê7âÏðÓDùŽcø6uß^ùIÙ-[°í›z;ôÝ…;ê}¶ß¾ç1GªØ§ŠYžfŽæSQ!_œ5µžðŽ8bR8Òg{)–g;‚~øp[´³( ¯§”e,JO8ñ˜åç \<Ë2м`9¸¼ïY¸"˜Qþ`4ƒ+emïH}ßÈ}Óg4ÔÙË&Šô © Ų́…v¸¶Õ-Î9‰j+;–&j°÷žs©«›Í£(/!¥ NÎÎɾ*—EœA ‰Ñy­¹ Å ÓÆáùÙŸq‚¿Àöüôt–ñá2ª¦¾|‰+¬ônÅÔÚÈ…É ë‹»žL®(ÊšÓwñN0W¬V ÓÎê †Vá> H íÝ,‰TJE{©l¤yÛuSG^7UÕÌdÞîRŽØ·¢9—nÊëŠâv…v#ÖD†v1ä%å sLJ …£²éËöF|½ ]Ö›äVRNé†Eg²bÝïYulwB“û¨à¼YcÁS¤)x($ÃÚê3Ý»t?EÝ—ý-tH^7´\`³ª¾ÜVT›ì¡Jƒ°½9kæL“šXØ€mËú‡àÁ´§=Å^&tܺ¨p@~tä'0w[V´º ™-8Æ´ß–½QœLÂßuÛ–2µééžqER7L9uÃ$×Ì­Èh&«˜Xã’¼^¢È@ñª¬u`’âç»­^–v¦í™ø:ÇrÙl@ݽÇwÒ“ûí±Q®ãL@–ãL&‘‰ZX¸7yñŒP,R‰´iQBÊ„1‡lšS-•LŽwÿ.Pƒ<.2eB(—ªMÙºmË^»ºE•b$˜ˆa7áC‰½âeIÞú/³v9\¿˜}€_š,|ˆ F5b @¸6”2Rl9T”Š¡¿iÚ?Àf¯‹JSˆ¾‚Ô‚™ APàŒ:8Ô«£ÃÉ ¥ÂEÑÝÜ6퇿]CUly{€â¯Sñ~©—{èñ?–í›]Ðúµµ4œ.ŠX–콉?èSß¾™àDì½×€ÔO‚Bœr—/Õ>÷!ÖCø„ÛPŒLf" ÏOÎÑ~·äêj݇f~ÞNﮤõ=èpæ(`'f"àb² r— ŠJ¿jÊŠT12#ÁbþU Ó A˜‘&€Ãì1¤ Ã͇­í¥r`ë±Í M ?—ß¿FÜAã󜥥_Yµ—–ÍÊÝlh}@xåϧ BÖZH†žêþ ]Þ‚þù™.4Ü8‚/…sÒMãØ€LPœ?•K= c°À¦±slÄ/IÏžãî°ÔA¢—Ø+¶D¿¸Ã…ÀŸð•­Óæ«[Â%Ê»y¬Ã%±³3ãä'#¿ñ"2R¡Žsloð†8:ÌdAG!2OÛiŽÚ†¹­Ÿ1\8Ʊ/DF˜V-s»vÇ×®Úô”‘)a¼€ºjì{šcDaù\û,;ã5Çl[Uö¬×3³N‹PAǺœ¦Y[›ÖP¿Hœ= t¬:q’$w¹oOþ1òJ²‡ºdàÀ*-É÷«KHQ4Õà‡ ZY7íR[?H à++Ú‚tÀG·#A_´T  h`ªÝ#3'hõÚ¼€Lœœä(S›®p$°±>/Ç<ͦ¹…ªîµEòQ$I¾$è»HòvG’äIcYr?’¤ÀH’NÃ’[å`ïŸ3!„óQï‚iâÄÇC£ÐR»Ðâÿ>=ŰR#Vû&•™©Cn¡]â`Øùο’‡ÚñhУmžˆ\ðìo¦>×q àP¸—°{¡ä_))” ‡¡„’…5²²ƒJ2*Ô|ür¶Ò¶ZLÔ§@Yü5u٦ω Ÿ²TDO/t)K¢ƒRzb…ìF¥Å+`dï!q?Nc#‘¿ñ;Ü—_@÷G ŸÆLÉä‘÷ÏÝÃP¿¿Ë«ìÞ4cÔ®Hfä¥ë'›ñ‹°­JÇø±ö,ð_Tý×<äF÷ré9Á>ÚyWã÷ÙÇ”šæ,eŽtúY/‡~J©J°LEOˆ®µOææŽúy ÃÍ3å“C$V,‰ÕØ@Cýôð@B›kà’Ø>Ü?Ñ£%ÿÿ"DßÃù¶Ív¨ŠÞ9Ýø‚Êü=ð9þ ‹ËäÉ?‚îÿ©‚-Ü«…wÉž¸ÃÁý/{Ê;gw®ì£Áwz¦™‹,޳P‰CeüdW÷ endstream endobj 387 0 obj << /Type /Page /Contents 388 0 R /Resources 386 0 R /MediaBox [0 0 612 792] /Parent 371 0 R /Annots [ 384 0 R 385 0 R ] >> endobj 384 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 519.561 293.438 530.345] /Subtype/Link/A<> >> endobj 385 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 501.628 292.332 512.412] /Subtype/Link/A<> >> endobj 389 0 obj << /D [387 0 R /XYZ 72 744.907 null] >> endobj 390 0 obj << /D [387 0 R /XYZ 72 683.97 null] >> endobj 26 0 obj << /D [387 0 R /XYZ 72 575.377 null] >> endobj 391 0 obj << /D [387 0 R /XYZ 72 488.687 null] >> endobj 392 0 obj << /D [387 0 R /XYZ 72 460.182 null] >> endobj 393 0 obj << /D [387 0 R /XYZ 72 418.317 null] >> endobj 394 0 obj << /D [387 0 R /XYZ 72 283 null] >> endobj 395 0 obj << /D [387 0 R /XYZ 72 223.224 null] >> endobj 396 0 obj << /D [387 0 R /XYZ 72 163.448 null] >> endobj 397 0 obj << /D [387 0 R /XYZ 72 85.739 null] >> endobj 386 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F39 133 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 401 0 obj << /Length 1411 /Filter /FlateDecode >> stream xÚíYKsÛ6¾ëWð*c!ä¡׎;Í´¶ë¨Óƒ“I(’8‘H…Øî¯ï‚ J¤L+Rü˜é´'àbw±Ï µfµ~ü<¼9s<+ Ç=k<µ$·¤ð‰k[×öåÝ2‹'CNíªޏôíÓ,ª–*-Ã2ÉÒ#\»R  'ŒÐáÇñ»ÁÛñàë€j1ä+ˆ¤ÒŠ–ƒëÔŠaýE‰øÖMMµ´„çÃsa½ü1 FGÚÖUlëÊ(¡ŽD]£EXZ4Ðù-:Ή`0©©Võ‰ª‚L’òÓMž”êÓRE8S÷z¢µ—y 4Íæ¿4ýû$-ÔI–,®T±ÊR8ø}¡Ì‘Døn³ñu)R¹m*—R64açCGÚ ÍOçYj¸:A›Þ'ŽÏš G½=¶–ú-\Tê!^" Žãîâ5b̦$p]¤|ýúËMX+:ë³¶*J¯unVGCãÆ-%FŽ D éû¸i<סäR;Íòe¸ÀqÞØºž%>ß*šg8ʦø,&¹úZ©¢xNã_ét2×N‹]J:a‚7ÇZxr×(†1YeêéÃŽ*#FqÙQæ¥ øïÕ¢LVX‹+,}¹RˆC’rß ¾«èrnô… >ÒDÖÈ×uU˜0ñð-(þ`u‡6¼»º øk—îSÔÝýËJà×e¨]Ô!?ñ\×m‡J{Z¥‘8«“ F¾Ý¼¯ Lz¤SU¿™fy¤pI…Ѽ٨óA’ÉBœÚ«i³A÷„†¼@:d ¼(ü9Ž/Î yŽ4ggmæ²až«eV…AqîÚß’£¯®ø­D6©âÙM˜Áĵ/OÿÄÕb¥¢ä¥sÝmº·Ît¤MU®ÏÒd¸>Q8¯ P‡KûO¨ß‡¹!ÐQ ‰Ùéé®–—a •Qéo•g†è 2³¼­IÏÑÒj9B“ól«/·eÂÔ,Ó‘Zm䳦U×»| þxS»@¿ª{qc<lÌã›É¾ˆ²ZsièkHO4®¿†¸jºŒjN mJA¹}Œ´‹l–D5ŠÏìtDè·f'”"¬²"©ãºÇ`÷‰‘×)Å=4’c©ËáTåª*»lbÈ/Î Žñ±Ñ&Ÿé«>YÉC¼ÀöpD2”Tg¯ø¢c¯ÿ΋Gÿ‡qŽó8œÓµª ˆlÙôVEUÙgThÚ¾pùf¬ þ<¿íC0Ú°Ë÷NWÓqÐU• H9BÓIÒõ|”E©ÛÊ£<ùý,ªmÒØ£sB_æd;i£c'{:Ç›æÙr'´5R:ñqñ·ÊVÕ",UÜ÷)œ™X¥ÙßFj…¸êÙ€¸û îîâ_¦¸P™|(¾ó2edH:eUZî¸Tñg'OCÒòO|§j{º’v§‚W#ÛdƒÔ1çcu´¾YãÈ5à;j®eL¨~­Â´LÊ»îÝL«r4èulÁÿ ØÂ;[üà‡üb ù²Øâ™¡…w´x Û¿¶Ø¾Q‡~ͽ½/Ô{/û'Œ™ÄDs _1'óp…£Ò·9|Qxœ÷ü ø-™äPµ …Øw¸r|ùkÏ_‚íCüûx#­ endstream endobj 400 0 obj << /Type /Page /Contents 401 0 R /Resources 399 0 R /MediaBox [0 0 612 792] /Parent 371 0 R >> endobj 402 0 obj << /D [400 0 R /XYZ 72 744.907 null] >> endobj 403 0 obj << /D [400 0 R /XYZ 72 686.137 null] >> endobj 404 0 obj << /D [400 0 R /XYZ 72 626.361 null] >> endobj 405 0 obj << /D [400 0 R /XYZ 72 566.585 null] >> endobj 406 0 obj << /D [400 0 R /XYZ 72 473.111 null] >> endobj 407 0 obj << /D [400 0 R /XYZ 72 413.335 null] >> endobj 408 0 obj << /D [400 0 R /XYZ 72 353.559 null] >> endobj 409 0 obj << /D [400 0 R /XYZ 72 275.851 null] >> endobj 410 0 obj << /D [400 0 R /XYZ 72 224.22 null] >> endobj 411 0 obj << /D [400 0 R /XYZ 72 164.444 null] >> endobj 399 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 415 0 obj << /Length 1446 /Filter /FlateDecode >> stream xÚíYmoÓHþž_aé¾8Þè8Ç]HH\É7@È7­…c—µMáßßì®ÚNš6ÐCÜ)Bªí=3;óÌ33,.<Ÿý±˜œ2(¤$•Áb0Á‰UóqÅ‚E¼ _]WÙùœâ°­ç“ðÏjÙ®uÙ¤M^•üÚ™.tZkÿ@ž¿_¼œýµ˜}šP†Äärã8X®goßã ƒõ—FL%Áµ{kp™ÀµÞÌþžáÎÐͰ#*™ýJ’ÀÀ[‹g7[#1.©Ý›UcÄyì·Es6~»’“SɇhŒá ÄÉ_9¸-«õº*­¦©A„”$Iÿþ;Jcï½Å\âð:¯ù…§õ×réo_MBñÌimääT$ƒ˜F„3„1 "š ^pÚ@N[Øh ¦msY™Çóˆ>O ]úõgUQäeí~õ:%Öœ)ë°Þ9gºÎçi}y]™¿_¬Ó¼°žÙ oüfà!"1R¬3í^Z\ý¶}*l˜ RB¸0ÃîC‰dØ!òÀ ÞFãðéë;òq§ì“S>Ìs°‰¥è¡šÖµ‡(B‡RÄ­÷ü[WŽÚüw 7ßy–l2á•ûÌ#õUþ%/'p…Ð áÊ8RÀ=‘ó¡ô"—¹5U¡¿&aêGÔËÞ¶î7§Ë-7—i·xeª9áç<Ó½¤,Ë­ãÒ¿±šÔÒeS™¯þ…µ fþUeüMZ½líW–­1òdb˜ê… @2f~?=öVºH¤¾Ï»ÜlkGphotÚ=T+õºá¦¬ÌÚmî¯Ò¦Ñ¦ìß|ìÜMûåoE#r&X9°@H…(dCt”$– ‘P>hÀc+D삃D¡‹NƶuÛ+c=,&C=ök v’V;Eo%Þ&©3¶dÇÕ¾°@.•JPeÁŽd86HºÚòË<’x2û`Cf¯Múan&y¡†e¤#8n éÿ8ûF+ž¬’'6s‹Ã<|‡>@hˆ&lø£>²… ²É•gŒþÔêúa\sazVåE}æ…ÜCXÖ3òpÅM¦F?:èk|ÿ=×WU mÖClú`hmÇFÑ˶ÑÖ]fã9Ò—ã;³i’J¶,’d”EeuíoŠü£>æÐçÐÏ…'yøaiñ‡f 3ÿö,¼Wy•SÁ0Š¡2^ ·K²•¡ÂP’§•ÍW­a‹¦$R0ˆq¨Z ó ½ówô‚B¡8›a¥‹ÎdÖ°oè«ÌœÅ¡î{ËQŸ’t#ïÑa°¥„l¦©ª-›'d— h®9»· ¶Ì›'xW³#Š“ÁoéÒ÷wÖ´ë`ïFš~„v-¥I¡ Ô¦¾¥CíŠÀèI QLYo¨>‘jä}’Üæ}‹—áè)}·¸èûɺIM“—ݬ ßsVþj6ÝèÊLǧfNÎâï°ß|‡åà^ªÈËËv}®Í¸Mö Þ¶~l38%–ãYô “-´vXL’Bîóu‘ÚIá³Kú¶ßŠê:ü¼î-öýŽŸúí¤.ÊÚk¥—H9ŽÈ™nZcÕ–õ¾™Œ)DnRì©×œé•†y'ëë ‰{ºLˬБ#þ‰€ÔÀs#kÿdy½4&òòªmv1‡ AŽTt'%I¢„о#-ŽL4`¢Þ‡±û²‘øv6¢ÿÑ]VE[ù`ô…ýÿX³“nRùèN>R ±8¶F?!m3®G÷±Eú´H®3ÚOIÇ选>(#[¤ÈHÓfÁ•÷>_Þ>·‡BoëvœoQŠ’»zžÂˆÇsÌQL¨ 9›:áWÞ{— endstream endobj 414 0 obj << /Type /Page /Contents 415 0 R /Resources 413 0 R /MediaBox [0 0 612 792] /Parent 371 0 R /Annots [ 398 0 R 412 0 R ] >> endobj 398 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 675.442 293.438 686.226] /Subtype/Link/A<> >> endobj 412 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 657.509 292.332 668.294] /Subtype/Link/A<> >> endobj 416 0 obj << /D [414 0 R /XYZ 72 744.907 null] >> endobj 30 0 obj << /D [414 0 R /XYZ 72 720 null] >> endobj 417 0 obj << /D [414 0 R /XYZ 72 644.568 null] >> endobj 418 0 obj << /D [414 0 R /XYZ 72 616.063 null] >> endobj 419 0 obj << /D [414 0 R /XYZ 72 478.836 null] >> endobj 420 0 obj << /D [414 0 R /XYZ 72 369.083 null] >> endobj 421 0 obj << /D [414 0 R /XYZ 72 261.486 null] >> endobj 422 0 obj << /D [414 0 R /XYZ 72 153.89 null] >> endobj 413 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F58 191 0 R /F46 173 0 R /F48 174 0 R /F39 133 0 R /F63 203 0 R >> /ProcSet [ /PDF /Text ] >> endobj 427 0 obj << /Length 1051 /Filter /FlateDecode >> stream xÚíY[s›F~ׯØGðD›½ïÒI;MÝÆÓL;ãØzs2,6 {üï{`A©R¥$jã'à°œ=—ï|œÝ%h†ºý2=Å p ˜B“H3¤…Á"àh¡ïòq‘E·>#Þ²ðÇLï×lº\Ø´ Ë8KŸ9Ù•MlXX÷@1ñßM^~›Œ>(ÌCuzÖD£ébtóŽ ä¯Á<0衵@B¸&èzôfDIÇÖ`ÍV*%6F"M &\;ƒß2&ëéÛïž¿ë‚o*€õàe—Õà¾â+©ÚA Qg’x“¹u7EV!¹÷©ôQ­ª¾+çqáîrûii‹FÜ Ë0÷©ñf¶ŒÓÙ6KÇBa)%SΜ1W¶\æÕ´iÑû¶ã0¬õज़9²lžÛ¨5®¸ËÒ¢1¦Qb{*•X7GCT‚Æš¹ £‡<.íûÜÎ⢴yáÂ)Ìz8)ÇR‹U<‰$n”\%q u;æ ‚Äµ7+²Ãæl•gªÌÊù³³]mã¸Á\Ë5Ûh'›®ý\Q·2ªžKŸ/ÌÃ…m#³úÝŒ%!›¨9Öš}s6ft|:6åàÀ7Íâd µ2óå°3í˜0Šj¤Øbxœ`Åä?¶îÃdi‡ðK¡‚ÕTyý‘ ¸Ç4fÄŽ\*j{vES VÞU²סXn=„D™m¨®ñ\?UÊ*Eï™»ÖXY‰:Å8¬×˜_lCÂ%&Fn1þÞgÒ}¼Å঴[ïì4~Kk«©u¹ëæ•¢[3ÿ×?$?Å?äg‚úù(‰µäGf(±ó¯±f¨b±BŠÓ¡(& <Ù1)Š}Ç5˜q·tŽ*ŽORú°_ÇG£‰j;ø26¼îm÷èßÑH±)³³ší,¥g)XJóÔH=5RßIñ/@Rt¨“Ú¶Ï`à+Å¿ízïßwS_§„Á\§›¢ûW„9!¢Rû•ùO´S,8À³'ªÚBUÓjó³Ši0‡$çðâo« ·³NÔšb®ðˆÀ20!@ œ ‹á*Þc ϵy€&(]ÑÍ4‰mZââ169í¬4_Kªv›ø_+ªœàºRˆŸçU޳4kÏþìœ×öÒÐa;XÏÁº×@—P•‰n¸ô,“&îᲜgùþ˜ÃŒabS'?Ï’$Nȼps`F«È4„ħ¾ðªÚrõnÃbþåž-Â8ÁÓlÑÛ”õ~ªðÐ×µV‡'aÛJÐ?^‘`±óéÊàÉ`€œ&B€'°x§Þù<¼«ÖuvöÇŠ±s¡?âÛŠPî=:ÉËËߎŒúNüÇÁeÖ endstream endobj 426 0 obj << /Type /Page /Contents 427 0 R /Resources 425 0 R /MediaBox [0 0 612 792] /Parent 371 0 R /Annots [ 423 0 R 424 0 R ] >> endobj 423 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 136.995 293.438 147.78] /Subtype/Link/A<> >> endobj 424 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 119.062 292.332 129.847] /Subtype/Link/A<> >> endobj 428 0 obj << /D [426 0 R /XYZ 72 744.907 null] >> endobj 429 0 obj << /D [426 0 R /XYZ 72 689.948 null] >> endobj 430 0 obj << /D [426 0 R /XYZ 72 636.15 null] >> endobj 431 0 obj << /D [426 0 R /XYZ 72 528.553 null] >> endobj 432 0 obj << /D [426 0 R /XYZ 72 420.956 null] >> endobj 433 0 obj << /D [426 0 R /XYZ 72 313.36 null] >> endobj 34 0 obj << /D [426 0 R /XYZ 72 190.754 null] >> endobj 425 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F46 173 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 437 0 obj << /Length 1294 /Filter /FlateDecode >> stream xÚíYÏ“›6¾û¯àœY+’öÐ$ÝN2“Nºëž6™)yí G‚nö¿ï6°8ko»îN''„Ð{z?¾÷=Ø»ñ°÷Ëäõ|òê"½Å! ½ù 8CDÄž`bqàÍ3ïÚÿx·.³?§ûµžÎ¨ˆü·eZ¯eQ%Õª,ÎìÜ¥Ìe¢¥½!O?ÏßO~žO¾Nl†=â zXxézrý{Ì¿÷0 âÈ»mV­=Fpͽ«Éoì Ý^;1¢a`¤Bâ)xpoòrç!(æœßÌö#ëEàkcåã·ñ®gcÿ§ïF3ªûÕë{3Ê@·6¥y¢µ‘ƒeQgc ìnWmš„Ô¥ù 6Cú®H‘• YGŠPŽ8[±Ð\%…NRcà‡¤Hn¤Ù°±¶’Ÿ0Çvï®â(¢]cm·=ØÙªúA‡4ÅÕ3Dz3›:+5_® ô8öÛkb/zµÞäwv¼©óÜT™Õ)xÖÜUˤ²£…”™“Wòk-uå]*í ‡x¸ßy×1¼lÈ$(\ÑÈo2­+9FPÄ‚cB«¦ð­½cñaäàørŽ0cýð^U‰Ú†¢ Á ˆ.BZÙ`a!§„ûߪ^`ûRiYh( 5åÜG·jUIã÷…JÖÍÀÉÛGìïÌŒ…(¢¬ì'-›×Àj¶tÞìƒxÌQØKÁ¾”Fíš…21KÖ£ÕG"±8¼b8 ŠRú®¨äbJ PR“¦(ö¥²S:ñâ0m±TeQ¶.LÓ£Àl(L ™ÙgK©¤U“(§?iª0XËjY6ë¶Ûo¤‚ñzUÜ –+™'U³A¼ƒ8Þ”VÇ5§ À¿·R­¦”û™€Ê¬-W„Ô÷9¢í’šæ¡4œ=@uexrS*7¿óÅO[ZÅÒR)™¶|£’ôGPGš—ZŽzE]”= Ð*H?zoŒöa­×E&U~gsbü*Ó/<­{P¿…Lï5µ3 û1ðÆŠD„úC` þCnƒÃˆ\ÉuY¹gËRWã€qî! ' ÀM»ù¥¬ê¦” ý=ÃÃÑÛv:ŸÜWµ³bµ¸c›ƒ:M­2;³÷ cäÚ‰•ࢺ]Y¤ì͈ˆQ;‹É“v¦ó_ËbL)‘ ûÉìÀÐÏBð Š´¨;˜6üfj¬’ªw0ƒÔD½ÎÙËJºÂ-Ï¢4´¡·àíeÐS©õw›”ÓÜ+„c ²MBÇ]çÎ’r1yÏNÛ6„<ß/Ù¾¥3Äáø}º~ꎡéfo;´ ášJ>ÿƒ@ã„>"/F’ ЭÀÙˆFÛÖ¨aþsŽéˆyA„¨8ü@oÓpØžÛ®“ì ¾Mk2Î\UºyäYô_n('î'ÑS÷þOúÉ6+Z*{QG·”Gw”à$eat{Ê ã÷ì¿ Œè! ϗ0êìaü ŒgF'ã‹+x/Lò½”A°«Ä1œa_Ï_$:]­Fƒ ŽãƒãåË/·Is‚¾Ñã¬Ã€f&"Íð”¬AŸ’5³ý¿³Æ3¥á¯Π ü§àþ¯ PÁ("„î¾öPNDXÿvÃ3®¼»ÃX&è¼. ûÑ> ±þØoW0qeä†KeRØ~QƒÿQ;ðb}Ƈù”eª endstream endobj 436 0 obj << /Type /Page /Contents 437 0 R /Resources 435 0 R /MediaBox [0 0 612 792] /Parent 455 0 R >> endobj 438 0 obj << /D [436 0 R /XYZ 72 744.907 null] >> endobj 439 0 obj << /D [436 0 R /XYZ 72 720 null] >> endobj 440 0 obj << /D [436 0 R /XYZ 72 699.42 null] >> endobj 441 0 obj << /D [436 0 R /XYZ 72 671.56 null] >> endobj 442 0 obj << /D [436 0 R /XYZ 72 641.672 null] >> endobj 443 0 obj << /D [436 0 R /XYZ 72 599.829 null] >> endobj 444 0 obj << /D [436 0 R /XYZ 72 569.941 null] >> endobj 445 0 obj << /D [436 0 R /XYZ 72 510.882 null] >> endobj 446 0 obj << /D [436 0 R /XYZ 72 456.367 null] >> endobj 447 0 obj << /D [436 0 R /XYZ 72 426.479 null] >> endobj 448 0 obj << /D [436 0 R /XYZ 72 396.591 null] >> endobj 449 0 obj << /D [436 0 R /XYZ 72 337.533 null] >> endobj 450 0 obj << /D [436 0 R /XYZ 72 306.927 null] >> endobj 451 0 obj << /D [436 0 R /XYZ 72 277.039 null] >> endobj 452 0 obj << /D [436 0 R /XYZ 72 217.981 null] >> endobj 453 0 obj << /D [436 0 R /XYZ 72 187.376 null] >> endobj 454 0 obj << /D [436 0 R /XYZ 72 157.488 null] >> endobj 435 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 462 0 obj << /Length 1802 /Filter /FlateDecode >> stream xÚÍY[oÛ6~÷¯öd5KR%ë°.mŠí%Þ^ÒÂ`d:*[ž.Móïwx‘-ÑŠc;íP©dR<×ïžCbïÖÃÞÛÁ“ÁósŸ{1Š9åÞdî…Ô Y„Xì{“™w=¼¸_æ³›Åúi _çI½”«JTi¾zfÆ.e&E)Í‚ðèóäýàÍdðï€ìC—¡‡^²\ÆÞ Æß{ùqäÝ鯖ã<3ïjð×[7OD@DŒ(÷Õ*N¼&v/·Z†|Æi£c¡Q‹¢@Ëx:“ç眵9Ѐˆ¦Ÿd)˜‰ò~•(F®<„¡i>ÿDihŒ7q<¼KËJÎÌÀ+MB¿~t*Œr¥ÕŽi]ìZˆÎÈÖœ/.zðz¤®å”–>Q 5PÔü&Ó¶GÌÖƒ] ž³:Ž{ @ŒdŸûL°þ.ºFUAkˆ\A“|µ‚½}rvÑ2 xq ¸Îˆ!õ‹¦ƒey"²Eî æ$ŽG:I[,Àt—†žéÉLÊ V•?µOOŽH9€Lç{S'R›X›NWb)§Ó£óòKë•'ùw:…ÒmÕˆùD÷>=¾èE–}•,ŽMäôl3a’!vQ¯šÄú@©õHñ0(·.vê7ERrLçÖBºÎaNËüá(nöQ–¦Kcíî‰RÄÔesi_EƒÌ2ˆ¤í2âƒF˜÷Õ"E^åIžõ0#$Fªkh:B‹§¡ tYˆ›"dD‚!„kñòÏ|%{ˆ`ÊZ4ɾº{ìãȦ…1«&‹ªzŸÅ€­u!KUØ“¡ªÒ¼ÜèößgѰ}^ fÛ ¨¹µÕ)8óá«,ë~b½ÎÒÄ6j&÷²0sI>³ÌŒ@‘ª4dQ¨NYÉÍS˜ÉEz»0KŠÑ`øU™Kf›°i)yWgø:œpp|Û´«¶¡³»HûÁî5Ž"ñˆàÔ°ù¦ùáPŠÒ4s."9ÒH?Ì1g1ï*z)I-@/U¯7¶y4®Rï³´´Úõ7¬ÛͤZ²ؘBÿ]9BAû. À)dÂÒ-0T3 8ŽÚ:wÔˆâAû¤„' Ûf¥~ŸCêÓ/U3û€Ž_Ç:^ÇVr×»ÅLäÝG} ÝÛ듲Nhæu¶ë£­€{Akа Ý™¨Ä¥LdúÕ4ãŽj!Èé“c€«ö嵺ŸÃS4¾ôMû¼•UãéÌSÊgVý…L¾8~ÿª]d©5§í¬ì‚™49ÄÒª³Ó±}(´·Vé誑ïµ^g­êbÕàC7ž]„—²0™­Øp¢Üź±)¿É¤®ú°ÍŠ˜|æÚ4”nêŠaä`À¾ëâàªEU:ÚÖ2«Ù„}ÞØd5s>„ Œôm%#hgÄSY/í6pW¤•TzŸ+ÿ¶ŠônÞ•1ã(¢ÌÙ-þ·:£sæñ€… U'ON¨]¥Û£¶v¦i6n B û>û6Ëu«øyHªM¦jÉÑ,¬×óyÓ9âH•¡*ì¤`Së;ƒÍ¹Â–¼²£UfOºèžóÃHdŽ}h¸hs ÌìÝ ¸«ªì»xˆbA´{ïàžà¾–s¡ò£þñ:·€ÿeŽ ”§ö²ü¼— þO|ÙàÞšA OùÁ—f½zŒ<,ø˜Úh PÈðl!֕Λàaª*rJ{®û>¤7…€¯À*÷öFêñ«Pâ?d$¼¬ endstream endobj 461 0 obj << /Type /Page /Contents 462 0 R /Resources 460 0 R /MediaBox [0 0 612 792] /Parent 455 0 R /Annots [ 434 0 R 456 0 R 457 0 R 458 0 R 459 0 R ] >> endobj 434 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 675.442 293.438 686.226] /Subtype/Link/A<> >> endobj 456 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 657.509 292.332 668.294] /Subtype/Link/A<> >> endobj 457 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.369 172.925 270.914 181.826] /Subtype /Link /A << /S /GoTo /D (section*.64) >> >> endobj 458 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 103.15 293.438 113.935] /Subtype/Link/A<> >> endobj 459 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 85.218 292.332 96.002] /Subtype/Link/A<> >> endobj 463 0 obj << /D [461 0 R /XYZ 72 744.907 null] >> endobj 38 0 obj << /D [461 0 R /XYZ 72 720 null] >> endobj 464 0 obj << /D [461 0 R /XYZ 72 644.568 null] >> endobj 465 0 obj << /D [461 0 R /XYZ 72 616.063 null] >> endobj 466 0 obj << /D [461 0 R /XYZ 72 455.022 null] >> endobj 467 0 obj << /D [461 0 R /XYZ 72 411.022 null] >> endobj 468 0 obj << /D [461 0 R /XYZ 72 353.283 null] >> endobj 469 0 obj << /D [461 0 R /XYZ 72 321.358 null] >> endobj 470 0 obj << /D [461 0 R /XYZ 72 263.62 null] >> endobj 471 0 obj << /D [461 0 R /XYZ 72 231.694 null] >> endobj 472 0 obj << /D [461 0 R /XYZ 72 201.806 null] >> endobj 42 0 obj << /D [461 0 R /XYZ 72 158.912 null] >> endobj 460 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F58 191 0 R /F39 133 0 R /F48 174 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 475 0 obj << /Length 1577 /Filter /FlateDecode >> stream xÚÍYKsÛ6¾ëWð(ÍD¾{˳“̤ucµ=$9À$dsB‘ ÚQ§?¾ àRl‹M;@‹}|»XîBØ»ö°÷óâùfñôµy)J#y›­ç‡"qêÅA‚‚Ô÷6¹÷ayqØÕùÕŠâe+Vk'Ë—uÖîx%™,êꉞ{ÏKÎ×/áÕ§ÍÛÅ«ÍâË‚€0ì/¦À7@1޽l·øð {9Ì¿õ0òÓÄ»ë¨v^%0–Þåâ·6Šö#" 'F4òÕ®ˆx ,M¾L#¥aH•mJ<Áû±6‹¢‘NËùb¼ëã峋7'€™ò~4OÇ%Ø[ÓŒHµÖ/êJHVIã‰×u£Þ9.ºä°@ðò–7O_”è4Q„¤ŽµÍM¡Ø„xiGyÃõƒ(ªëÒ<—u¦ íÞ¶*ŠDÖ Pé—œoW kK©'nW4\²²åÂÙ6ˆàÍ튄KÞVåú!ë È1%pð"1J}¼YÉ„Pô@–ŒÈ(Eì5Õ¾ ñV Ì‹ôž(í!ÄGaØM/ùV™%îvM q⫉zç3cN]–<¬·zœ·0œÊž¨ë qQ7÷ÆIâ~rŸÚë^‘‘òï¹l .¾ŸþU»»²*ZÜe±³QÉÆg »©ÛÒbꜰ½.”W´í•%û>2û¯8„º¡ÕÊeL6þ,d6 tÝ~Gϲ]ÝZ›ÇÈ< ˜;m˜tÎ6;‡¬-.&Pš:ãBð|„ˆ )8–¹˜8°·‚SöÃÄ“’ïöUó8Vržy('²ä4Sçµ Ã»j@Ï‹«†U‚uÙéM>;H¦”x¥s€¨øˆ1µfŒ±¯üESËÒë¿ÐÜMp{ÃÒ*ŽVk¢âEÛ4`CÙg÷Ë)¸ãjlÖ ¬4,µ4ÙKÛCÂô ãb·/y_Z}†ä÷ªsáðÓ)~²%S¯:5 ËóF… Ž&¸¹Á¨ì¨qQKµžÀ_1Ö3w7EvcÄrˆ—ÅF”"öhÁ•Ñ䪩Yž1áHPPƒý݇qdãp¤›ºÃ×%õ"³‡[ÅdÓñ‹U°ó=“ŽOåÍ·øL¿&b>U‘~¾_Ÿ³6o˜äóèžó>5™,n0±€IæÔ„ê4ï Hgf{­€›$¥q¶*˜¾zÜœž¡Ð%Y1=/$¥ŸËk y˜Š<ìù4Ãg7<û,ÚÝT»Vð鄱¿ØŽp«ÀK°xÝ)…Ë#2VM¿‰neØã¹…ÒQ'zH®?uVOý «ÐÐILº/â'( RÏ÷Sÿ¾ÌOˆnLL6f’"¬˜`ŠÜÇ{~½^jxkw<3•C¡^ÉQ ~2hj97ÜÛžd}ÔÐ^`›@ @©’> ˆBG\!¨‚õja}Ô_©Cy«ê95cˆð©·¿õp1ùvuMÜHÀ¯Š&‡DèùÈ#ü¢hꊟkAUëq?œ£Çøps!X‹£ár"nG„ÃsâvâöÉÑ'˜ÒÀ£I€‚Øw»Þo'£1¢3rõArQü5?WŸª<¯ «d_R˜6%ga•—“€á=FfŒÍh+ýäŒÌžœà¥¬÷Á³ŒGF©¶)îŒTQÒUì0ǶҒqÖ} cõ ` ”¯v¡Ã;ÖxGªV‚]9M^dLÚÙN ų»Òˆq¯C¿¢¼¦0„Gƒ!<9jþ'JrïÕÈ:ˆº¨ÿ¿.HÞu[.¡dmg\’€Ëì·µá{¨‘únJ_4Emks¡E˜K•œ;çA>TÓ£3®Qþ„îV54g`ߘˆ8®¿uûcKÇiù•M;Ûy÷Í–Û|?t?1¤8L«B˜úOÔ¶óoÚª:9&>£Í8Ë?rÛ†;5Üž7€ÞΉžª«Þ¾ÊSØÍ‚ã×ê?Àâ¾û( Õ|ËÃçÞÚ½ÁD£G_àÿÃ, ä"„—ð' $Ÿ„ôy¤Ï9šrÂ/ŒPØ ì(?RŸ¾7—µúåU„‡;è(–AìZýüÿ5 endstream endobj 474 0 obj << /Type /Page /Contents 475 0 R /Resources 473 0 R /MediaBox [0 0 612 792] /Parent 455 0 R >> endobj 476 0 obj << /D [474 0 R /XYZ 72 744.907 null] >> endobj 477 0 obj << /D [474 0 R /XYZ 72 720 null] >> endobj 478 0 obj << /D [474 0 R /XYZ 72 699.42 null] >> endobj 479 0 obj << /D [474 0 R /XYZ 72 657.556 null] >> endobj 480 0 obj << /D [474 0 R /XYZ 72 629.725 null] >> endobj 481 0 obj << /D [474 0 R /XYZ 72 597.78 null] >> endobj 482 0 obj << /D [474 0 R /XYZ 72 567.892 null] >> endobj 483 0 obj << /D [474 0 R /XYZ 72 538.004 null] >> endobj 484 0 obj << /D [474 0 R /XYZ 72 508.116 null] >> endobj 485 0 obj << /D [474 0 R /XYZ 72 478.228 null] >> endobj 486 0 obj << /D [474 0 R /XYZ 72 448.34 null] >> endobj 487 0 obj << /D [474 0 R /XYZ 72 406.497 null] >> endobj 488 0 obj << /D [474 0 R /XYZ 72 376.609 null] >> endobj 489 0 obj << /D [474 0 R /XYZ 72 282.69 null] >> endobj 490 0 obj << /D [474 0 R /XYZ 72 252.428 null] >> endobj 491 0 obj << /D [474 0 R /XYZ 72 212.642 null] >> endobj 492 0 obj << /D [474 0 R /XYZ 72 180.697 null] >> endobj 493 0 obj << /D [474 0 R /XYZ 72 150.809 null] >> endobj 494 0 obj << /D [474 0 R /XYZ 72 120.921 null] >> endobj 495 0 obj << /D [474 0 R /XYZ 72 91.033 null] >> endobj 473 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F46 173 0 R /F48 174 0 R /F64 204 0 R >> /ProcSet [ /PDF /Text ] >> endobj 500 0 obj << /Length 2109 /Filter /FlateDecode >> stream xÚÅioã¸õ{~…¾EÆÄ,A[´v»Øb¶;iû!`i™¶…‘%¯ŽI¢ÿ½ä£.;éäØļÞÁw?Šz[zï/þtsñ›w"òR’F<òn6^̽8HH ïfíÝú?÷ÕzµàÔïšÅ’ljÿç*ëöªle›Wå•]ûIJ6ÊN¡‹»›ï/þrsñË:Ôco@b{ÙþâöŽzkXÿÞ£D¤‰woNí½ Jà·ð>^üí‚"tÌkŒxM#’¾˜QBElùý°Ùâ ®—Ž@8#)‹½%ƒß0´ 7»\_-¤~^®óL¶ §íN¶n¤ì`›/xèY°ÐW¥]šÈæ š¼="B\«6lÎÙä2Ëžµ˜¤¥ÿ±_Ô‡òߨ)¤žÚ3Ó‹Õ]Yæåö™ûæºzäfeÕ>|EJðH „¶àˆy5lœ,þôÞº ˆ"n¤D$á· I¹½'ñû“©x·ËRÿ£ªK÷Ñ¿ÆWe+›¶ªú®,×vð¶*[¥oý¯¶™é LF:  >`4fNü(¥MUÕ½‹™Êz!b'Á^üñ;;XCJ¾­Ú)O~^0ÚkiS/‚Яö3tMÕ2™Êªµz‘>ŒEq¼ $‰D¯ Â^¨ cÿ#‚“0 ‰¡`Ô¡)Ì9)aIêŽ}â<žéÐN7Ú`+Ôï_g±ùÄFz~L͢‘8«ä`©ú ï ”½ìÚ]U_/–°¿—…SÕ[°ƒ¼D5þÖ’ œi™àÌ “$±‚š/«õW²ÙÝWõç?l÷2/HJ³ '~¯´š&áCeƒ)}cÞžj|çŒ$˜¹¬·ÌìKÌ7SD7ˆ™ f7›Áæ‰ÏiLW)¼Óf¦ S0æ+™íôˆûªmx§¦>Ô ‚Gþ±êìî}^"«Ds;–ög`ócdlЃ iúôA6å¥Ý2‘C/¬ÕFÇÙ-X¯!&6ÈáAfxp3ç`AH)ˆôö¨]7šÅŬ0\衽šYÄ«c5X§"YÊí¤O¢„Ûëú·„®¾œF¸ZÉõ” <Ô¿ŒÏ8ÎíÛ^^si¿@ìN¾ó¬*÷ƵWí®Z7'EöÕìÅrÛÉÇ"Gw˜²Ô‹£k\õï¶²Ýp—¸¢»pÙCæq¨5Ójþ]5 èük?«žýäpÂ:KyNæ¿ÝÉCëž„9džˆó3„ÈWµ´ÈG»òOÙp‰ÿ­´]M endstream endobj 499 0 obj << /Type /Page /Contents 500 0 R /Resources 498 0 R /MediaBox [0 0 612 792] /Parent 455 0 R /Annots [ 496 0 R 497 0 R ] >> endobj 496 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 509.153 293.438 519.938] /Subtype/Link/A<> >> endobj 497 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 491.22 292.332 502.005] /Subtype/Link/A<> >> endobj 501 0 obj << /D [499 0 R /XYZ 72 744.907 null] >> endobj 502 0 obj << /D [499 0 R /XYZ 72 695.925 null] >> endobj 503 0 obj << /D [499 0 R /XYZ 72 666.037 null] >> endobj 46 0 obj << /D [499 0 R /XYZ 72 621.14 null] >> endobj 504 0 obj << /D [499 0 R /XYZ 72 554.44 null] >> endobj 505 0 obj << /D [499 0 R /XYZ 72 478.279 null] >> endobj 506 0 obj << /D [499 0 R /XYZ 72 452.365 null] >> endobj 507 0 obj << /D [499 0 R /XYZ 72 363.845 null] >> endobj 508 0 obj << /D [499 0 R /XYZ 72 69.917 null] >> endobj 498 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F39 133 0 R /F58 191 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 511 0 obj << /Length 1277 /Filter /FlateDecode >> stream xÚíY[oÛ6~÷¯Ð£„Õ,o"Å‚4Ë€ºÆÛKSªE'Fe)•ä¤Û¯ß!u±¨(²“vAŠîźøððܾ‡ö.=ìý6;YÌ^ž1á)¤Þbå±#"•'y„¸bÞ"ñÞûoÿÞäɧ€b[s*#ÿ4_n7:«âjg/êwïtªãR×áàÃâÍì×ÅìËŒÀdØ#ž¤ —#‰¥·ÜÌÞÀ^ïßx1y·VjãqÁ5õÎgÌpc(îÌû#Ìdmë2ËÒL rQOŽRĉ%VêÚz´-QWqYå…Fõo=RðÞHÂâLµCOÀÇßíàS{’æËÏÖÓÖD¨êž.Sæ„ †;%¨7öÚÛU^Ô7q}q‚Ï­ƒùœx`oNR¤žçTë€`ÿÆüè¤7¥n”n¶eÕ¼.ù:®˪«æfÐÐÏÓ47Jn×ÙeýúcªÓ¤|5å>ÅQÎÛàÇIRhë0‘þÂÎ@„_VqQÕŠáµ#s¯³ª–:Nôj›VoŒ=qºÕŽé'znLHG1*ݯŒõ1Œ¯­ï+Ç|5pzlûxÜÉ7q[´‚ñ²ÚÆéè Þ˜‰Œí/c­Dб‡'n½¹NµAc+·/s]]åûU/0i׸²õq©Óà>Ä]ºìSä/ómV!Áœ‡Â¿ÔÕ_!¤Än¬Í¥‰ˆ»™Ú)´N÷šå= ël8cw °Ã]wwI°àiÐõf æ.›p‚"ÎÚàj¡°/"%e+cݨ…Ü8SDX'õbD G*ÚåÁxstHãW©ÇLƒ¼)Þ3LÓ¦H’ÐÍÀŸeWJyScÙºZCþÿÑEõн•̳aõ;ó¯0 F{k[ÄPµº(G• œ%O”†S „BíÍp™êR*Å€ ²íæ“.\FiiÒ±cŽ7g°0Šo0Ú2Eî¡á!ÂQ8a8Tú$=öÍv^tTÓ¹92EQȨ“]CEJ9dHªè”u2þ“¾n{' #Aù>¬Q§ ŽÈˆaŒ à¿c1ü–rDÅÁŸî~ëœ"BšÂ¥H¢`.(õÏ ‡›ÃWËåæsG›”æóGœ%õÍë<ƒ&¨ð«Ý†DÐçø\ ­þ§ÕA» endstream endobj 510 0 obj << /Type /Page /Contents 511 0 R /Resources 509 0 R /MediaBox [0 0 612 792] /Parent 455 0 R >> endobj 512 0 obj << /D [510 0 R /XYZ 72 744.907 null] >> endobj 513 0 obj << /D [510 0 R /XYZ 72 624.568 null] >> endobj 514 0 obj << /D [510 0 R /XYZ 72 530.59 null] >> endobj 515 0 obj << /D [510 0 R /XYZ 72 414.979 null] >> endobj 516 0 obj << /D [510 0 R /XYZ 72 387.148 null] >> endobj 517 0 obj << /D [510 0 R /XYZ 72 291.487 null] >> endobj 518 0 obj << /D [510 0 R /XYZ 72 175.876 null] >> endobj 519 0 obj << /D [510 0 R /XYZ 72 145.988 null] >> endobj 509 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F48 174 0 R /F64 204 0 R /F39 133 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 524 0 obj << /Length 909 /Filter /FlateDecode >> stream xÚíXKoÛ8¾ûWð(¡1Ë÷£À¶ífÑ¢º±—¶Ʀ¡¶”’t‚þûÒ¢äZ‚b8‰›º»½X´4Cg¾o†C.žOFOO©jA˜Ì$@2™¦`2²w_—Õì<'([ù|L¤Ê^VÓÕÒ–Á„¢*OÒ»÷va·é†(ÿ4y=úk2ú2Âqpš—A‰$˜.G>!0‹ï_©VখZ&T|.ÀÙèŸjlD[õ–­˜s¨#ˆ¨L$„×Ë·zOOÙöãÞ„Žjáiµ*ÃZº73hÜJÅ)EÜGÙäÒ¦A¹Zž[—ÆÕ<=¯sÂ3³XYŸþ‡*= ®°ë×9æ™ÝeݘSˆ5c 5%iù÷6¬ÜZ³ô=ÕŽÍTCÌHkóÆRg¿D‹‚ 9wÕ2Ì3ódÚ[@°mÛ¤†ÑucB¡Æ8-ãmø7ÍV;‘©-ù¸©ÕƉˆ£$Ä;BPKÙʘÙÌåTfÖû Pá­ìÉÀd jµñÀõmvg[vá]nk·» †šó¤tfCä=ýÜ‚d#?3ÁøP¹.úÏ<°‚‚â& AscçÌÒëüàd}ÚD Ðbô¤©£„ÕmQŠAju|0.åEƒº8Y3Ñfq0f4š(`ãuí•ïè²;FTìdwÍ֛ݴ>o÷cÃNÖhR˜Eh1þvœLÂïÞÁuHÎàN>üØE)T5û’# )í’æÅ¥~î9ÑÛÆ‹Å|˜OÍ×F­(›¦¼¸?qÂòeΣ”İ‰ì¼r{×Cúz˜Sž¹Õ~hkPvS„ËâNÒøt½¸YøF·Š³¸›Âï.ì1ȵìäéÂøÁ E â5ýª>‚­<Ü” ˜~“f'Û`¦cü7Á|[+ž]çí˨ý|QM?,ˆcMäDß%謁Rýzõõ…³1=6 1 úë]¤qçLzk1íùuatqˆ£Ë]Ò0ÛûèrÀ4,!]ŸmçÖü)þùåwþe;“{dbúÃ;“î¹Ø/½Ú™¨#êLBl|wf«Çf¶¸;³Õ±·&ÿ¯Þ„è#kNÑÔãÀòW Î÷&ä!ñ±{“þÍ,gˆ½/f/7Nˆ‰aLR8²Ê\…ÚñReæcAÈÀ•ò›âÜ™(Aÿ5½ùóÝ«Ûæþ&¾O÷Ÿ endstream endobj 523 0 obj << /Type /Page /Contents 524 0 R /Resources 522 0 R /MediaBox [0 0 612 792] /Parent 455 0 R >> endobj 525 0 obj << /D [523 0 R /XYZ 72 744.907 null] >> endobj 526 0 obj << /D [523 0 R /XYZ 72 683.97 null] >> endobj 527 0 obj << /D [523 0 R /XYZ 72 590.366 null] >> endobj 528 0 obj << /D [523 0 R /XYZ 72 474.755 null] >> endobj 529 0 obj << /D [523 0 R /XYZ 72 444.867 null] >> endobj 530 0 obj << /D [523 0 R /XYZ 72 331.293 null] >> endobj 531 0 obj << /D [523 0 R /XYZ 72 237.689 null] >> endobj 522 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F46 173 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 536 0 obj << /Length 1493 /Filter /FlateDecode >> stream xÚíY[oÛ6~÷¯Ð£ÔEŒx‘Dë°­]ŠèÐ%F_º>06‘¥L»ý÷;iYT7n³®ö$Š"Ïõ;/м+/ò^Î~ÏNÏhâ $’xó•Gc†p*¼”qÄõæKï½ÿöÓº\^$òÛ:IÊýå¢]«¢‘MV'¦ï\åJÖʼ`æ¯g¿ÍgÍ0(‹<ì¥ä2”F©·XÏÞˆ¼%ô¿ö"D÷¶Ý¨µÇÏÜ»˜ý1‹¬¡ýa°3B$¡zV‚½ >Üé<ß»†1qL´oZ=ŽPDSãA)"•_®æô,aC” 8‰AH§aQúØhccbŠbÚü“ÔîÍ(Òª BùýÔϵÌÇþǦvŒ?=‹ù “!OöBÂÀÔØhÙm®G¾l›ë²z„„¿”¹*Lÿó2ϳ¢6/?ˆ`í8£B;ι‰Æ¸¢:.e}½-«›Ÿ¯Ö2ËÑ¢\;FˆŸt¼„8E‚Zˆ]¨…†ÒwaÛQéwªœ¢q‹¯_Þ¾š¨–IÀž±‘œaÞ¹¬k"6L1!ˆiGͨۮJÛ-e#리²øCf. u?'ªŽí&¿é¦^är£žAëèÃQŠb‘ö âÈŒr€#‘öcžÈ* ©e͇„8æSÒ›2!Š!ÁI/êÉÍÖ•æG9¢i2° ªÃÑR1v5ifͯ3‹®E¥d£ì‹4‡á]ǹÿ¢rÓÜf͵i)¹¸”‹…ªíô.EKÓÎ,³Æ~,û[Û{«ªº,¤Õp™—‹›‘oNjG¥u¥šw2oÕTЀ…RÁIèêãD&S„a­xh"årÙ¥QÕS¨ J{°°EÙÍ3<Š8£Æ1ëâ]€1öež-÷hh®-…Vê/ˆjc{K ykÙ!u[©]fí³v3^ÉâJM/BÓÜ€9J¨&vŠÆvÖ*e%ת¨ÜÃ0Ž» bcpBú5(EÈQ 3q°¸¦\ Ö´Äø8ßElÕšßî–¢]Œ¶Ú à笸“CÑ!d‰„ey‡>ÌïC€+á\¨Y5½Z˜4°‘Qnú6v žbÌXW´ëËn£ )dežM%†¤Vª©2¥?nôvB².¢ÀÂõè\5m¥gõ¡Â¢aÖ—ë|T<;¹ª`q6!~*X¢»0Bg Âhœ¨©ŒĹCwŸ¥p8­çZúniÈó+ô«ï(Ü0 ³™YªUW°mÞÜñþ£ŒÀ#\ÕÁîü³ìNþvßÜ牋ÙäAÝ<^ôY¼›¼ñbÞ¨ÛÛÛ<›€í—Røc08ýŸÁ¿3ßtù܃ץq̓4ÞÑòö0_îü1Tw/kˆ±ˆ;hÛØÍDa±íËï«(ã³B|eÄöˆB8¢Gô¿±!|”ý >~CÈ¿:ù‚RåßšN’¯°ñ›lÝpØ—Þ< ±_µ»BX*£]YèÌ;%rbÚgZ¹Ìk;·)Õ6«ïfqÄP,\Nüv—,ªÚ¨êÀ-‹æ4>æP^ë{›úÙïe¡&@?xÏU˜sõlÄ&Ik‡}åW-œ!Ú­¦W-Ò}«÷ëEcߥy¬!¼¹àŸçû Bj‘LµßúBÆž_ìÐýõ(Ò7ˆÂµÝ9fÚF9óëûª1mSÔoÀë;¬1ÏmföùÔ®º0X_éÉKó.]뇺&õWŸƒHÔZ­ þà(V}2]m‘5a¦5¢ÏkmÕÝ~ÂS,ШäMk g”ÇÅÀaiuçµ–dÕ›# ¸N#ëºè]Öu-ôT3sxƒieÊ!ƒl ›-1ÎÖD$ÜÜ !5þ3D’ÿE¸û›D0¨ Lö‚0!äîuû‹áqÞe±œ¸‡ ‡=–?*°úo•54} endstream endobj 535 0 obj << /Type /Page /Contents 536 0 R /Resources 534 0 R /MediaBox [0 0 612 792] /Parent 546 0 R /Annots [ 520 0 R 521 0 R ] >> endobj 520 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 682.638 293.438 693.423] /Subtype/Link/A<> >> endobj 521 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 664.706 292.332 675.49] /Subtype/Link/A<> >> endobj 537 0 obj << /D [535 0 R /XYZ 72 744.907 null] >> endobj 538 0 obj << /D [535 0 R /XYZ 72 720 null] >> endobj 539 0 obj << /D [535 0 R /XYZ 72 651.765 null] >> endobj 540 0 obj << /D [535 0 R /XYZ 72 625.85 null] >> endobj 541 0 obj << /D [535 0 R /XYZ 72 597.392 null] >> endobj 542 0 obj << /D [535 0 R /XYZ 72 465.885 null] >> endobj 543 0 obj << /D [535 0 R /XYZ 72 438.054 null] >> endobj 544 0 obj << /D [535 0 R /XYZ 72 324.46 null] >> endobj 545 0 obj << /D [535 0 R /XYZ 72 190.916 null] >> endobj 534 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F58 191 0 R /F46 173 0 R /F48 174 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 551 0 obj << /Length 1296 /Filter /FlateDecode >> stream xÚíX[s›F~ׯàQLÍš½K¦í´uêL3í$µ5yIò°+‹ )ù÷=° Œ±;®;Ó'`uö\¿óíê¸ÖµåZ¯f¿-f§çÔ·BúÄ·++ VÀ8b!µ‘õ~þöë&‹®lâÎËÂvHÀç/³e¹‘©*ÎÒ½v!) ©?0rí‹×³ß³Ï3 v\ k½ n`-7³÷]+‚õ×–‹hÈ­]-µ±˜Ïá™X—³¿g®ñ±}" .ºˆø´Úåc+‡n-^ì£Â…žGš°°‹\è° ­½üv3§ç>ëÚ y¾Jj ¹ÜdJV&†¾04Fî!A“ÄzGý~™{ó­ù<ËRø¾¨žÓ§çïÏ 8 I`9„ Oø+‹Ê¤Òã¹sQªu–¿°ŠÝù+‘ÈT¯ŸeI§…þøQ›@W!3V!s®ó0\„<€+Q¬wYþé—눴Ì6=7Aâçj> p]Êe¢gáÛQ…ïµ E1âY¿¾ýc¤OF¡vzÎzºu[&¢(4~X·È„ Vª¥nêþ,  % •åiä!½ ºßŠ=öÍ^ 8€ÚVÖÓà˜Ãn€¼°µ÷Áõ\-ÕCž‡Â€72Ë$†¸Ç]§¸« Ð ¥ë ™pŽ8È;ºyô®…ùüÍË7‹ul±Ì¥PÒ|ýèQ|W9j‘‰~Uk¡ŒŠ,M‘FXe=]yÓ•ihÎxi¾«¤DòF¦Qœ^ë¥ÕjmdLRê÷²ÑHô½Z ZåZªw")å"<Š‚P¡¶ò«/†˜ºùÖ7'#* yË["Šr›sÙ€´§‹ºÈ'ì`eˬLÕOx$6Jgô`È`"z{ygcŒç"‰£=BÚºäò3dUõk¾Ÿl`!#R”¹y‹\ºØT9éµ?NÆ{säS §(ÄØµ•I‘‹T2/î`Œ^¸ r£qBÚãÄd¨gvøá$ à˜ xØ9žü¦ÕLVeÚ¡í]s´49ÚUaß¶­°‹ÕºO¹T‰z?Êó}@w Àåó‰ %rÕ:X)3Š:>2 É àc êïˆHˆ'¼KËÍ•Ì ¬ôs[1Ž&€Rs©òXÓQu?SÞ9@8ìGt!U™W;Óbª±hˆ0kÛu1h;¹Êá°Õ)~!~XNÑ„R§¢¹Arô ç=º»— à÷Iá¢ÒÞI2`…ö4¤â¼9VuÖ‰ºýT œÀ\Áîü^v'ÿ»oï ÀuÄcG;üµä~ÙVñvñ:üÓ—(Ê›8£ÉÂBá=À~#ãã9†?àùS1¸ÿ·uA÷àíÓ8ü×ò'i¼¦åÝ4_5ñH5É¡˜Ë{pÛšÍHc±íÛïA”qï…AÞÁÊñBH‘çÒÿÆ…ðQîƒøø áÿtò$tò$BUa÷;^ÛÖ[ØÔ›çeÓ«©6jÚ¢ª|¯ENôûye\$…Ù›–|ƒ:n@Ó™¸9ØeȃK”S±‡Oö3@ö=f€í„£ºˆ\‰bt èCW¹üöðe³ç!sÀ"03OòŸï<Ç9 Yޝ=¥?xz=:YgalböDB\Žçgkq£êö† d;>!#s÷?ã«\€då«^9`ÔAüãÞÃÍ endstream endobj 550 0 obj << /Type /Page /Contents 551 0 R /Resources 549 0 R /MediaBox [0 0 612 792] /Parent 546 0 R /Annots [ 532 0 R 533 0 R 547 0 R 548 0 R ] >> endobj 532 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 682.638 293.438 693.423] /Subtype/Link/A<> >> endobj 533 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 664.706 292.332 675.49] /Subtype/Link/A<> >> endobj 547 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 137.789 293.438 148.573] /Subtype/Link/A<> >> endobj 548 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 119.856 292.332 130.64] /Subtype/Link/A<> >> endobj 552 0 obj << /D [550 0 R /XYZ 72 744.907 null] >> endobj 553 0 obj << /D [550 0 R /XYZ 72 720 null] >> endobj 554 0 obj << /D [550 0 R /XYZ 72 651.765 null] >> endobj 555 0 obj << /D [550 0 R /XYZ 72 625.85 null] >> endobj 556 0 obj << /D [550 0 R /XYZ 72 597.392 null] >> endobj 557 0 obj << /D [550 0 R /XYZ 72 465.885 null] >> endobj 558 0 obj << /D [550 0 R /XYZ 72 438.054 null] >> endobj 559 0 obj << /D [550 0 R /XYZ 72 324.46 null] >> endobj 560 0 obj << /D [550 0 R /XYZ 72 182.956 null] >> endobj 549 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F58 191 0 R /F46 173 0 R /F48 174 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 565 0 obj << /Length 1321 /Filter /FlateDecode >> stream xÚíY[oÛ6~÷¯Ð£Å,/"%Û°­]‹Ð%F_Ò>Б¥T;û÷;”(ERdÇ^‚¬)ö$Š"Ïõ;écì\:Øy?ùm1yõŽ G")¨p+‡qO:žë#W2g:Ó¯Óp9£xZæ³9õüéÛ4(×:)T¥Éi=w¦c­r]¿„g_&¿/&_'”a‡8¹.ò°çëÉÅì„0ÿÁÁˆIßÙV«ÖŽ+|xÆÎùä¯ ¶†bDÀ>Œ¨`fµ NîMž¹d´Œ0ójo~ýøÇˆ•µ­:û|õÎíJÂΜº ˆ×‚‚Xå¹ÙËüÎ2J‘KÀ°zÕM¼2G¡*T^¤™®FKª7 ·³™10Ùî~k—žÇj£ß¤I¡o‹•„HÄy»ë3æ¸^Å»«8’ž×¬9QÙŒyÓKë“}m=8å"é·êNN®·}i=ã˜ñŽm¤ìî9ãa€Þœ$¹õâ*2ÐãxdZÚ¾¨úу'¼›·_t\·QqU´ ®† UèÜn¯²Öã(±ÏÂ~Lg”O·vöFgyš(«a§ÁõÀ·^r@ÄC’ÙººÔÅ'—z,hœ!Oú$´]³ºɤ‡ˆ¤'R…a•F¡‚a$¨{°° -“â'2†|— näöñiF™ª8 ïÐP\ézé¯ÕÂΦ êzF ù6Çe¦›ÌÚgÞÏx¦’K=J ;èøH0Á$–9F•©µ.);8¦ç-qY&Ÿ)åƒõôÂ!÷¢èÏóÛ% OÔ..š€­Ê$¨X°®;«šmiv%—ZêšKD ÿÞò |Äß>À–ð÷¸*+Z0+¨c£Ë ¸Þ#l¬0=F˜QIöX—”ë¥Î,ƒ¬êçÆ0I]ÿ= fºÈ"m>nf„Oõ>ëæÀDZh­þLefv&ù¾ºb‘»*\ j§á¿¡‘«,]Û¿V?ûØnîI$dS¶" +zì£ù~ïäpÙë“™Þ q<`…öÄD65¡^U[ÆÅ=÷÷yJÁ2þÙ?$¬Åøf—k”#—»G;\å€ÜÏÛ,ÞOÞð0oWäåÍMàöX  'Ç“ŒÿmP¸÷ËŸ‹ÂÅ#lÜT ½oŸÇ}îåñŠ—·û |ÙøS3ÝNÖ¹ØïÁmco4#…åzȧä˜+þNÎ`—ù“rÆÞ☽Œáÿtò=ÓɳÜ Ýg¹ÎŸfeS«}UÔT…É|¯BNëñ;£\Źݛ‚”låƒ:¸±Suuêz§5 `q¸DÍ y›;Š<ÄÇKôM@ÓMŒ".ÚnÈ:…QƒÈžMª w!é5­±0²³óX™¸llŸ¬j阉Ûa‚{4:— QÏí÷ŸþLÃ2n ³,®ÒìõlΠ.ß«XÛ„¼Iã8J,œ~¬U jºS€{i|†Kwˆá$t,U~µ…:ÿår­¢pùïÐÅôg31ìlœëyüǶ‹±^»†ä?YßP<²oØ@a…¸‡š†˜ Aä1¿Bž¸Ó§Ïwvú¢ýM¾*‰âæ7rÝìTS5Qæ-ÿg¶À¾à¥ îα:Òß^ïå_ÙL|ŸW¶á¿Ü^9øOŽÑ¿,\Š¡w‡Úl.(^…˜³©n†‹º= xWIxï`1Ÿ|ø¡1å¬g=Xýà*IY endstream endobj 564 0 obj << /Type /Page /Contents 565 0 R /Resources 563 0 R /MediaBox [0 0 612 792] /Parent 546 0 R /Annots [ 561 0 R 562 0 R ] >> endobj 561 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 211.957 293.438 222.741] /Subtype/Link/A<> >> endobj 562 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 194.024 292.332 204.809] /Subtype/Link/A<> >> endobj 566 0 obj << /D [564 0 R /XYZ 72 744.907 null] >> endobj 567 0 obj << /D [564 0 R /XYZ 72 720 null] >> endobj 568 0 obj << /D [564 0 R /XYZ 72 700.018 null] >> endobj 569 0 obj << /D [564 0 R /XYZ 72 671.56 null] >> endobj 570 0 obj << /D [564 0 R /XYZ 72 540.053 null] >> endobj 571 0 obj << /D [564 0 R /XYZ 72 512.222 null] >> endobj 572 0 obj << /D [564 0 R /XYZ 72 398.628 null] >> endobj 573 0 obj << /D [564 0 R /XYZ 72 257.124 null] >> endobj 574 0 obj << /D [564 0 R /XYZ 72 181.083 null] >> endobj 575 0 obj << /D [564 0 R /XYZ 72 155.169 null] >> endobj 576 0 obj << /D [564 0 R /XYZ 72 126.71 null] >> endobj 563 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 581 0 obj << /Length 1379 /Filter /FlateDecode >> stream xÚíX[oÛ6~÷¯Ð£ŒÅ,¯ºÛ°µ[Š+%F÷cÑŽPYr))iÿýŽDJEs¬†>’hòð;·ç{;{¯/Ö‹g§,ðb4ðÖ[/¤^È#Äcæ­ïÒ?û´/’«%Å~].W4ŒüŸŠM½Wy%«´ÈOÌØ¹Ê”,•ù /ß­_/~^/>,ìƒ=bärâÐÛì—ï°—Àøk#GÞm;kïñ ‚gæ],~[`‹;XãV"Š"á…#ÌBøJE»}·îÙ)*º1üÑNÞ~l¦ŽÅbFý€^ûëke^¶u¾i”7_·vTjûr»$Ø/ôû4ßÙ´ºv y+#*"x†(fô3Ë$ÑKùª,'T`^£ÊJêªØ³‚9C¬j0²ÏÀ¸)꼚@GDc2ƒ.¯÷WJ›÷bkž7K*|™Õª4ßUažZU:UÍŸ7K"|5‡n%"±«Ñ¹ªjݬÌËÑR×¢1"œv˜{¤Z}D•J¦Anu±·&~.¿ÙŒ6ø[#0ÝŠ2¶iwŸ(k? íCØßy ?2‡~ ‡'(¢S¤[œ2ˬU;ÅYɲ*ôØÜ0!µžIÔ¶‰tYgÕ=í祂Œâ  ¼íWŽÔ§…±£®™$œI `8›Ö!¸žu3N&D@ÎDÔÉ/>”_”-ìæ!Õ¨@\ðjó^$à? J8~¼è½xßyþqg”õá¥a;Ü~üe‰PÀˆX²gmDh¹W•ÒÓÒÆl ”Áž@„ÿ gÿS ¿iýy½.Gð:Ëã-/ßÎøU§áºi#Ç‘CW)ĺšÈ,¢»ü›áŒèéœAÁâhaíáù™P1qöÒ`H`î’ÆÛ%p‰oÍ7¦{¨¹nÚË÷M,ôüÑ¥Jjç¥VJjsJË|§¾ÒÉW:ùâaÕÄî©—Løºîa;—F]Z4žwRäļŸ6›Ë¬´k ¢oÓr”AˆŒhÀ兔WÃ÷Ï_™®ŽpÄx@ `ŽD ¾c@Ú61(ЦôÛG¶¡@˜¤¯g’TîþÜCÉ¥hÁ!¢ÑÐí¡m1SÙdÌ./Ê*ݘ±7£6ôM#·µ\5¼4ö›Ã¬+ñÏb쵕p»!ˆ¬³.WëêºÐÏ—+Â^ÉLY½,²,Ím„}kö@”4–à` ¨Ä[óŒÁ<°Ç•,¯o!õØíeš¡ ôNN]úß7ðáÄã…ðÉ¿Œí³Ã¯=oZOƒvÍ©°>ðybèy—+±ÿãÙ/×O}5€ ôXÄõTTž«M¡›°Lºo™<û]§•š£‘F0Äc ún²ì+¦®&—GU_­§y÷Ÿ1ΦÖLÐÕSÚtÄ–67E¨`à£PØ? †ÌÑžÜkõì6‡ƒ.:m*­¾¿˜cØQ0o2YNv?ñ&0ͬC{ÓT—hHÈ,s*AÂ4ý:p‹uÉ8¿.Ï-íÎÖDÐBc"Üšh}Ý×0¥cß+Ù¹ÇhbŽüÎ|}ï\Œ€¤G6q t%Ãl¯<2dÑ–LU»L@r‰Ç”» G‚`·_[²j¶Ø5ã‹ÞZ®ÛkÞ8ÿ\ÅhãNs·Ô‡Vœ3%H»ÎIëŽQRVÓEaç¹ûBq§ÁQùÑÿû>Äòè!_™ŽöUy€ÈUn¹kÁÞ¿Š¸ëïœR¨TÙ ʃùøâXp é£ï'ï´9…ÂÄ–=‚ÃþGÄy-U«4P7EËU@éÄ÷¯é•–º-3>™‘#NPâ/€D­| endstream endobj 580 0 obj << /Type /Page /Contents 581 0 R /Resources 579 0 R /MediaBox [0 0 612 792] /Parent 546 0 R /Annots [ 577 0 R 578 0 R ] >> endobj 577 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 310.345 293.438 321.13] /Subtype/Link/A<> >> endobj 578 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 292.412 292.332 303.197] /Subtype/Link/A<> >> endobj 582 0 obj << /D [580 0 R /XYZ 72 744.907 null] >> endobj 583 0 obj << /D [580 0 R /XYZ 72 648.105 null] >> endobj 584 0 obj << /D [580 0 R /XYZ 72 620.274 null] >> endobj 585 0 obj << /D [580 0 R /XYZ 72 506.68 null] >> endobj 50 0 obj << /D [580 0 R /XYZ 72 364.104 null] >> endobj 586 0 obj << /D [580 0 R /XYZ 72 279.471 null] >> endobj 587 0 obj << /D [580 0 R /XYZ 72 250.966 null] >> endobj 588 0 obj << /D [580 0 R /XYZ 72 209.102 null] >> endobj 589 0 obj << /D [580 0 R /XYZ 72 179.214 null] >> endobj 590 0 obj << /D [580 0 R /XYZ 72 121.475 null] >> endobj 579 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F46 173 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 593 0 obj << /Length 1740 /Filter /FlateDecode >> stream xÚíYÉ’Û6½ë+x¤R‚—ƒñ–Š+.Ûã¹Ù®%b4Œ)Bæ’ñäëÓ@I-%ާì²"Ðl½–è^h¡*¦y¥jËT–ØPWøló^4ì×N4œ «VFÆ4 å"ˆIZèýÖçþ¶¹ª?èEyè/o‘¼•5¬¿)ª5öQh€e‚q⎯Êñ}.‡dÅý¿æLøY]dËÒϳ6Ãv(÷KÙ6Øê¹.ÖײF>c"i9rùŽRVY¶›ë¬í•^jˆ~jkkn»¦¶¤²&Þnee˜¹ÆµÒÒÞŒ9q*ý'W]+Gn¬åÇN6Süް5ñ‚±’<ÝP$‚蘒È$†L‚¤qâxŒRûÁjq& Nâ6y”‘õõèDqë)>?ÆázÓç4È$°|0Úܯ4@³:ÛÈVÖã(ã½]Jšt‰á¬ßÅï‹P®>üìÃÀº·hå¨ývsÊnUë¹c®jq«UcÞ<þYg0/ÌqÄY¸Ò¹Î2½›ÑÓ Ó›­‘­³•#”WÄFÖÿèöûÎ,÷’ZÞ›m)/vado3‡$<;*<¤‡HAôîqC±1Ö~ûFÌ/3pTâ¯%v7ÙŸª.Ú[ì™È>@!ú»,eºYmùc Û6ÖÁ¶ ²fÖ~0™/BfìÚ.·aoi© DzLLСŸ(ü匬™Â½O  KQ {¼N"M®¥ÛoïÊ!”Ù2à¶•Í8ÕëÕÉÁü}5ÚÏc9Š ²eÑ6N„¢ypHU•¶UI'Š zÆúºìþƒùbóµÊï’÷˜‚Ÿpô>.y@Þâ±ãù àÆþúÈ6 9às(‡Úœ1—ý¢i@ÛØªÎ ÇPXŸTªQæ'CHÂøØD÷yv){/ô¤`ÊäÇœªHø‘‡Áyƒò#ò|½ÈsµŒ“Nœ_µ¦(¨^w²¾}âH”f\|N¦4Rh·­M­p0irAΈ&!85ŽÆÀ5JnAçêÖVgrÝŽL!¥i­cë17ùúÐ#ËÉú0i)|.û±ÎÓ8DÐR©­´£–PpŽñÂQ_¼;¹!˜õ„çˆ]ˆuáµêJ‹¸¥ –¹žj••ãpÚ'«99ã4õ-dkž§»ÚL×NÒΟ)MG Ž×EE™7{CoìÉóµ‹ï­ÚveÖº3ÕgxÐ8tV¸ÿŸ½0s<+êK±Dœ¬ÿ#Î@F ãæ‹š¹.ü: „Ú£n«Í¦«@|Sl¼ÜêÇñ¼ÇhHX˜œ“÷ÀŒëR>|6…Ÿ•Ñ.”©ÁÝq™„ 6½ÕfµÙ7ÊœXêCí„ÿW±²ï8½A»,*˵Uu‹´MרÖÒ¾+ª¢…!Åß&ê„® 2ë;Ê\BwC CðJ3´<ñÀoV#›ãK”pŽÀDÒJu•»“Ú«á°˜Œ$Ú¹®E‚§¶(v5iät‚–¹Öºº†%ÌÉ,ªŸ¿M+mûeåÞ¾€²Ï|%Їœz`³º-jå°7ÑðbPFØ­|±;"IU·_«¯\Áj¡¿¬¡¶5dv<­~@Rå3æB¯ЬFÐÒi!)Ec±ðÐdFN àI C«ÝXíȬ·šR*Uo à€ïT°q÷öCstp±¯V IãЕªå~–Öp˜ªóï´èy¬÷‚Óx¼L°žœ`œ­þm1óEª™qdùf*˜Éãh5ÃhJXƒ˜s³;#{Äpîÿ‘<¾§äÑ;{úõZp(xïüñzÿë:LÁ { H;´I`o%»›§á–²ŸFSž|<Ší÷ùk÷þ h/&ßð_ì*ü….ñ5™ °‡S#ü!ïYÈ endstream endobj 592 0 obj << /Type /Page /Contents 593 0 R /Resources 591 0 R /MediaBox [0 0 612 792] /Parent 546 0 R >> endobj 594 0 obj << /D [592 0 R /XYZ 72 744.907 null] >> endobj 595 0 obj << /D [592 0 R /XYZ 72 701.903 null] >> endobj 596 0 obj << /D [592 0 R /XYZ 72 642.127 null] >> endobj 597 0 obj << /D [592 0 R /XYZ 72 584.389 null] >> endobj 598 0 obj << /D [592 0 R /XYZ 72 522.575 null] >> endobj 599 0 obj << /D [592 0 R /XYZ 72 462.8 null] >> endobj 600 0 obj << /D [592 0 R /XYZ 72 432.912 null] >> endobj 601 0 obj << /D [592 0 R /XYZ 72 391.069 null] >> endobj 602 0 obj << /D [592 0 R /XYZ 72 349.225 null] >> endobj 603 0 obj << /D [592 0 R /XYZ 72 289.45 null] >> endobj 604 0 obj << /D [592 0 R /XYZ 72 247.607 null] >> endobj 605 0 obj << /D [592 0 R /XYZ 72 183.91 null] >> endobj 606 0 obj << /D [592 0 R /XYZ 72 122.077 null] >> endobj 591 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F39 133 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 609 0 obj << /Length 1437 /Filter /FlateDecode >> stream xÚíYÝsÛ6 ÷_¡·Ê»š%)Q{H“fK/[»4{j{›"Óo²èêci÷×$D[r_Ò»9×./6 ü‚õ–õ~š¼¼œ¼8 "/%iÄ#ïráÅ܋Ä„ià]ν÷þÛ/+=¿šrê·õtÆãÄ?Ñy»’e“5J—Ï‘v! ™Õ¡Ó—¯'¯.'Ÿ& Ö¡C¹!‰iìå«ÉûÔ›ýµGI&ÞµòÂ(ÿÂ{7ùmB{L{{L#’‚œ˜QBƒ÷Ù\g ,ÏÿªRåÒì•s J$®uÕ I·Ý<½@¹ªY"íMY|ÁÑ/z.ÉtPêŸ-4"LÕH)uGÈÛªÛ8)ª¼{ n×0䩟!¥ÔÕ*+p^%ëµ.Á¨`K0ÄŒ1’ ÚÚu…9M[•rnvʨyí^èvÒ¯äBWǨƒå¬›ÌªŽKN™ð?›™·küèñâ4ì#eÆC놋ItpÉ‹¬® LNz“9'!‹@Œµ¶ jk2WÙò•¬ël) ²EaŒºá»°êžS©ëFåri []ÈO-è3².K ¶>PAq–èÏ$c7gž5ÙtL‡”„¼'‰íXg€ÑY¦$fÉÐo—Îü¹.óÖâ–wVºéÆs9åÂÿ[åòÙT¿caÑìJuî›o ⸠Ñ4HÉ*9Lç÷ò6*6˜ëi6ð z\~¶X±WÈH1ýY5 by‚ÝÛüà ®†Öµ…öŽÞ·-ˆ€BïìZi©¬G¬°r×Iƒ]íþï„KH0ˆ¤€¤¬q#¹¬÷餄mÁ¸•*U£²Býã<¼q¨}ê"moT3‘ƃM=VToÁ¸Ö)#<¹¶’~XIŠ!JƒD5X%q¸Š¾é¨Þ“Íù!ü~|•KyTçJ•ë¶9‘…Z©ýéüëü~g:géý=/‰R1–Ï©ðóë¬Êrô <þyüóÑÅ3¯Á†Ö50¶®t z‰¨¢vôR.‹ŽéJæ¬:à4™§{maÿ.¸Q”³%>B€ë-Z@Š0Ôƒ#ÚZ¶’ë"Ë¡˜2(rµ ær1…b#k‹ ç§ø¿1‚1¦-I¢MIÂaé27%#>9j[ËE[t´î]¥d÷cˆÛúȬ¤Ên§ᾕ„5˜€ÂiÊ|0«ªpFàgÍŽh;?R]½;>;dÌš<ˆO'âÁNÄ=™qÏ‘±‡Vºw‰áƒ Ý4ŠÇŽÄï)1Šï 1Báj§3æ+ý—ùñ±oЧºÊ%ÞºÍ}ÛÜ´÷Tp´‡,yŒK"K ”3ëžš\g¶î xŒ^Dïgó9$/2†îªI3vɼšw¤Fã¿jjdw=CÄ„Ëà{ –ôæä¥Áñïïð‚lÕ–*˜p ý‚Eèº vZYã6¬š1Ð,*½Ì„.Ra adÂ/Œz§Eݽ+w&—²¹1»ÒÕ_Ï‘'+ m¸n0qò ßí ÅTäªl¥{ršlXnTs-¡Ðd!ˆÑª]wÑ·ëÈndD­žSî.ñ»ö̯zìºÝËy*5^jükÑL9z_ú˜A¦£L|+¹ ¤7O¹ë ¹k7Im»Òs-»QW“P¿îj Š•Ì×4+RP2«Žuk¬\ï)%à@‚?Fƒ‚'2@…ñ†óRÛï =Ÿ5Gs£‹Âõ±PÏÎ1ÎC{2v¯5%„ßa樨õóQ‰®qe×vøk×.Ù¯16@§þ¬]?ˆO—ïþ1}ÐP¿ûÆÍÁá/Ü<0Ñwè»ßEHxtïÏÇ£Ÿ¶CNëšÛ"2ßYiÂL»e8ñ9èq>òáû\]UÆ€ ð¤½=ù&¾«Ä¿„Æ-‘ endstream endobj 608 0 obj << /Type /Page /Contents 609 0 R /Resources 607 0 R /MediaBox [0 0 612 792] /Parent 546 0 R >> endobj 610 0 obj << /D [608 0 R /XYZ 72 744.907 null] >> endobj 611 0 obj << /D [608 0 R /XYZ 72 697.983 null] >> endobj 612 0 obj << /D [608 0 R /XYZ 72 666.037 null] >> endobj 613 0 obj << /D [608 0 R /XYZ 72 606.262 null] >> endobj 614 0 obj << /D [608 0 R /XYZ 72 576.374 null] >> endobj 615 0 obj << /D [608 0 R /XYZ 72 522.575 null] >> endobj 616 0 obj << /D [608 0 R /XYZ 72 462.8 null] >> endobj 617 0 obj << /D [608 0 R /XYZ 72 409.001 null] >> endobj 618 0 obj << /D [608 0 R /XYZ 72 355.203 null] >> endobj 619 0 obj << /D [608 0 R /XYZ 72 295.427 null] >> endobj 620 0 obj << /D [608 0 R /XYZ 72 223.696 null] >> endobj 621 0 obj << /D [608 0 R /XYZ 72 193.808 null] >> endobj 622 0 obj << /D [608 0 R /XYZ 72 134.032 null] >> endobj 623 0 obj << /D [608 0 R /XYZ 72 104.145 null] >> endobj 607 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F46 173 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 626 0 obj << /Length 1172 /Filter /FlateDecode >> stream xÚíš_oÛ6Àßý)ô(5Çɇ½4ËȰ¥~k‹B‘ØD€-9Õ&ûô;‘”-)òêCtzIÇãw¤ Ü8ø}ñv½øù7 ©˜ÆÁúKÀ"ŽˆPàqÅ‚u|ÿ|Ü–ÙÍ’â°©—+*døk™6[]˜ÄäeñƵ]ëNjí*áå§õ»Ååzq¿ 0H (èåH`¤ÛŇO8È ý]€S2øf¥¶%<7ÁûÅ_ ì Å}ƒyßàV'Á3álM7I]·CƒœìÉQŠ8‰A‰•ÚÙ55ÊòäöóV×ur«‘ëó^7Šð¾ßµ6MU¼mê+×å¢l s­ï]›‰AI,¬ëýGØIE}©)!:™,1É/xj qÚÓD¬;ÇÀ2ªžðŠÝBFÁФ¢ÈõZßµ«ÃiXézWv­8³CºöS½É\seçZ»7¦ëzß$…ÉÍ£“)¿¸VïÁÚµš»ÄŒºUz[šn<½¤Qø5Oý»»Ä’i£S£ýøe1ÐÁ´Ün›"O“U;u˜ö`r-‰­ž‡õcmô¶«ä…йñmÀˆq%ð„I*óÆÕR ¸òÅveuå;”;]íYoë^jW¶Sù¦«vs¬šÝhM(µö ¤˜ßTúA§ ¸äéJs‚$4?ÕL„·GÀg4>Ø÷”ª¡k/–$ ¹vÚnI ÐnŸ¢¬Mžvît[Á9©Ißævá­2ÿ®ÇBߪñs´é‰D1#ÁŠ2¤Hog¶š‹úßæÇ"‡m´î,Ë‹ÜäÉ&ÿÛÂç¸èvÔ<Þ#Å“Vs)á"gŒFÞò‰p$bÑ `É~4šT„a§##Ås0:k0âJ9R‡!é…h½è\×ÎᲪÊê;i”2†È †îÑ4JÔéè*"&§Ð%².Ô<ºP: k…:t¡âø!ª.4[táyq}á ºu‹ï£ ¿þÚk¼y(“ [+ú@C½CÆÌÍ’„µ+{êÜ$u?ep·s>|õùðh„9šiëÌŸ{R?–ù3 ÐMæøòŸÄ—W“â.R½;9½a…(‰ÏqK„aL'³W£=ßÌTÿ`¦ú3uˆlýƒ™ò3¾"€”nãSç©á˜u_»öcY.÷Ô¾îrjÈåþ¨5ÁÜ| œ³Þ™õ&ƒÎñŒG"D;Ãm0H*5ÿÁmïý&ùªOüV*„(y–KgHð) ¥è 5$”@Z¡H¨˜®kH¨Y ¥´aÎIþ 4$YƒÔÚë2åH͸vü=p-*v8À‹›ªL²à²í¢ûìáõL¹×:Œ¿¶î>‚´UeÚ™6K³èžE'âÕñ¯©2B Çg¸22‰0¡s¼zÕñê%à?Êãïå`%É9n¢1A±`/ÉtPÏLÏ9ø%rðø7ˆˆÃ>;ù/ˆ§¿i€ N$`'D‘œŠà*yÈ+ýøá„‡*!åî½J©ð?zä°M`¦{§@ÛÕèg+·™@j ™;´[ G±#1vÂ?¼ k endstream endobj 625 0 obj << /Type /Page /Contents 626 0 R /Resources 624 0 R /MediaBox [0 0 612 792] /Parent 641 0 R >> endobj 627 0 obj << /D [625 0 R /XYZ 72 744.907 null] >> endobj 628 0 obj << /D [625 0 R /XYZ 72 683.97 null] >> endobj 629 0 obj << /D [625 0 R /XYZ 72 624.194 null] >> endobj 630 0 obj << /D [625 0 R /XYZ 72 582.351 null] >> endobj 631 0 obj << /D [625 0 R /XYZ 72 540.508 null] >> endobj 632 0 obj << /D [625 0 R /XYZ 72 480.732 null] >> endobj 633 0 obj << /D [625 0 R /XYZ 72 438.889 null] >> endobj 634 0 obj << /D [625 0 R /XYZ 72 397.046 null] >> endobj 635 0 obj << /D [625 0 R /XYZ 72 337.27 null] >> endobj 636 0 obj << /D [625 0 R /XYZ 72 295.427 null] >> endobj 637 0 obj << /D [625 0 R /XYZ 72 253.584 null] >> endobj 638 0 obj << /D [625 0 R /XYZ 72 193.808 null] >> endobj 639 0 obj << /D [625 0 R /XYZ 72 151.965 null] >> endobj 640 0 obj << /D [625 0 R /XYZ 72 110.122 null] >> endobj 624 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 644 0 obj << /Length 1537 /Filter /FlateDecode >> stream xÚíZKsÛ6¾ëWðHub‚ =8vštÜÔñ-Ét –8•I… 㨿¾‹—DѲ#w2vSë`±X,ß. q4pôËäùÅäÇŸYå(Oi]\F‚F"ÉP’³è¢ŒÞÇo×WM9›R÷zzBE¿lŠþJÕ쪦~æhçj©¤V®Až~¼x=yu1ù4!0Žˆ“› ET\MÞÄQ ô×F,Ï¢kËu%iå2z7ùc‚½Ž·•V÷d¨;a%œG‚`„™p 8W]ßN kmµ ÃaÝù`,ËI(ôØA ³Žãª®ºJ.«¿Ué­Ò«¦Ö¾ûJi-çj$xG©‚ÄsP†râäK©­:Àœ ˜)E Iƒ+kû^£²’ó?ý\È K“…Äñfœ]rýn)?«³æ\Y}_4}ÝåoÎLrhJ‚ˆ˜cÇŇ\åBžRvrŸ$I‚È]v?a‰i€yA9lÞÀþ™šZvF[û€1UË20™õj×èÂÐO½¬»ª[»VsiÊÌîØ”Ä`Hí²,a­¼¬®‰iÕUÓ…ùÕ”òøsU(ƒû<›ÖuÌÚF–…Ô¥›Ñ²ÛÊåÃÒw¸‘ ÚJuí…Ô®²j›"¨M]Õ¥ê< à¨Û³“­UZøcë«fçUë4+Õn<×´=ת1J\«Ö¸úI¿ºÒ4A96[&PÎØCCúôó§Ö¼œÃœÙ}±üÞ§~޶aáë`&9ÊØ>,§ùËÐòX†ÚË–)`„± d‹e(½ù<óÈfl3”a%{șɇƒö¥…T®U±pÕªss;‚X¹ÍgÊØù”Ç-+Ù´­îi|ZüU;p-U9·Da÷älÊx|úÆÕrZ)_ µ2øt¤`3ƒÖŒm\zœ €(ïŽ9¸ð€Ž @kǀѮ]í,5uK}5Ðopxï’íÆ§uÑV³àº²ö•e¥»@¬¼ ­Š­@á 4‚ÙŽo`ïyÔ©¤¾¨¢ïöõ„ ,a÷ñƒÀdLÄó[<™Ñô`_€,‚Ò|l= «®]gZêFwU ë¼Û©zÄ= ¬¾E‡ZËqÊ¡”‘³ù‰' ›èz{ªHÚø°ÊIØ=¢k‚&O'¼¦O'¼ò ¯èðĆ>°ë=ïõúk™Í¿s½[3’î|”£ ‹]d>ܶÆvãr¢—£„Ýlœ«]lágÄ”E°ï'‹àÇ,âûÈ"¡ìŽ4B &Ø#¼8PŒ°È‘ì;Ždÿ¡÷°Ù‹…le«úý³jÛ¾þÊ)N9A˜çqŠ'„£DìM¡yvxJbOðð‡Úþœ à=>œ²!üláÔü:„?tZøelßÅ’Ñ»0^î] •/NA«ºñ´äƒË½Ïne² ÛLÌH Žb›EØ@×l øŸ˜ít¬ES—•‡*c4àßð[ü›ŠÃ¿áÞâŸ1ðïÄüÛ™ø7B¯ÁMüŸúâS/c£îž5ÞPžäY\iSæq!{k|C›­Ím²¡läj×–mëŽÈªž;^c~Óc7ÌTVMÛ¹®Ë)L\[#:Y»à ê¬ý$Òë4S®[wM«Jpf `ˆ³õÎDy¼l´×Ê Ëjç—t—1ì†ÛC¿ñW…ðÆÚ–×Váp¥¸’Ë˾¶W…cÚôiÓ1oºó,¹5¢\Ø]žF%8G"aÇ£äx”ü¿’ô%ýÜô ç°,•$ %)ýfYê=¾¢a2Áw±h5×££è@íÕ¥m›ÝËÆè•Ju#IƒAŸ˜œo 08ìOëÛ˜Ýh"¶ˆÜÜ|$ ÕþHáç† ³°Þ`E.µÚ}·Ü¨¦Wª¨LÐ,äö2fþÀ¨šÚ¤ÒáRÖ„Yèð%œï Æ1³ùF™Íøßž šükËÞßnŠñA€g0;Ä0C:°rYL!v§”îù)ç·jÖšcÂÐÚQNßþºçñ"þ‹À|¬ endstream endobj 643 0 obj << /Type /Page /Contents 644 0 R /Resources 642 0 R /MediaBox [0 0 612 792] /Parent 641 0 R >> endobj 645 0 obj << /D [643 0 R /XYZ 72 744.907 null] >> endobj 646 0 obj << /D [643 0 R /XYZ 72 701.903 null] >> endobj 647 0 obj << /D [643 0 R /XYZ 72 660.06 null] >> endobj 648 0 obj << /D [643 0 R /XYZ 72 606.262 null] >> endobj 649 0 obj << /D [643 0 R /XYZ 72 546.486 null] >> endobj 650 0 obj << /D [643 0 R /XYZ 72 492.688 null] >> endobj 651 0 obj << /D [643 0 R /XYZ 72 450.844 null] >> endobj 652 0 obj << /D [643 0 R /XYZ 72 391.069 null] >> endobj 653 0 obj << /D [643 0 R /XYZ 72 349.225 null] >> endobj 654 0 obj << /D [643 0 R /XYZ 72 285.529 null] >> endobj 655 0 obj << /D [643 0 R /XYZ 72 223.696 null] >> endobj 656 0 obj << /D [643 0 R /XYZ 72 160 null] >> endobj 657 0 obj << /D [643 0 R /XYZ 72 110.122 null] >> endobj 642 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F39 133 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 662 0 obj << /Length 1732 /Filter /FlateDecode >> stream xÚåYYoÜ6~÷¯Ð£è²$E]E[ÔušÔœ¦ö}H‚‚‘¸^"Zq«Ã®ûë;òzw­ªOß]î¹,P¦ö˜°ã[=ƒ5¸c8h"3"ý°=ÙÝL˜ƒtA€’(è= ‘gúš÷nbì¿=]ÈñÏ=8  ¦G´w—{½ñD•M¥7€Y ¸ïI%ØÄ_E}ê¶Ù™ù¼r.0ˆší¹ÀxÏKH©ZR;ÞAþv5÷ Ô”@ucëj‹Ü~ì HÐ’,…›—åŒc-*—»ì8¹ Ñ?K—±FG®4F^Iet£™pÙ¼î]{”1LØaÒ5 ˜6]Á’å, fªª ì P™±qî«a]<O«^&U-Õ­R#Ý3³ý8Ë Ðó«âÞ’ˆ„SulvRëŒ%¾ý}n‡µÜLv‚)ó9x·YWÛÉö7¢¹^A~€x¶ógcG 7u#öö»á ¡=§þiãøÚê Œ#ŽÀ4ÊŽ3'p#ðtƒ6Ö’µÇÎ3S®¥F‡¾lìœá¿m-r]…5°„ù¿®(€v ¬Ñm¿›o­[-í/ÈZݸ%GS–Ý!aŒèºâ²™næE¡´'_OÔFPQ»£ÊÎBÍQm+hàÿè™@uÂ2ì&Dƒ\ÐKïÛ;U;ÇΕp¹¼TnŠÐ^ÜF’®71€·ƒ¶üTZDåÀ¢6í%ÔÔ]X•å¸Î\ËŽ„ñ½›iŒf8GŽ&ÕÊE×…,³.’réxðŽ`yYˆÆvߺC*;òªt'æêNÆÝxe-{Ym*’Öúö©—’IÃCph»…m¥öN#¢óô1 ó™€<Ï—ûG]°шc/ŠPœÄnDIÛ§îtœZ¹°3¾†6r€A½/½9»;Õbi^N´& ÈäªðVç!^q(`ÏYOaªö ‡‰d1E:j#‹is;VL(jq­aî2#èˆAÛ‘8xÝUf'²OKÅ EI’<Àžñçµg€QÌʉÁhýy; ¥A }ŒÍmÞk§ËÓöva“ÖǸ½gÞJìÕÕÒÅ1asøÏÇk™{æÄ¾çr¸%þ/c·šªàÞ‰ Eq>ëAâQ-Ý ³û4‡ŽBneÖ_*æŽ7WFâǸ”,·ê›7ºêÝ&ÇDÇûMc”`ê+šô“£ô Ûª©æëöЗ÷Þ¦ 8n„1ÍøÐ#l»çˆþ\%xþ¢?{º|ª-³N…ó«îKmþŽê^Uý•XíG44Ü´Dp¹uÄv†lò h»©BŽa*Õ¨Li Ýsè2¬GPÂÆÏIøï+9|Ópžš¶*§Ý“ãy‡˜¼k %4øÏïÓìù˜ÇÇ^Øc —Æ2OÃ:OîqŠ¢tšÚCΛϓÚeyh—r;Å(ŠèƒcaB)˜Xð‹r*¹½"m×–ªéãÕ„NsÓE-´Ì]‹«€L5Þµu»x}›§ºÁCËÀHÓ:¢€{ Á€pZìÕAó¼YÖ–){có—ú!=ø¡þö€£ˆá-t©T@mIî|Ðy£Ögøì÷L0H?Lç’ÿééEI endstream endobj 661 0 obj << /Type /Page /Contents 662 0 R /Resources 660 0 R /MediaBox [0 0 612 792] /Parent 641 0 R /Annots [ 658 0 R 659 0 R ] >> endobj 658 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 603.247 293.438 614.031] /Subtype/Link/A<> >> endobj 659 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 585.314 292.332 596.098] /Subtype/Link/A<> >> endobj 663 0 obj << /D [661 0 R /XYZ 72 744.907 null] >> endobj 664 0 obj << /D [661 0 R /XYZ 72 701.903 null] >> endobj 54 0 obj << /D [661 0 R /XYZ 72 657.006 null] >> endobj 665 0 obj << /D [661 0 R /XYZ 72 572.373 null] >> endobj 666 0 obj << /D [661 0 R /XYZ 72 543.868 null] >> endobj 667 0 obj << /D [661 0 R /XYZ 72 492.105 null] >> endobj 668 0 obj << /D [661 0 R /XYZ 72 418.317 null] >> endobj 669 0 obj << /D [661 0 R /XYZ 72 360.579 null] >> endobj 670 0 obj << /D [661 0 R /XYZ 72 300.803 null] >> endobj 671 0 obj << /D [661 0 R /XYZ 72 241.027 null] >> endobj 672 0 obj << /D [661 0 R /XYZ 72 191.169 null] >> endobj 673 0 obj << /D [661 0 R /XYZ 72 131.393 null] >> endobj 660 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F39 133 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 678 0 obj << /Length 1394 /Filter /FlateDecode >> stream xÚÅXMoÛ8½ûWèh5Ë‘‹ÅbÓ¤)¶hnb`Ù‰±…êÕäþ÷;E[”å¬v‘“dš"gÞ¼y3$ö–ö>NÞ/&o¯™ð$’‚ oñàÔ üù’y‹Ä»›~Ýæer?£xº©gs„Ó«2Þäªh¢&-‹7fìFe*ª•ùAž}[|š|XL~Lìƒ=bÖõQ€/Î'wß°—Àø'#&Cï©•{¾á™y·“¿&¸³÷mõ‡¶Œ0 Œ­qÕµÞæ…½y”"ŸX¤µn=ÚÔ(Qi¬ù@ø½$GÂ'öƒ/íô˲hª2{Ÿ•ñ÷Ö=k({ßÎ%C„{sBäܬ°X¥<ާö™Ç2+ï£Ì¼×i±ÌTÙæg³Šó›í—™¿­•wËe•·Ѷ]s ɺ ^Øo¢¸Ig”OÓfÛ­²*7YbÞï•yfår©º±•ªºÑ¨H$i´,ʺIcó»R?6ªnêÕã2ïVy¨Ê|¿2 éDÁ:AQ’|xÞØPHm¸þÁ›9¼?‡#„vŽša|1 ü夷y6Üœ¶&šx‹ï$qã\(ú“ùѾ?j ´&Ø¥ º::¢â2|’ƒ„H0âÍ)C’t¹<#`QåªQUí,æÝÍ9Æ.zÚÇÛ@"ì³6” cÛÅynB(ø=tr@ @Y".]·bОª¥ÆXò ŽÎúäøÏú … ŠN _êMêµñ¦ªöñIëî­|A ~Ö/ $›µKÕ\í3ìг 9 Ï¡ý}:FxP!åÉ„@MA¦=DéQÃeeµjðÿ¡ŒhÈ–§ª^Ìø“)¿ÃÄñR„ˆ²¢/¬Ñú3‡Âàø6Ǹy·¢#i7ªÙTÚÙ¢~r&ñwú¶eßdKÁÈ6[Yï¼>ÊGHt!](:Þ¨}|†t¢q*ÏÊ8‚„žA*p<­þOýŠ=kXW³LŶnŸŽ£šÇ dxVAaHܴζ’ø¸LTr`þ8íÆëzÕ¨8 4º[GÐâéxzŽ˜-*U«Ñ–B¸çÅT"p›Š}:ĽZ´kÆ ï÷‡q¹)Zt;¹ó%x´n…®ªÕ¿¼nåÑz ÝñÈZÌG¡8½vI\:ŠkýʵëÌfÍAÅñSúˆBT¯«´ÍÕ¨Úºœ1l×–oß <^Gé®í)w0¹®!ÛbDÁ8Î âUðÇÁàÍGsÖ$>b¾ F÷¹Dä6 ‘ìØCÁãȱ ´· ãï8òg 0¨9æ€Íén{ºmkçÛU¯ËÂs¯”–‘V††*ä0y.%bºòSp2 »3å&³«M³*«w3Ð`<ýeª#Ù%ˆkjå÷7³¢ú ‹|7¸ šÒb0¼Ñ™ÞGõꩬ¾ÿ±Ì£4Cp°húûØañ¶/è¯lÛ‹9ÖV1Ú"ôó‚íé…ÈO¬KÀ‹¯Ž\‰üŒÝÎáú(Éåþ¢ ó<%Û×z~KöTÕ£4Ho É Ä‹nSº¼»N!˜@w·ËÀ[U=ªÊÚú¬\C§á°)i=7Ìû¢ê:Zvyt­S±Ëîv@—½Y[Ý+[†|_ÌBPÂÁÁ4/ms™æëLéàÚŽáaS´ R¿±÷60c;<Ø®lé=<âêsâé×I‹ÓXéãÀj~VÕhi©_½Y“!ÎýÓï>òéÆçï Ê’mo­óý+‘ªÇ#×ð‘ƒ@‹“/G/7}ÒA+0ìNqH¦—«hÝØÄ¡h6”Ž\}~Nï«f¥[3r‚€ÿݨH endstream endobj 677 0 obj << /Type /Page /Contents 678 0 R /Resources 676 0 R /MediaBox [0 0 612 792] /Parent 641 0 R /Annots [ 674 0 R 675 0 R ] >> endobj 674 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 238.614 293.438 249.399] /Subtype/Link/A<> >> endobj 675 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 220.681 292.332 231.466] /Subtype/Link/A<> >> endobj 679 0 obj << /D [677 0 R /XYZ 72 744.907 null] >> endobj 680 0 obj << /D [677 0 R /XYZ 72 720 null] >> endobj 681 0 obj << /D [677 0 R /XYZ 72 677.993 null] >> endobj 682 0 obj << /D [677 0 R /XYZ 72 618.217 null] >> endobj 683 0 obj << /D [677 0 R /XYZ 72 590.386 null] >> endobj 684 0 obj << /D [677 0 R /XYZ 72 510.62 null] >> endobj 685 0 obj << /D [677 0 R /XYZ 72 450.844 null] >> endobj 686 0 obj << /D [677 0 R /XYZ 72 391.069 null] >> endobj 687 0 obj << /D [677 0 R /XYZ 72 361.181 null] >> endobj 58 0 obj << /D [677 0 R /XYZ 72 292.373 null] >> endobj 688 0 obj << /D [677 0 R /XYZ 72 207.74 null] >> endobj 689 0 obj << /D [677 0 R /XYZ 72 179.235 null] >> endobj 690 0 obj << /D [677 0 R /XYZ 72 153.874 null] >> endobj 691 0 obj << /D [677 0 R /XYZ 72 107.483 null] >> endobj 676 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F48 174 0 R /F64 204 0 R /F39 133 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 697 0 obj << /Length 1440 /Filter /FlateDecode >> stream xÚíXÛnÛF}×WðQ¢Õ^È%E»1lÀ…++ȃ”´’X“¢ÂK ý}g¹»—¦eÉ6èɽÎåÌÌbgé`ç¢÷qÒ}bÜ QÈ)w& ‡y."~èøn€Ü9“¹s׿٦Ù|: ¸_ƒ!õƒþy6«R±.£2ÎÖïÔØX$"*„ú ¾M®z¿Ozß{.Ãq| çºÈǾ3K{wß°3‡ñ+#ÎC½*u\À3qn{ö°ô©g­€ÛT€0¹žçø#Ì|­À€à~”G©(E^Ô‚™œ»¡‡q?E-…œ‹„C‚\Îam}ÒWJ9hèáþd%ÔK ˆ×P–¡ä¤ø^‰¢T›hv/%¥%BKƒ¡¾qH|2ªî‹²Êå=뢵×–…ˆ¸Ô»q.fÙ\Ì»E4ª×Y®ždk=/ÔSä9LÚws·)·":CÊPHˆ’`qHXJ`¡zÂ4DîÒõhÖð‚ŒÃ~&k §Ò†à´C4€Žæ ô­yCpoðOä LÛ?Ëèÿ¼á?ÈØæ oÁ0Tc D9“½ 'NǪ[%.b.§6Á`€B‹Ò £îoÜe‡»üCÝΗè¶L!4žJ©¯ZâÛ-¤˜T½_Z'4¸;[™Ýe‚[ÈŒ‰([ž{TzAÌmƲßÙe¾“£Båî²Ü¼ê°P&„\# `$è/G›8´œí'‡²qQŒýáU•lG®‡y@ЪL“N ·  $Äþ3øåu½é˜&¯Ëê×-H™sê;ìHÜ£¶n×1Du ¨ÙÆL¨¡´ªôÁÛTØmžœØªg¤wXI¤>æ;ÙàCßT· 5þ‡Ì÷e‘/D{A,ôi*âàE·Eñ41‹'¯µk²èiQæp†,÷šÔ­‰°_Ê)´ÇÓ!å!f}NÚjþ a½êè endstream endobj 696 0 obj << /Type /Page /Contents 697 0 R /Resources 695 0 R /MediaBox [0 0 612 792] /Parent 641 0 R /Annots [ 692 0 R 693 0 R 694 0 R ] >> endobj 692 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 280.457 293.438 291.242] /Subtype/Link/A<> >> endobj 693 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 262.524 292.332 273.309] /Subtype/Link/A<> >> endobj 694 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [178.649 148.33 450.666 159.234] /Subtype/Link/A<> >> endobj 698 0 obj << /D [696 0 R /XYZ 72 744.907 null] >> endobj 699 0 obj << /D [696 0 R /XYZ 72 683.97 null] >> endobj 700 0 obj << /D [696 0 R /XYZ 72 654.705 null] >> endobj 701 0 obj << /D [696 0 R /XYZ 72 576.374 null] >> endobj 702 0 obj << /D [696 0 R /XYZ 72 528.553 null] >> endobj 703 0 obj << /D [696 0 R /XYZ 72 450.844 null] >> endobj 704 0 obj << /D [696 0 R /XYZ 72 421.579 null] >> endobj 62 0 obj << /D [696 0 R /XYZ 72 334.216 null] >> endobj 705 0 obj << /D [696 0 R /XYZ 72 249.583 null] >> endobj 706 0 obj << /D [696 0 R /XYZ 72 221.078 null] >> endobj 707 0 obj << /D [696 0 R /XYZ 72 179.214 null] >> endobj 708 0 obj << /D [696 0 R /XYZ 72 149.326 null] >> endobj 709 0 obj << /D [696 0 R /XYZ 72 89.55 null] >> endobj 695 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F39 133 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 712 0 obj << /Length 1578 /Filter /FlateDecode >> stream xÚÍXKsÛ6¾ëWðVi&‚ €ÏcšÔ™dÚŽë(ÓC’i!’XS$CQýï» š (Ynš&Âsßî~ ël×y5ûq5»º¦£8 ³Ú8!qB/B^LU꼟ßÜïËt½ % £ùË2i÷¼hX“•Å35vËsÎW0rWof?­fŸfîq¬ÎõPè†N²Ÿ½ÿè:)Œ¿q\DãÈ9t«öŽDÐæÎÛÙo3WËèe ¼¬q€b8/Ä.ri¨äMyR¦\ÞuíEƒÅÔGQàÃIݲ®ïªEþp‘â02kö\¶Àî|+&ϤÈ÷½Á™¸SÜH ¦«—>E°}‰1Š}_íù½fUÅk0œïΛRµZ‡®ÏT³ÍÄŸ^`Î 5T±ä®¬±.· òп8BÅÎ’Pcíby «Ùž7¼ÖaÎû¥ïºÊ[mK§#/z (áV;-ͤÌõÇ‚’œäŸZ.š³úŒä_ê—8D1Õ ½åM[Ë{ qÎ4FØ#FØ^DeîtZD£z÷§ÔŽúµ,ôH¶Q-¯k˜´ï¶°º cÄ–Ýó²¼k«›´}‘3!&àQDÁ ÍšM[$2,ÿ8~€ˆFäL˜´F}|‹Š'Ù×%€YaþFŠ…ÎF„¹ü? ‰G,nF˜Ý–Ð{Ö$»¬ØNá$‘çâ‹bö¼(ºCÃKN„!ÈÃ}hU]‘kÊ @À†%\ µÉŠaŒcÈ÷½ì¯é6]KÕçã<ãv=_,©§ý i…Òx.šš5\:m{¯f•<<@ÁÒcå¤n2åL÷„j•GdïÀÕ‘l-Mô¨ÎÅò8}Ës³Uï€ d™dë²Õ[w¥,@½¸ì×rs¶Ì„ꎮIÚº’ Í&±4·Rj§¿Î¡Rº¿3шgƒÄ,a¤£Ñ£¼Iôt9†çÁ š™Ò™·ÛÉhƒÔCúj¥«¢`ýW§©ìËDÜ$•¾·nZÝc"É2™SѹBàÚqÊÒtUv7M¥ë…á·å,am„Ü4¥„B¹®™(™M9Z;p¸*´iòŠ ±uV»Lg±+Û\ß»Öû[Áû‘MYˆ ŸÃ.ËõÞÊ–Ž©Tó„8—žNYÃNè5RFçÅ"Í9úžùÙ¾4üKj\<ÆÅlNƒ)A.öìjÊÒϬHø)L‡…1ý(½ExÄBÞÞeàCÓy)¤¼T«•Ë c’P÷§^*ûÞDå„™ìA6TiNÿouÛå;hÅ“/\äÍÙ¦Y`½ÁS)–ì˜DŽ:k*]Vu õ¨‡zÖØ\´'Mé8확I µ¬0%º2V߇¢º¤àê×z5ËMí«ºÁzÍoÆÜÌh;Šíg©g‘D@aÚÁ¿n³<½$*Žpø(ôéWÈôâÜüœ¡í­5‡nÓ!JïmSÊ6ýà°Cï8!N½«{”çÅ0'eyÓS9™l^gxf›çZƪ¬Ú”H/x´]õȨÊBp«|×yOkqeKn¹çÿ}…Ž|ua>~U&;žÜ]N(9 Qä¾Z¼w˜t’´8A 6Fª ü%V^P^·£G»á…¢Md†…ÐLîZž‰Ð<ûAŸú þDWlysÊX•<Ér™Km?¼:JÇS×Ýrž±|Y°~ø‚>P,Sáþüáb»ka2õ(»íòîW£ ƒ(8BÔ€­Êò›-?“¼ÜfÉiÚK]¢^v.†LÇ:JCV²`Ù*,ÉÇŒaªÑŒWÎ(ÆÛ¨×(º–,V»î3îðžœ7jÎÞÒÑøsWô/ÃîXö°ºæC%¼o3xnNPœŸ›÷Ø)v|å[ u2¨Âøó§äñgqßC$¸ø«øä{ Œ‰’4 p9q# ødUÓq°0š y!ßóÎÖ5ƒUPlîÕÈó›×ŸúÇJüƒv × endstream endobj 711 0 obj << /Type /Page /Contents 712 0 R /Resources 710 0 R /MediaBox [0 0 612 792] /Parent 641 0 R >> endobj 713 0 obj << /D [711 0 R /XYZ 72 744.907 null] >> endobj 714 0 obj << /D [711 0 R /XYZ 72 648.105 null] >> endobj 715 0 obj << /D [711 0 R /XYZ 72 570.396 null] >> endobj 716 0 obj << /D [711 0 R /XYZ 72 528.553 null] >> endobj 717 0 obj << /D [711 0 R /XYZ 72 450.844 null] >> endobj 718 0 obj << /D [711 0 R /XYZ 72 409.001 null] >> endobj 719 0 obj << /D [711 0 R /XYZ 72 313.36 null] >> endobj 720 0 obj << /D [711 0 R /XYZ 72 254.301 null] >> endobj 721 0 obj << /D [711 0 R /XYZ 72 195.846 null] >> endobj 722 0 obj << /D [711 0 R /XYZ 72 104.145 null] >> endobj 710 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R /F39 133 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 725 0 obj << /Length 1723 /Filter /FlateDecode >> stream xÚÕYKsÛ6¾ëWðHM"„ >2ÓK›¦ãÌ´“Úšöàæ“Å1E*Ùÿ¾‹IÈ´l¥Í´¹ˆ¸XìóÛx·^àý²øq½xó>Œ½ e1‰½õÆ i„p’yI”¢( ½uá]ûvMq³$ß‹åŠ$©ÿ®Éû¯;Ö•MýZ¯]òŠ3Áõ£`ùiýañózñyá°ÀÃ^B€o„’ ñòÝâúSà°þÁ P˜¥ÞAQí¼(NáYyW‹ß4˜ G³eÀ/Á ÂDË»oö}Å:~ÉE_uRŽ7ï£t²) QœÆÀQ‘ÿÐ@Ñ)EY’Xšv&þSìA4 vXén…gêU B±·Âe”‘¥}iàw[®ŽáaÞjÔøPv[=Êû¶oèɦe;³{ËYÁ[) ²Â ÊBãÒ?—iàKªˆø7L”9«ª9Å~Þì—8ðôK- Ö1MpÃò;½Ö,1õ¿È8E­lÚf§©†£p°j$Ò¬ç<*‹†R-Waøëm)ô«3’Ö!¾1GÕœ¼ôØwÓ´Ú$‚·%«Œq¹ì– ä¸ëø ΞfNQ‚÷Hˆ2lœ'íŤÙ;Þ ‡™w½¢¸m‰Óiø8‘(IÆh$$Öâ­m€]öM-Ìl/Íòî(Êœ¼Xa‚QH"GÐ}Ûä óE7»²¾ýœø\@cB%Ù9 ¢Âã±ns Ž,Ñë.ÊRl dBxɈ¿›‘+ QBÂ'Žc8èHˆõ®Áª5_êUϵ¥ÀHö}¾­T"–¡ ÌBžijøe­—™~Œç%2Ô?÷\tšp<\Aiì³¢0,ËΰnÌSIŸLSVéa.º–³eVæÞBì g Z=˜4µ=Nf[B@)Ö9yd¬W Å5ñó-Ïï+b’ Vû ’@Ïlz™úÀ–`ƒB¿jêœÛ½}]ŠCYUzÕ˜_O˜^•ʳN/q‰9÷¥ÐâÆƒ7@ÝŠ›‰±f ñ[^ÏÄaÈ5VèÑoúñJ?°~X1Ó7©UXϘ %f ½´¨\Ö¢Ni6†!š-îÄ#7»´OaPð¼)ìêÑù­ÝÛ‹­%Qq4-0&ãîLíèë\–t—xôÆôhÁëâë‘óyè|"«KºÅ “s0숳g€J¢%>Á'ÑÂòaE2õÌõæYrŽy,k!Ó ‡Ð®$GnË+&Ä|'á¡”íUߨ TÖàÝ ËM©=*UgˆÒÁø¿ªMWûÂj`ç}wåÁÄAìfó…:PÅVnmbS—ÍwV¢brª»™‰[u–H9æt§Œ8Õö´R„/*TŒé•ålŠîza"¨”À);ê!Q_ʸkªª‘\CA‚„Ù6…x{Ê4$ÊP †¯óNÖrÁ«*ž+Jdÿ?¬*A[nß Ó,ô7÷f v 5‹À}Ýý€mý¥I uäúà–w,1†úWA©›pVZÝ›ÁÈYÛyälbö|&F!MO6Iõ2“œØÖ:×}P”ÆôMQji6÷3lDyq–Űݱ,dÅ2²¡ÚvÇw7û•2óØ/ã[tMËgà[ôÁÖ ŽŽ1´€Aüb8³•z¨oylÓÞ ¹¡.F§0Íœè‘KÞõ­´h-N9$ÌÀƒûšš»eüºqQм¨ø{]ª•zßwz¾•óò äU ¹Z(«çÃ5|ΓË$S¡>hºå,ýšxu4†ÌÈsü@ SQß•Å\å PL¢33p0H£3nPb¢ôèÎmE!ßq ú$sv¦Á7ØÔÛ,-Š Ûá&¬¾åg'Õ¿Ò!áó[¤Óa€á–šfß2KAâ šœß4¥Nô©«ölôAp´3*ˆŽµÝ  ©*G2F!X7þÖUA=ׯˆd'[Ð~wÃ[oÆZçF*ÀO[ª–Õ44§à€g_†C®’Çö2ÇBšDÒÄoÙ«üyÈsËŒêcæšÏ ¥©ƒwÏâhœ¸ p)™Û«NU=U1Å£’Z¶öF¦ûÎá3ÝDûg[‘Ôõ‚øѾøcÏ—§T#E4:Ü1 @ ÇWüQ_tÔO Ñï÷U9¶ÿ)„“ïÂÃÿ#„ÿ%A#Dâÿ#ñø/`„±q“ÎÝrIŠB:@åx#6JN9ÆшN”Lô÷±«ÀÔ_Œ V$΂ÐÃc5ÿÂýoû endstream endobj 724 0 obj << /Type /Page /Contents 725 0 R /Resources 723 0 R /MediaBox [0 0 612 792] /Parent 733 0 R >> endobj 726 0 obj << /D [724 0 R /XYZ 72 744.907 null] >> endobj 727 0 obj << /D [724 0 R /XYZ 72 636.15 null] >> endobj 728 0 obj << /D [724 0 R /XYZ 72 482.77 null] >> endobj 729 0 obj << /D [724 0 R /XYZ 72 409.001 null] >> endobj 730 0 obj << /D [724 0 R /XYZ 72 331.293 null] >> endobj 731 0 obj << /D [724 0 R /XYZ 72 199.786 null] >> endobj 732 0 obj << /D [724 0 R /XYZ 72 171.955 null] >> endobj 723 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R /F39 133 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 740 0 obj << /Length 1530 /Filter /FlateDecode >> stream xÚíXIsÛ6¾ëWðVj&‚±që46Ž“q¦n\GíÅÍ´4 IœpQ¸Xñ¿ïÃB‰dhÆÊ~è‰àððö÷ØZ[Øz>{²œ I»øº ŸƒíåFèA.æÔ±wúçVŽ5+õ_ú{cW¢ˆåòÎa‹ÀEûÖ‚2#]˜&qX -÷;˹‡|º ;X¯qºkx~»fõnDCð³Yñh„G¿·@Çåœy¶¨Æ¬Å0r©ó`fQÑäõc2¢cÈ笣Ø­w삆Ì厣wý5'„ØÆ|­CZ·•â-x©î») ßÈXhÕ”f”˜u‰á’ä†I˜¯EO°áwwÄG.#Æ¿ÆÁ—*üÊ0µ(«QnÇðe «‡F?"àGÃÒÇóƒ‰@_5y$³]ÿí 5lm´“jå›$_BRoz"Ä¢ Zrö’«è#þ}ÑÁåú*TuXÖ{%3è##g`\ïdTA=VI\DƒÉJÒd7¢Ôãb5]Pj»-iÝ×âJÔ Xϱój*™X€ß—•åœ9vÙ´‰°šJ£6-¤ç{)òHŸÉÃô2{ àRî’jAˆ€Q—ɾµ·„‰÷ˆWÏuÓ"1îR]0GN¾cP´A{¥…­ã)zÄAP¥;1ύŻHle^´Ù“)€¦äº§{º=žv©ÿ?+ë‘2|/ï.=·ï®^A]ê â¸PT@kÏÔØÛ¤mŠ6õ¦(œ/dèó0Æ5§Eš&¹ ¬Ÿôˆ©>‡ˆõ}_ÛdH›À7aµÙAÆÿ²ÎÂ$EQ‘õSéÚþY৆¯D§Œ|cÙ>:êT›Qíd³ré!ÜùÄ€³®Æö¯—ç#hëSä>„ ˆ q¸÷¼³^XßW+$v×tÜÓ¦ªÁÒÊiB&{7ÊGàP££¾S:ö…å¢+üGE,ÐTÅDY”†mè J'û„Ü*½› ²éM=\F q9ξv_¨={ œâÈG@³ª.ec˜AϤFBŠSà Г°­¹ÙÀ¬=/M6ðs€½¯mäó—Sf&˜ —ÇÛùñÿŽN¸aj—@Š}SŸ•eQ¶½±jÒÜX•mj MØŸß•Ñ/[:muOµÙ[Ý{°ÕÙV÷åÞñV‡Ã÷€*1ug+†ëAù]¹á÷¢>϶©5_Ä“¾`â¾óY}qDpydüx_äE jnŸ ¶®“ðТٗD„ ×þ“ÁÕ"\‹1PHˆ|²ÞG…/åN=¼ôã ÉN^±À–òŠúLè`py`0!ù~LÈ÷1á4F{š„뼨ê$js4*ʸ‡ñÉ®LôkÓÐI§MYÂéÝ ‡Ã4}?™¿Rëe×h-U%q¾®@§}}æjª+si|Q’ ë—¶åF½aÚ} ÁDSM×ó¤E¶@Q˜¾Ò¶zNc\ E˜*¯ÍuåªHÖ›Z•_­ÐÒë©¢©·M»U¡g †íQYQ =«çÉÛ$[A½ææ¹¡«Bëꎢ۲Àð6‰åÃÃ\²fر+åcM °)b=^É’/„Q®Q%_Òke19‘ä°&›»}2fć‹@6•Ð+”]䦎ڒþR©ª§J±„Ñ^¬Òl~“êvTÙss÷( 7ï†[ÿy/"¾÷bÒšM6Çþ…døÔípÈà¿t¾Âs(Ä´-—ƒûÄ>݄ۺmüêR:r…û-¹)ÃRµ‹;My@ñ%þë÷˜ endstream endobj 739 0 obj << /Type /Page /Contents 740 0 R /Resources 738 0 R /MediaBox [0 0 612 792] /Parent 733 0 R /Annots [ 734 0 R 735 0 R 736 0 R 737 0 R ] >> endobj 734 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 507.605 293.438 518.39] /Subtype/Link/A<> >> endobj 735 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 489.673 292.332 500.457] /Subtype/Link/A<> >> endobj 736 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 218.042 293.438 228.826] /Subtype/Link/A<> >> endobj 737 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 200.109 292.332 210.894] /Subtype/Link/A<> >> endobj 741 0 obj << /D [739 0 R /XYZ 72 744.907 null] >> endobj 742 0 obj << /D [739 0 R /XYZ 72 703.94 null] >> endobj 66 0 obj << /D [739 0 R /XYZ 72 561.364 null] >> endobj 743 0 obj << /D [739 0 R /XYZ 72 476.732 null] >> endobj 744 0 obj << /D [739 0 R /XYZ 72 448.227 null] >> endobj 745 0 obj << /D [739 0 R /XYZ 72 406.362 null] >> endobj 746 0 obj << /D [739 0 R /XYZ 72 376.474 null] >> endobj 747 0 obj << /D [739 0 R /XYZ 72 346.586 null] >> endobj 748 0 obj << /D [739 0 R /XYZ 72 316.698 null] >> endobj 70 0 obj << /D [739 0 R /XYZ 72 271.801 null] >> endobj 749 0 obj << /D [739 0 R /XYZ 72 187.168 null] >> endobj 750 0 obj << /D [739 0 R /XYZ 72 124.825 null] >> endobj 751 0 obj << /D [739 0 R /XYZ 72 71.027 null] >> endobj 738 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F46 173 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 754 0 obj << /Length 1577 /Filter /FlateDecode >> stream xÚíÙŽÛ6ðÝ_¡·Ê@̺õØl6Am}K‚B–i[¨G¢²Ù¿ï ‡:Wqvs hÑ—ÕpHÎ}™Ë­£Å­—«g7«§/ÜÀŠY8us°\ßc"Œ­Ð‹˜»ÖÍÞzkÿqWTûÝÚávÛ¬7NÙÏ«´-d©•UåÂme.“FÒB0¾~óju}³ú°ÀŒ[  ë±‡VZ¬Þ¾çÖð¯,ÎÜ8²nõ©Âò‚¾¹õfõçŠAùXàÀ ,z¡àŒ»!É»—iµ—Èÿé /šhÇ¢ÀJúØ;îs:äù,£îÌ>QÉ!²P¸#BBkÛ‰ FG§7nÈÀl„`±ïÓ¥çZH´§Ï‰†ÎI­ªôU'I@!›&9J6a5ÿ‚ cŠ8 kã¸,Æ›kÁí¤N ©dÝLˆYo7>ç#¥'j„>óê”vœ€äºéÌÊ´*²ò8Òij•‰çÀhjÏg¤“åƒ}÷E³   4&f¿.GfŸ›ö¢¸P!P Yì:FÚO2mÕ’¸ž`‘ç>N\€$qº¸mK’ЄG-“=Ar-|ûþ‘©nÎÕ¹Í%÷Ô͹*›¹7&"l÷˜‡)Ò Ä,ðµ#»¬ê"É îM­Wi„Vª»0DTdŠÒhSfÇ“"°· -ߌ¯µêܪ†­7®ë ¢4.bˆpò|NÒ¿±LQHDPYTe®”ÚØ ÷$OL B{w§¤îH¾ÍÔ‰x €ÈÞeŠ0gY›--J#`{ñľªÛÌ«µãÛ·”Y= ‚ky51s434Œiµ>ˆ.Iœ$´NC¡ÉŽeöŽs'MJ5ÑÕx’tXê¨;j"FE9C§n>s¥÷ŸúüÇÇŽÂÞaîB;|ÌÒ®œeJ’³K•”›êþ[»u2³ábÁzt ý¶¾^èË}6©ù´AYÅG>x`·þÁÍ:¸Ô¬¿¹¿­ea¦™Ò2î/Í?½s½”êª*ŠëP®ª B½5£Áe—x,†³>‚5ÍuûЖ)• ×Å ‘ ñ´Â4 b0ÌsÄDDBë®Ê"ê#´ª÷´“”æjbÈS©ÑãNiˆ¤¨ íêª0¬N’Pµ,*e$ Õ/kß·›Nâ¢hË,Õ?b–šÊŒ'USm=¤Âú+£ðÙœsÀ6R¥'…#i[×DËéåGp'UmÎhÕ5pPºI9Ú^ÈÒÈ:Óé(]Éák¢ Ážç : ´ólàƒ6D`•tÃèö$1Žf"vô– r+»Ò)ó.êi¢ÈïºÔ¸—¯ä‰iËX4åW/x ±s‡ªsˆþÕ™™sðC˜Â-! *jÈ ­$=Ô´i JÚœÖ]k’$ç\b„`cCû7øãUVê>Ó…®MŒûÁ}Òj»Š¦¨â<ïâ­(ÀÔÝFGC‡WGî³A:¡0µ·;²·Ëý{éŽ îëBìÎ\‘u2ˆÇ­B&8;" ƒ¿”°÷> endobj 755 0 obj << /D [753 0 R /XYZ 72 744.907 null] >> endobj 756 0 obj << /D [753 0 R /XYZ 72 666.037 null] >> endobj 757 0 obj << /D [753 0 R /XYZ 72 636.15 null] >> endobj 758 0 obj << /D [753 0 R /XYZ 72 576.374 null] >> endobj 759 0 obj << /D [753 0 R /XYZ 72 522.575 null] >> endobj 760 0 obj << /D [753 0 R /XYZ 72 462.8 null] >> endobj 761 0 obj << /D [753 0 R /XYZ 72 403.024 null] >> endobj 762 0 obj << /D [753 0 R /XYZ 72 271.517 null] >> endobj 763 0 obj << /D [753 0 R /XYZ 72 211.741 null] >> endobj 764 0 obj << /D [753 0 R /XYZ 72 181.853 null] >> endobj 765 0 obj << /D [753 0 R /XYZ 72 122.077 null] >> endobj 752 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R /F39 133 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 770 0 obj << /Length 1413 /Filter /FlateDecode >> stream xÚÕXÝ“Ó6Ï_áGg†è$ù»ÓvÚÇÀÀ”†¼Ã(¶rqñ²}!ýë»òʉmÜ㎃)¼$ÒjµÚÏŸV¦Ö•E­'‹?6‹‹KÇ·"ùÜ·6;+àVà†Äk“X¯í—ǼL¶KNí¶^®xÚʸÍeш&-‹H[ËLŠZ℺|»y¶x¼Y|X08‡Z åº$ ç‹×o©•ý™E‰…Ö¡ãÊ-×á?³^-þZБŽÑ@ÇÈ'È %Ô PϲZ?—Ùo¨G//õ˜ÛøÏì½\2ÏþK éN+%—ܳ¯Ó²­³ã*­ëV&ýRy¥DŽ“¸ÌsQ$¸75ÖMše8ÜÊ´¸ÂUØ˺îÅløßì%®+™—DbbNåÙlÆõGíQpÇŠ1yÚ—E’ê(«Gm´1­›ZÛI–+‡QûO8PÒZ>@&<5° )ŒKà,UbfhŒ¶†Wô„¤*kÒE·ÏÅ¥ïb£U Hä˜ôId\‚u°áâÒ |ŽGBßöŽMŒLÞÉ#Qö<‰hÄŒ 8-`Î@›¨7J•(#?>ê”4¾?)YWeQË‘Ôé?è4,"Â!ÌZq‡DÌÔ‘v±€D’TõH˜õzåQ:°o¤qàF½}œû¨×¦W°ñ{-\68ïä  ¥¡œ‚ð_Qwh_»#•eqëØ}Öí Ä‚ t»·ßßß#Ýײi•®‡¢¾I/'"Ì=%ÏÉ¥ÛccFèS9Ô³¸š:r¤ÉŠQ—xQ0R&ÎD]Ïø‘sⲓ«iÛš”ºdß™ÃîE‹A»áiãZV¥j^eâZ>MÖòC+ëæ3ÁpI?*‚;óÀ±wmTY—ݨ_oÔ`Ô%ü+)zJçÂÀµu`/UZõ¢\»Ü˜`p¬:\ ¢3-n•‚Û'Ræ’AàÕr´ƒŒNÅ®T¹8«[W2NßPÊãbš”ë+Bô¹f0¸+’ƒo…oüGÀ7ßÎðP e £ ̰‡É÷åEx?Ì nÀ¼Ê2Ç«Ö;Kù>ÐÍ» Ýæ!d¬-),ߣò£ŒÛfN]—‘Ðuî¦.Œa8RwÝ“Òɰ­Ð?2–Õ¹Ý6Ê J¸Zz¶H‹~>h9JõŸþU€½*«6Mè³wÍ÷‡èg%§ØÀ=âñè.(“&®@„~ZyŠa~0# ®Ž÷ ˜¿l–žg«v6=CÂÝðöðåyðFð¦WS×û6b½ˆY@8-ôR†¤SX»¶é]ïKð‡Â $7gáY†¦‡÷Mꋼʤn§itVÅÀ(€.¼ŸÖF%Íq狨[è®-SÄ{s2Ü~sm}Æw¼‘nÕqû?XÇ=µî ¿fó¹á÷Ù8¥»¥.äþä]ôDV²Ðܺ£ð!ÒFÂßm‡U¾~•¼H5Qè<8àäZ‹Y+{&a¶ üƒîHáR,ŠþXÓDiþi؇ÚuÈÚ+ÞÌ%dn›%ý“ŒÜA¿Ý­ü_"ÞîjÿÝÕ?ØK„¦ß‰„ƒ”8>³,|B\?ÁO>Ì%Žëóñç„Р ô™·üç@hç@´èé²Ú¥™|7°cª£á!¤[€Ï€K؈£“/S/´4€ß: §!ÝŠ¹œ8~¥ƒÑ±D¶YÿÕ£mö¥ú ¿£<™4ÏC¸( »ÁÉÏxáL›ïBLÁü0DŸL‰k]böVÔûC©Þÿv•‹4#ÐÉNŠÐþuúd|n¾þgÝN„é7>Ï…¸õ'¾ÙÏÆL8| 1œ†Ì~¸Uƒ¯¾rs¹ò9Ÿù8ù<Ý*¡ºð‘òû˧3ß-§Fü ãæ9ß endstream endobj 769 0 obj << /Type /Page /Contents 770 0 R /Resources 768 0 R /MediaBox [0 0 612 792] /Parent 733 0 R /Annots [ 766 0 R 767 0 R ] >> endobj 766 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 131.018 293.438 141.802] /Subtype/Link/A<> >> endobj 767 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 113.085 292.332 123.869] /Subtype/Link/A<> >> endobj 771 0 obj << /D [769 0 R /XYZ 72 744.907 null] >> endobj 772 0 obj << /D [769 0 R /XYZ 72 696.299 null] >> endobj 773 0 obj << /D [769 0 R /XYZ 72 636.15 null] >> endobj 774 0 obj << /D [769 0 R /XYZ 72 576.374 null] >> endobj 775 0 obj << /D [769 0 R /XYZ 72 536.588 null] >> endobj 776 0 obj << /D [769 0 R /XYZ 72 474.755 null] >> endobj 777 0 obj << /D [769 0 R /XYZ 72 444.867 null] >> endobj 778 0 obj << /D [769 0 R /XYZ 72 385.091 null] >> endobj 779 0 obj << /D [769 0 R /XYZ 72 345.305 null] >> endobj 780 0 obj << /D [769 0 R /XYZ 72 253.584 null] >> endobj 74 0 obj << /D [769 0 R /XYZ 72 184.776 null] >> endobj 768 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 784 0 obj << /Length 1670 /Filter /FlateDecode >> stream xÚÕYKÛ6¾ûWè(1Cê­ÞÒ$.6@d㶇$(´½*K^=6Ùþú9$-ÊŠ³›EŠæb‘Cj8óÍ“2u®êü¶øu³xºö#'%iäEÎfëøa@Xœ:q õMá|pßÞí›âjéQwè–+/NÜM>ìyÝg}ÙÔOvÉ+žu'ŒÐå§Í«ÅËÍâfÁà0ê0'ö€o@b;ù~ñáu  ¿r(ñÓÄù,wí JàY9ïïT jž„œ”x‘/ÞŠ˜Ó ñò¨c$ COè&Žg”P?Fµ<Â@Y)æ÷Ÿã|X…”ºÏÞ^Ì có¾7ωM¨³òBÐ"E±×eÅ5âyÓ ³zžOÿlË^­¿æ]—-u¯—+øå%œ’NN @‚Oy>´-¨QÝ«ºuSs5[|ö;Þ)Ò^œsÍ;œe­"—ûCż˜LÔc1I}åly•uRPØ–Œ¶y @…»Ò%‡Žl¿”_‹‚ÑkÌg$¤æ=ѺÜ6ï>ðK~3ð®Ÿ9Œ±”¯Qo}¤!Å]áx%Nôž¬(Ú¥ÊsÒû”D^0âÇÎÙbå%!aQ ÈH÷Å·6»R@LCw;Ô¹ô/9Ë›‚ã(«ªfé…îg¹/pûé-h#°š~ ìR÷Š¡4ª`€uÙvýê¢^áàÍÐ#]à°¾X¿‘òËnŽ60? Ýë²ëy‹”²¶¸·|ßôê%ä½-s4žt¡r¤Uû5^2•BËû¡­;œÆ †ºGŠH½f0–ªÃµR±R{*#²YJÝm£±ä®^ÝÍm/Xd}F–«Túý ŽnpÉ÷p~"çæYmi­ }¥H˜í—åÊgT 4©ãªAÅÜpPAÙàÓgø4"ÂXˆ¨ùŽ$9â.‚гp}“ðˆî‰‰"=S®³ *Â_¨$! N ¤Ú+õqjQÉ;9®(ÃÌH8Š…$’usLç¼KÇ€‚+\¯Ðjt>«EÊ¡¾µ&ùíÉ$‚­´3Á³à2HgòBHH¹È3–™Œ“˜ù÷N0~L"?µáx!…쌆*‹×y³/ëkí:&ežÍùüÎ8ŠANóIÊTC!ŠRÖf{.caÌLÕÓ£––ÜqHÏ$óž¡X-íÑÉu((è•.šjÔi[¥ì¯Ù@,x}o‹~Ó Ø+¼¬çŒ¡áÇðÏò¿üûÍÁtU„¨1VŸmü:&S æWw½!(ÅYñ¦àÆ)…n ížý¶#Iàß#Zb½G†¸ˆë/seÞKHlUùóv‚Þ8ûXvºjåHÇœ­À€~?<ç•¢€ÞAo¨ó¶eÏìz 5²¬õÜX½ë›–?ÖÆßŽ9‰•ÆÉÒ<‰ tŸç£…´BÏÒnÛ6ûsmà ±ÒæãüòІ*ëµ?B˜šºãg…`4)bºÿ´%= 9Íðp äü·Y5ðYÁ!†ëFÓ8²ÿBº³~@•l÷Y…$´¬«º]'ŠÀô@ìvÇ–VÌæ›!«û²¿C2Þ:E‡Cô²q[±ŒB÷ ¡jUC¡Ê–Åû[Ã5p³º˜kÉnÅNØÄÓ(š …i޽D©VÆRÁ¥‚oÛFñÉwÒ¦âC=^Â<Ðôø4U® KZ ñ‚RÎ)» ߆0“ÂSÕȉqN}“Š ~Tªiz# ïi»zÚu¢NãÆSÝþ.¶£G¼0·æœnxU—ÕöŽQâ5 %6)Øt¬0û\ö» #e)@1¹SÿˆÅEU¡†"‹WHaÚ‘?–Œ1éAF÷‡\ˆ!kïšb&™@M ™i7ò¬Êeλ쇵Èõï˾šK¼ô!É=z»d‰»§ŸI*>TGvï¤Â ßÇÓjú\Ë?m|:¡‡}6gTQ ‚?T›ÄuäN䇷¯ ,”]M=&§åôÙø² gËÛy½ú™~w—©àØe‚íì<KÎM ä¼”Wn¹\ó%%µÜñq5¹öWZ)mI“})^Éí†ÍKØCþ|®ì+šÿo¯Z£þþüÕëg¹Z=ïy—RݤÅpúi*ÝûËúé§`x„1oôu|¦àÀÉ?¶¡ã^W9‘ŪÓÈ…bü.}ü‚ýzò¿ÂékÀÖO©ÅSÝÿÖÿ8s endstream endobj 783 0 obj << /Type /Page /Contents 784 0 R /Resources 782 0 R /MediaBox [0 0 612 792] /Parent 733 0 R >> endobj 785 0 obj << /D [783 0 R /XYZ 72 744.907 null] >> endobj 786 0 obj << /D [783 0 R /XYZ 72 720 null] >> endobj 787 0 obj << /D [783 0 R /XYZ 72 699.42 null] >> endobj 788 0 obj << /D [783 0 R /XYZ 72 657.556 null] >> endobj 789 0 obj << /D [783 0 R /XYZ 72 574.587 null] >> endobj 790 0 obj << /D [783 0 R /XYZ 72 516.131 null] >> endobj 791 0 obj << /D [783 0 R /XYZ 72 454.318 null] >> endobj 792 0 obj << /D [783 0 R /XYZ 72 376.609 null] >> endobj 793 0 obj << /D [783 0 R /XYZ 72 304.878 null] >> endobj 794 0 obj << /D [783 0 R /XYZ 72 227.17 null] >> endobj 795 0 obj << /D [783 0 R /XYZ 72 167.394 null] >> endobj 782 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F46 173 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 799 0 obj << /Length 1524 /Filter /FlateDecode >> stream xÚíXYoÛF~ׯà[© ÚìÅ%EãÄ ÄMê(Oq€ÒÔJ""q6ôï3{ð4cËI ¤@íî13;³óͱÂÞÆÃÞËÙÉröø^ŒbA…·\{!õB!3o¹ò>úï{µºœSì×å|AÃÈ®Òz/ó*©2•ÿj×ÎåN&¥´‚ðüÓòõìt9û2#pöˆ•ËQˆC/ÝÏ>~ÂÞ Ö_{±8òn ÕÞã"‚ïÎ{?ûk†ŽíP#*˜æÄ+`ãÖâygáˆqA³pˆ8­Y‘À(ùý§<~!xÿ ƒ #_^à •ú„±&A€ º JC{k§–ÃŒ?”reGYn¿ïgúz?~D=÷-bŒBx ¡Œ6‡k½ÓÎ °ŸÔÕVOæ F°ÿ2ÙÉÜ®?S»]–—vò›=Q¢­æ,ÖVG‘½Šñ"\œq™”ÛU|þc³O²JÕ~ &Pü®`² !Š™Sí½L5Œ~ ݾ Ú¹ÅA`œ Ö1†"Á:”!òƒ8ó>.Œý§ï^MÄßèݡԦ·xDü¹Ü«J`:„ Ü÷PÀ]+ì)8‘ÑØ—sø×sø À.]´Tư_Ȫ.r z»n¿ÕVÚÁKé8ž©ý¾Î³ÔØ^ZÞÓ¡Ð7jcë:wØÒ³4É-ù¥“ s`s'©Ü­ªµcVuaé«Ã•,Ñ|ðÐ_6 éE»•-¬ ¬Ñ+y15iÌjƒÌ7«ì ´Ÿ `c¤žl³Ív¡Š•,ZbØ'v’¹PIºmdVÙPyå¤î“æ'r] ÞÉ»O'F^å#ˆ˜¸uÈNwIéÒïçJ×1h©®Lñ¨KdÓ!² 6;†Ò!' Ù!7›ŒÈo b £âÛ]vªVrBÈÈ‘èR-\¶%dLÀ}µi[c¢9}lÔµ€ô¤‘»¢`Á1‡°xn4u‰¬uü(2Œ'eY&·_)‡Êq–ûuÙº¯œN0Ó~$”ÈPL\‰Ÿ†’"ÙËJå(5šÜc¯E“µW306Œæ¬WÓ„Uny‡‰U÷z* W£ *-ó£~¯«ˆå«\Ú:ͧ\uëæ¦$ù½}¨§6Ÿë,©Åæw&`#Òy£ó±hõ G*-æà‰h Õ¿” lÑ9—©Ì®åé7b’@OÄá!ÞÒ?Ñaôá½ó:WØÅ® M4LÖ”CÉtÇ÷àØ++Uì¸Úf¥¥²EK¯é*§¿cÀÌÖd½y³5ýŒ»õ¥–ÅÁ®tù &übhÌJW'Lluê‘åVvíR®aÒ(*9ƲɲڿNê_*Ml˜²&Li£ž)ºjgŽàê©ßl ª#uÕ‘6Õ‘ÙŠMýRºy5'¾²ÃÚd©Ýþ›übš°£¾j*0m3óo²Ýέɱ`e’¾ðþ]8˜ÁQ®,¬GI*UxÿJå«,ß4K0éíÌ%®åùœOÑ(]·=Á.9¨ºj˜ž˜P žþ‡ÎÒ ›<ôÖáÆ<Æ8Âßw½ö OŒi_|}!x¾PÂ#z—ìÅ1ôúÌɸ­Ýí•á9,$ýsô†VŒèÜd%Ò[OŠþV&7PH+ð Æ$*%<m6á.”Oô- ¬ß7yÕtÕÆÍ¸ëÅ÷þMqáù‚D ùOåŽ0/Ê Bz,!;–;Âg[h)ÒJc_“¿½–EQçSAÃQnójw°ºåÕß7YYI7~›7»g®ú… 'ì¤PÉ*MJ§­«#×>†Žƒ|Ý÷à=`ðâ†ýáz;f´ ûˆ™Iè ˪@1¡}… Ȧeÿ·Ì?{Ë «óOÒ27FGÓLÿ‘¦yü»gA-ŽþÙsò'YN!®lˆH·I8":Å^™«˜¡PÄ¥?ؾÉ. èx!ééŸ;ôÊ¿%_0ãV% endstream endobj 798 0 obj << /Type /Page /Contents 799 0 R /Resources 797 0 R /MediaBox [0 0 612 792] /Parent 733 0 R /Annots [ 781 0 R 796 0 R ] >> endobj 781 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 675.442 293.438 686.226] /Subtype/Link/A<> >> endobj 796 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 657.509 292.332 668.294] /Subtype/Link/A<> >> endobj 800 0 obj << /D [798 0 R /XYZ 72 744.907 null] >> endobj 78 0 obj << /D [798 0 R /XYZ 72 720 null] >> endobj 801 0 obj << /D [798 0 R /XYZ 72 644.568 null] >> endobj 802 0 obj << /D [798 0 R /XYZ 72 616.063 null] >> endobj 803 0 obj << /D [798 0 R /XYZ 72 562.243 null] >> endobj 804 0 obj << /D [798 0 R /XYZ 72 534.512 null] >> endobj 805 0 obj << /D [798 0 R /XYZ 72 474.617 null] >> endobj 806 0 obj << /D [798 0 R /XYZ 72 412.804 null] >> endobj 807 0 obj << /D [798 0 R /XYZ 72 234.083 null] >> endobj 808 0 obj << /D [798 0 R /XYZ 72 174.188 null] >> endobj 809 0 obj << /D [798 0 R /XYZ 72 112.375 null] >> endobj 797 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F58 191 0 R /F39 133 0 R /F46 173 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 812 0 obj << /Length 1811 /Filter /FlateDecode >> stream xÚíYKsÛ6¾ûWð*S¡xðÙ[l«™¤MÚÊôd&4ÉœJ¤BRvÕ_ßvI‘å8N<ÓÎÔ>` ,ö |X@ÜY8Üyyr:=ùñg81‹8Ó¹£|‰0vB/b^¬œéÌyï¾Ý®ŠÙõHrwSÆ2ŒÜó"ݬt^'uVä?`ߥ^ê¤Òø!}œ¾>™LO>ŸPÆá„äz,ä¡“®NÞäÎ ú_;œ©8rî,×Êñ‚Ú¥suòû 'Cy×`¯k°‘)8ã*D[ÓeRUF5ðE>)™'b¹ÖÖ£MÅô-xQ1œx ±ÏO4.õª¨õ•Îg3c@A³ÿîsdò»L>‹Ã°áyþüÏ»¤©Ð] Y¬"¦Â #P؈6¡€ÄÅî±”!A茅`±ïw͆”øÜé‘ôÝÛ,¥ï7ç§#ÁÝwWømœCjr;¾Kn‚²±Y¬h1LoÌ|¸e#èɆ±ª.J]áX}“UØ[o×Ä_̱§5Úl×õ¶zw£sìËjìùÀ¹Ì³êF“ÀuY¤ºª²|ãIcÙç®HÞ ’…f£±âûŠz32Î:£¾yc°nïIJ¼'–¥®7e®)~ 6yQ®’%ÒE‰­6¾þ•êµÙ8ÍÜj]ä•þ¡Ï™ýa6œ ëHDºš¡™¶q³ÖÁçõÖlQî^gÄbSiú® ll–Å"K‘üÄŸÙÍî¹wY}Câ¬Ü/æ“xfr„´‚`F‘.[)dí]¶\RŸ~˜a „“Ïó¡ÔÕ7”§´(1ž3\3¶ >vÙ°Aƒvz9ò`oL}6Ð] –ɶØÔͤŸìÞŽù/—/ûø'¸ÙÿÊ dÌ`Ë} (=ØÚ¢®Z:¾„îì^$HÈ¡y‡=}E*]EfÀX&èÑ¥v惲÷±»á‘ƒ °Kð˜Á)HΤˆ1e§&ÎçîY‘×£Û¼rÈ+1ÿ7$ŽÆ"•—:™¡òÉn«šÏ+»· eð}oð¬˜\2¤«¯÷uÒqµLn5r¿¸.ÊúqúòŽ)“‡ÊN7Õö›|óÇÁ1uêPÝÛ²X”É ?~{ñË·¸Óë‘Þ?ʬ&Þi¶Òv{ZueiPÕiº) âˆñIÌ™áÈë%Å)#C~ͪZ}‘7£oŒyÂÖ78ÂÁùÔø޸гPù€C02Bd ¿Œwt¿x X UžÏ9tTÍtJ¡Ú¯s|þ §¨á±‡è‘2LBåé‹WM·žN:·–Ò9Ðû,tQiAŒt^ãADÕñ¦jOªgÇ~»WÕBN% ¨S,T„›Z-½¤k]ö…9ïÇi ‹akCÓs6Œ÷T)ƒ½³mÀÅÆ«]ö:&÷R!Œ™÷Öùƒ“þÅT  2zû$JÕAä{®$ùúØL‰¦ê†jÐΫû;ÊÈ=£l<ÈEØËÅÝŒ&p< ":4l/HG3+&ü‡_TH~4(lhDa{—é¢ðÁeÅë\V€îÉ÷:— é²âùÍeúìeÅ;¼¬xíe(º¬xtY1ì¹Ý¨$–ØZó&óA—1ß^PâÅÚ²ªq`WÐ[#°M†JßË'º[̱å¶7xa÷í뽈þ`ÜÿÆ¿Œ?އÿJïeêkP\=!ŠGýT<ŒŸ«Õ&ÏRû¢w©!ôeýÝ‘üU7í¤nÂÒÓ‰]¤ù8–‡ÞËîi€ïË&,7b9PCÿ #0DXn7ËÂEDÚ5•úÖöÚfÙªÆ2k½ž0dc²Ìä!¼¶ËZ*|ýr'“º·Ø›:γd‘U¥ŽÌ7yJ…a³%íÞµc<2ÛÓ¼áȨyæjs=îN“mìðÙ¾óÀp±Ð1àçtä£ùÈ#VbEa×ô(r“åH¸Uëe’ÚÄF!ùDV`è­Ër ¾ Bóɼ$dù†øQKˆ—ÃgHÛ·7ÃzUëãl+<®=ëyϤ¨1i ý¥*iÑHôlF}ht(zÖ€5ÚÖ  ö9®Q}ëZ¶ëz§1™Í…¨Nv˜Šª| KD.‹…Yáa€þwç?ÐEÖ(±ó_µþÕ¤¬ïªã»Úù®|R­ZGaŠÞ9ªÈQj¹Áb²ÏÞÝ¡’%½SÍj¬O—:)ûJ¤û·.‹Š±á#¡yzì–wæäÛö^• :£°_Åíÿðâ{pËðï.‡? O2!$½,î*2bÊoOC<|¨† ú¿mÊïÔ0!þl4Á –~Wá"ì Œißnßt‡òU»A¼ïö?v5ºá endstream endobj 811 0 obj << /Type /Page /Contents 812 0 R /Resources 810 0 R /MediaBox [0 0 612 792] /Parent 821 0 R >> endobj 813 0 obj << /D [811 0 R /XYZ 72 744.907 null] >> endobj 814 0 obj << /D [811 0 R /XYZ 72 518.062 null] >> endobj 815 0 obj << /D [811 0 R /XYZ 72 458.167 null] >> endobj 816 0 obj << /D [811 0 R /XYZ 72 396.354 null] >> endobj 817 0 obj << /D [811 0 R /XYZ 72 338.635 null] >> endobj 818 0 obj << /D [811 0 R /XYZ 72 278.839 null] >> endobj 819 0 obj << /D [811 0 R /XYZ 72 217.026 null] >> endobj 820 0 obj << /D [811 0 R /XYZ 72 85.519 null] >> endobj 810 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 826 0 obj << /Length 1581 /Filter /FlateDecode >> stream xÚÍX[“›6~÷¯àgb­.\;NçÒd’™­×}J3ä5 W@6Û_ß#$aÀŒãÍöö„é\¾ó#açÖÁÎëÙóõìê œÅ œõÖ ©zòbæ¬Sçƒ{}¿/ÓÍœb·©æ Fî‹2iö¢¨y•ÅS=·¹à•Ð/áùÇõÛÙËõì}°Côº qè$ûÙ‡ØIaþ­ƒ‹#箕Ú;^Á3wnf?ϰÑ÷u ¼ž®q€bX/$aj}S‘”©Pû_½ò¢ž0óQø°R+ö+ö±òûB>ŠÃÈʈ9ñÝÏ`éÄjŒôIo5Òšlu§Æ=é…‡aáÐY‚bß×?½h5UNõ±[9…MÕÎjãvj/ªŠßšïu©ŸYmþ¬ oÔl_ñ¬è›D(`ÄYP†bbâ='Øå’ïE-äp1çÃÂÇØ¸E‰u®Æ{¬s ¥Vn}ÆDkÕ1z=•1ÆÈ­ÒFkQ\õ¯ÆŠÀº°Æ V/‹©X¸~` /¾ÑÇ…j%êFªu‹êœA,FÄ£ÖÇ(´&¥ÓX#0•¸q$|8™\½Ö¬B<ļ€j¯bÂ)À"S­ E$˜öÁötôöxãØÚzHƒÏ.4F¡=€†š·žw vSË&犱cd±<à 1“BcÓû2mrãbÞÔ»R~7_0H–×<Ë2ϳÂÀå{½¢DçAèÀ¸(Ò'W* Ý ¯vw¥üôãížg9JÊý(OÝÔ¼,HˆbfT»‰¢ìÿ…nß ©6)Û0‚uŒAv³#˜y$œ Å=»~3QëÎ%(Caá‹e ÿtnÜh`Á(ÉyUYÙ–Ò¤Z¿Â»4¢ª¯‡²¨Ä•ÒÊxò©åÞzŒÍ]ØÐç´ûN•1Š<F“/m­o*‰ƒ´ô€{ÃêwWß·²×/~™X×g(Œ£‡Û'O>Ýq9g¡{;¥(° ƒ‹ë- #’øóaFAày®ïGÑ"¬Î¥‘·kɋзÉö[–žSÚˆ˜Œú‚õ.3;V¬ÌóÆhm§›ÊÒ¶-5M‘bò{#—b³­yãLÙ , ©LÎ}ßEš ޡİÝÈ…Ê»=¯[T­€ù¹l@Nž­äñ=×dY—I™?ÊoÙP¯L­¹­k•0nžxÔXi–ðZŒÁ0òŽÝãИ‰b)Ñ®ÜÚ: %öËàØ@öĹþvÏxFkXyÆlxʲimölRGÌô»¯I)%T=›ì²òi|Aù ­Œ¶f;'‘»Õ´;®ô 8°’j=8„¥Õÿä ¤ì¡^èÐ7Ÿr.>‘ã;æD†Ñéqô™A½qœÂ³]ÁPÝ›f¾)¯¹²åcÇÕ šl½´Í4ð£ÈŽd“¢³ð2šzöÇñŠf¿±FÙÈlî»ø¤ÈI¸Æù g{ÕUõÃó7^¸´îH€"aC—…€¡øÌ]Ë1Š.ë¡w:W˜ã/úǯSŽFoQ|äÑà¶£Þ¦ªeÓró»ªÇç fV0ØŠg•8.`±¨£#“8ºüNµ…ºÉIÄaâÐ4‚“ ûonr{!CþuWŽ/W¡ÒÐàâ»ÕÉ{_ŽÐÄðN¨ºŠ#â.wüP·$ça T@éÄ­ð»l#áü¶¸ßë™ Ñ`Ä_.͉ª endstream endobj 825 0 obj << /Type /Page /Contents 826 0 R /Resources 824 0 R /MediaBox [0 0 612 792] /Parent 821 0 R /Annots [ 822 0 R 823 0 R ] >> endobj 822 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 543.471 293.438 554.255] /Subtype/Link/A<> >> endobj 823 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 525.538 292.332 536.323] /Subtype/Link/A<> >> endobj 827 0 obj << /D [825 0 R /XYZ 72 744.907 null] >> endobj 828 0 obj << /D [825 0 R /XYZ 72 668.075 null] >> endobj 82 0 obj << /D [825 0 R /XYZ 72 597.23 null] >> endobj 829 0 obj << /D [825 0 R /XYZ 72 512.597 null] >> endobj 830 0 obj << /D [825 0 R /XYZ 72 468.187 null] >> endobj 831 0 obj << /D [825 0 R /XYZ 72 438.299 null] >> endobj 832 0 obj << /D [825 0 R /XYZ 72 408.411 null] >> endobj 833 0 obj << /D [825 0 R /XYZ 72 378.523 null] >> endobj 834 0 obj << /D [825 0 R /XYZ 72 324.725 null] >> endobj 835 0 obj << /D [825 0 R /XYZ 72 294.837 null] >> endobj 836 0 obj << /D [825 0 R /XYZ 72 217.128 null] >> endobj 837 0 obj << /D [825 0 R /XYZ 72 139.42 null] >> endobj 838 0 obj << /D [825 0 R /XYZ 72 79.644 null] >> endobj 824 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R /F39 133 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 843 0 obj << /Length 1650 /Filter /FlateDecode >> stream xÚÍYY“ÓF~÷¯Ð[l OæÒŒD%©@ [P"Ëæ ¨d¯ Y2:X6¿>=š]–µëÀ©}Ðáޞ>f„­ƒ³Å“‹Å¿3áøÈT8‡¹é;’{ˆû̹ˆœ·Ë×7»<ú°¢xY—«5•ÞòiÖ»8«‚*ɳ‡úÝyœÆAë‚ðêýŋų‹Å§ŰCIA/GK'Ü-Þ¾ÇNï_81ßs®©Ã…×Ôy³øs¡¸o0ï¬tŒ0“ÚÖ0 ÊR- r^OŽRĉ%Ô¾ñ¨.Ñ>ª‘–¼¯U‚•ž•~ÙȞǟ긬&t7Ò¾•~‡]¬…ܾ‹|)­Ìƒ¯ƒbÅär;e,ó“¢§4`Z f~OzM|‰(mMò]Wÿ× HšÛM^è›@_q…çÂøØ<¼~ú×hÙFÖ“Èg†$QþìKï!&<.’.»D-è±²kF#“}@oÅȃùdQ¤‘ñ<Èô5. ‹Q—û<³(~<#}›ÙêÊü¶©³ÐšyÀÙ΢q‰‡ƒ(C`—γCŠ`WqQ”9o×.놀 \`ì¶€P*´qÖÊxEÜQíAn]­ê"80wmÔb~$e\ÎáÎ8r½65gG-iÁž³`ÈtΠÝgÂw&”{ OœBço›ñ3D]ùM2¾OòÓR¾¼Êë4ºÔ*¢9ƒ)˜IFæ>ÖK¾Ã˜[›P©=I%aPÅÆÊdc’I»5˧ Q§•MÞðãñmî†iÌ,S–u’mGEWø³nìÁsYTõåF¥óe™üŸŠÏó!­9.]‡6£Ÿ‚[ÕShÕm%kÙiX§Ðέ÷WùŠºËk£·¯Æ#ˆÐl®r<ʇˆÜ_®¶½g¶­¥‹èmQÊ`¤)‚®$ÀûYA¤I4ö ª•³àPŽ|,™ZF0A‚ø§3Ûï.Ã<Š'Z‘+ç­ôà uà¿G&[}£óçWy6¥Øó‘KÙmŠéý]Ø’†Ò_[t'ú߉ÃVÜ!?tÉEžpO‰eTÁ+€G„€ L3Þš§‘å©HLNw¦Nœ¥:Ç®@‰àTÌŒQ{ÕK@ylšF£gP»è턨 ØIÆØgwŽêí{ÐËG•îYör®Z´R>_¢™oy×Í®éÑQ{A™ a…½æöû(¼ÏÓ4Þéï¦Pß;F‹å7¯¾ò{IBF•¦<šŒ2/Gä+Ó@=~VñÙ´ÜìÍ+˜•Àv¥³~i1ÿÁæmrœÚ ‚fôëO’–iÉxXø¨A‚8ê¯Ø:úæülxèA° sd$:Í|¨±@öô£¯D "©C=$f•`´@”ŒŽCëß ×a’ô×Q?(ðÝh.bg3©úXqÐÇ6žÓ(åzèðƒ|¤>×,Y¯ÖBÕå\ñDÝey¥o’Ý>w͘®›h¨›vßÝ<Ùtp((÷ y­!µ„oYÞüD02÷ÞC§¶ï]ê? TÄ…0à<°49°ÓXèKYÁønMŒL¦ùµùÇÄ:”ëë¾Èø,y9Ͷ[øç2p¹ÿ:‡V:¤8‰Ã“TN_4³0•Zÿ?Ï7£¶®²ØëU\Ø:‘7¬›f IÒÚŠ\'©©XEÝ–£¢ˆÃ*½¹÷ƒ¤‡ŸWyÕÛ:Þ}°1sÍ—8¬«©pr‚<ÎN99 ó¬Òû…©nJŸ.½sT]Ž„ëš¨Šã‡‚ÉD›éjÏÁÁéÔâ=³ ,’á–IÂÄCfæÙ°ÒÙ ¯ê“Õh+Ó2@… ¶óufÑáaá× €ÇÎeL~¶j@dDJ¨P¢©‡/ÏÏôÂã‚ö†Da`ô!–úïtOXR¥·Ô²î«/MIмþÁ®ÔßX?Ëü‘|(`”–7#Œ•¤Ð$iRêÔ€ÔÕU^]J2öõ_Mäµ endstream endobj 842 0 obj << /Type /Page /Contents 843 0 R /Resources 841 0 R /MediaBox [0 0 612 792] /Parent 821 0 R /Annots [ 839 0 R 840 0 R ] >> endobj 839 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 86.899 293.438 97.684] /Subtype/Link/A<> >> endobj 840 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 68.966 292.332 79.751] /Subtype/Link/A<> >> endobj 844 0 obj << /D [842 0 R /XYZ 72 744.907 null] >> endobj 845 0 obj << /D [842 0 R /XYZ 72 695.925 null] >> endobj 846 0 obj << /D [842 0 R /XYZ 72 618.217 null] >> endobj 847 0 obj << /D [842 0 R /XYZ 72 588.329 null] >> endobj 848 0 obj << /D [842 0 R /XYZ 72 558.441 null] >> endobj 849 0 obj << /D [842 0 R /XYZ 72 528.553 null] >> endobj 850 0 obj << /D [842 0 R /XYZ 72 498.665 null] >> endobj 851 0 obj << /D [842 0 R /XYZ 72 468.777 null] >> endobj 852 0 obj << /D [842 0 R /XYZ 72 409.001 null] >> endobj 853 0 obj << /D [842 0 R /XYZ 72 349.225 null] >> endobj 854 0 obj << /D [842 0 R /XYZ 72 288.567 null] >> endobj 855 0 obj << /D [842 0 R /XYZ 72 228.672 null] >> endobj 86 0 obj << /D [842 0 R /XYZ 72 140.658 null] >> endobj 856 0 obj << /D [842 0 R /XYZ 72 69.963 null] >> endobj 841 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 862 0 obj << /Length 1324 /Filter /FlateDecode >> stream xÚíX[o›H~÷¯àª0™+—hµj“´i«VÊ&–ö!­*…‹ ¸Iúë÷ Ø츹쮴ûÃ0œû÷a°13°q29œNöß1Çð‘ïPǘ^.5\î!î3cæé]šG—Åæ²´lêzæq.S™UAçÙžž;“‰ J©ÂÖ×éÇÉÛéäû„€l-—#»F˜N.¾b#‚ùFÌ÷Œ›zUjpǃ{bœOþ˜àÆÆîŽ˜ˆu˜úÊ!F/îMž­¼"ùBÐÖ-‚f®v‹"â"R›ùx=Æ…-0î…èÀ²çæçAÌN õ”Wy˜'zæCºHdÆž!`»¿–lØ”ƒJ¡-ŸZ6oâ²’QO‹ÀfÜÜõdœTúÄ×÷*_4WÍÄ\êA˜GÍîT˜?,"L™ä 5ïFce€e>¤Ô†hú¬)™#ÐC°yWij¹2 ó 8„ QŽI1&6\„~{Žþ´|j¢7èé'éåû=ýòDi)ƒî,G˜°€aÿA¢¯Ã¹§C˜‡ù\¦q _—¨°HÏÑžrT]WOãÒOÎu(Îäby™Ä¡2,x—ÉkýæTVEÐŒÓáB”¨ªáP¨ÏÓ¥4œ¬KÉœ[à"{]Þ(ׂ…?{A‡5¿×ÆÙcQo Ø$l™E²d¹žë¸!3€m/µ;—¿Æ”&whS¨ÌC>íæ=_ûï¾®€ „!fZ~!g Å·BÑ·T–e0«=šE< AÚï¾P궤¥%´OA¤GŸ•,•‚™eÃU–t o 6%>ò8øî­¹LšhËjžº\N‚D6p<Ê“$ÎÊG×ËePÎoòâúõ, â…y:^-¸_'ç2¬çß`Û£‹nEìªèò¶ªºçâõ7§F:ÞSì'ü¼ÃRŒ³Yûð})ËjÿL–‹üX»¢äÎæ–&AYjhðõÚ¥q•>½jQ·±%0á²þ¹úžx1,ZÊìÖ‘²1û0(åˆfÂRðl1 Ô­Wõ°rÝvME…Å\€cãJ¯I2ŒÚY²7"L­ð:óeVÈæn“c{`:gMpôÂW¯®o‚Ú¼ÙX ™ë¬yK¶5|›¹Êß°}ä¤êõöJ5ë:›õð*o8¾è*FZßÒÁî ª‰6uÖWßËê€/"YïFœ;±C ½vMTÁX1¸È%좃˜éõçc¹ÚÂ#î63uIê‡E^+Ž—Õ8YŒ ÈÅaŠëòª?­›u¤R•ý€æjY¹ÝsĈSg­-9Ú²iÛ¦{öVy»AkÓ±)uÞ³Rf;gñÁ 2zx[Ë/—‰ø™¬–…JnVn³“ùˆðŽbº¨êHD[mØ»Ž„ïö¬øgHU÷€¬êëN¼;$ÉRŽ*t'.ø6"„=èŠOŸ—9v†Üõ0þÖ¡éH°ÅË.dØQè”›Ås¡ôb\KÁ&b|™˜ÿÔØ©ë#ÇïEt&«n;{?¬Ž€Ÿ1ö+ðˆ3u(ÍÛ1²%ˆcgŒ¸QÊú :‘Õx×j±qßô{åìí&à'Sø[0QG ì¼­n¢M`oÑRȪˆ×Îz¶¶¿Fùà¸á)•ÖCðÆ‡ªÏGÏ69ìïIsBáRÐO±GÌ£y°èþ¾(²l‡Ò‘“ÏOñeMN Üé™~Á‰¿Û42% endstream endobj 861 0 obj << /Type /Page /Contents 862 0 R /Resources 860 0 R /MediaBox [0 0 612 792] /Parent 821 0 R /Annots [ 857 0 R 858 0 R 859 0 R ] >> endobj 857 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [227.466 652.631 294.086 663.535] /Subtype/Link/A<> >> endobj 858 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 566.926 293.438 577.711] /Subtype/Link/A<> >> endobj 859 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 548.993 292.332 559.778] /Subtype/Link/A<> >> endobj 863 0 obj << /D [861 0 R /XYZ 72 744.907 null] >> endobj 864 0 obj << /D [861 0 R /XYZ 72 720 null] >> endobj 90 0 obj << /D [861 0 R /XYZ 72 622.742 null] >> endobj 865 0 obj << /D [861 0 R /XYZ 72 536.052 null] >> endobj 866 0 obj << /D [861 0 R /XYZ 72 507.547 null] >> endobj 867 0 obj << /D [861 0 R /XYZ 72 482.186 null] >> endobj 868 0 obj << /D [861 0 R /XYZ 72 443.939 null] >> endobj 869 0 obj << /D [861 0 R /XYZ 72 384.164 null] >> endobj 870 0 obj << /D [861 0 R /XYZ 72 324.388 null] >> endobj 871 0 obj << /D [861 0 R /XYZ 72 294.5 null] >> endobj 872 0 obj << /D [861 0 R /XYZ 72 234.724 null] >> endobj 873 0 obj << /D [861 0 R /XYZ 72 174.948 null] >> endobj 874 0 obj << /D [861 0 R /XYZ 72 97.24 null] >> endobj 860 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F64 204 0 R /F58 191 0 R /F46 173 0 R /F48 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 877 0 obj << /Length 1707 /Filter /FlateDecode >> stream xÚíZÝoÛ6÷_¡G¹¨9~ˆ"õ°‡ÙÖèÖ¥)öÐ…l3‰P[r%¹Iû×dÑQ>Œ!É0ä‰âñx¼;w¤M£³ˆF¿O^œL~úM¤QF²”§ÑÉi$dB˜Ê"•h’d":YFâ·ß×Õr>å4Þ6ÓW:>ªÛµ)Û¼-ªò¹£›•Éã:ŒÐ駓ד_O&_' £‹¹ QTE‹õäÃ'-þ:¢Dd:º°\ë(I5´«èÝä¯ õŠÒ¡ÂÉPa”É(¡B9]«¼ipiàÓ>ÎIÂRb¹6Ö¢mCjsV4­©?×&_~^›¦ÉÏ qóÓd0ŸiI•€c`Y­–EyvìE4ÇæëÖ4íÈâ,e`bÒMþH%u\rÈ%I¦tÇ“/—õT¨4úùª4Ž_dC~M„fÝ„ç#gžeÆɤôª¶e{Ì$#¶ý&™ž%ùìÙ—‹Üê{6æ|j¨t`>³ÁÑíêž3¡5‘)fQZ»I'çÄž`">Ý– ;ìq°fiÝsˆQ³t”¶rÜ[O97Ý´²…øõ"«S׿»Áâl[mýð|U-¾¸¡ŽóÜí}'Êdì£À+Sk³®Z¯'èËeü­X¸8®´Ü,dÉ7›ºº„x–<—Ž)@à*p·;™!¨ØQ/ËMîAHCP±K ŒŒ K*Bswr…!¨àˆÃIé&’CPÁñTpØ‚ RýtÞƒ -tî APQT[P Öõªñ}P±kÖ£¦ýKPñ®¬‚Xü¯ âÀ™Q?UÃV ³GÏ×ÃRÂâêCòHÅð!¯7œH<媧\õ¹ê†ZX<8ü]­y³]µÅfeîRgŠðô’ØÕàؠ6‡…$Ó·á²»ÞK·òXq, ½nG õ½r›Â)¡ú…KPÉ;ÑNî4¡Ýi‚€]n ©¼yÇro ÄhÂÇJ{.ô.±çà¿6¦†Óµ¶\™­Jìðz^”ùŽß^‹±-ý4_ð $ôœ™¿^Y­+GxµEJls7µ)¦,F(`ñ ¾Œ£¾ùóèúýý»18ië¼lrkÙ0Ü¡îÖÅd?XAÄ{o{‡óδçfWÙí^+§ÿeÿò©³+X$‡9Yˆ9@>Vdñ€élA/á:½løÈ:¾”ºµž†Ö?]dþé"³8âÖ÷­¶Þi²«þ°f& ´Û 3Ao—‡°cóP*wéˆ6ý@»{e²k…Ù¨\;ïÕ±¹ÅzŽ ³Z )rD©ëóV‘ÉàiΨ;j°Ð Tó“üPÞÛàXA{RåÚ¹q-iklìÒÔÇ.oý—L+%|ÛÉW£j¢öaÑKO«ÕªÂzä¢K­{¡?8@Þø²ë¯àH(Šw…ä€ÞÒXØ:It*ÉVVŸ‘´§ˆbâ€Ç¸Ge¡WŽŒÇÆ‚'ÅýËÏ&_|Aˆ2í½ßâv†¦(¨þùþê.з»Áí6¤_fÿgv¸üòôο²_ýˆH8aÌ?§+yàJ.d¿q£•’·:ð LãÙðꪺô{e{6WÁ×…>Æ$ûçƒAaKc%ö½ðB5’ƒ endstream endobj 876 0 obj << /Type /Page /Contents 877 0 R /Resources 875 0 R /MediaBox [0 0 612 792] /Parent 821 0 R >> endobj 878 0 obj << /D [876 0 R /XYZ 72 744.907 null] >> endobj 879 0 obj << /D [876 0 R /XYZ 72 650.272 null] >> endobj 880 0 obj << /D [876 0 R /XYZ 72 572.563 null] >> endobj 881 0 obj << /D [876 0 R /XYZ 72 508.977 null] >> endobj 882 0 obj << /D [876 0 R /XYZ 72 433.435 null] >> endobj 883 0 obj << /D [876 0 R /XYZ 72 355.726 null] >> endobj 884 0 obj << /D [876 0 R /XYZ 72 292.14 null] >> endobj 885 0 obj << /D [876 0 R /XYZ 72 134.969 null] >> endobj 886 0 obj << /D [876 0 R /XYZ 72 73.136 null] >> endobj 875 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 891 0 obj << /Length 1439 /Filter /FlateDecode >> stream xÚÕXYÛ6~÷¯Ð£Ä OE[4Í…M±u¶ÈÃ&¸í"KŽŽ8›_ß¡HÉ¢­õîfsõÁHg>ÎùIØ[yØ{6ùótòà) ¼Å ¼Ó¥R/äâ1óNïÌ?¹\Éù”b¿©¦3FþãbѬU^Ë:-òûfo®2%+eáéÛÓ“'§“v°GŒ^ŽBz‹õäì-öØáaÄâÈÛ¶Rk\3ïÕäŸ ¶ñkÀXãÅ /$a¼*_‰Òö<åÑ@˜ 4µbo°Àð#-Ðθ"ücFŠAÇŒ aþø¤Õ¯]!°__(sSªªj³ØÈÅû)Á¾ªíûWÀ7t<‰PÀˆ7£`”[sU7å”?¯Žád1"œv';í0O$G1í9tÆ(ˆ- B}R‹¦ó('(âlèQ#$†BÅaØÉ,Š4A.}ªGôÑ…‚ôŽà(€¨8Ñ™7¹9¯4—m™ÖÖUš¯²>\Ú««´ªU9@¹š _¦y¿6—DÖ²ª‹RÝ1¬':²”kæÝÈzg3±ñSç#çÔQˆDÜ»ˆÒÀ@냾Ãh´9Þ²,Ö{žu ÏŒ‘ QÌØRñ¡ Fš§u*³ôs—¥ª6E^©û6SµâO µÑÅl­UUÉ•=EuVnÓJEM0Ø¡ãêE&«j,Õ(â$èpnÚ×T¨T&'Þ•J&ï,dþï ‰b¸ï&s­Síe“Õé&Ss«¨šÛ“ŽaÀ Qݦ~>Ê ÂøÛßE®FrC€>Ò‰ÞÑ53NÅÜ»÷~+Ë) ýÕ˜£X„Xܸ&9ȃ_f1 £hØxäçE¹–™¹ï2À¬t¾ëz3+ÓUyÜ&³ÙjSvø0òWeÑlÌm±ì”îʺ×%k£l«JÕ‰ÉMg Ç;lç—u¥Éí_Þ`LU–˜ýj£i»Óú Žoü˜Sö£àC#ó:­/mê.ÍUë¯Üš\YVL©ð·]• ™ìªÚÜYHèXǶt©A–¨OÄ+ò¯ÏÑÇ¡"°vã$a!´ÄØußcr߇mºO›¯9h¯ïÈ»“;g â48Ò]À]èw!¹r¢=Ϙ?ŠÛ|eŸ7vƒÑÁA4`šgÄ+áÁÁæü™!Á„#Æêh€±-$ŠH<~è[Ø(ì@àp?ÎûéÓ²–nüجsà‘˜!&¢AÞ…ï«V¯-ûÛ—Z™vÑj:ÓŽÚw¹;'(P1®³®qhãõ²HšŽ@ɦ¾(Ê_ ‰‚²g2S¶w=‚žfú8,~56`äh?pˆ*ø¦Bëœý͹.5ÿ\VÛ¢|ÿÇj-Ó -öˆ HüÞ7ß;y¥;êðƒ±}qöµ…Hmg *šíò‘;fžícOž¼ÅÝ·3w(4- Ž¶¾÷sx—¦-}0ßµ–ksôŠÎ¢Baân„Ï)¹QÆD£¾µ¥õª}¥èÎi56Œ ÞfÒÊ$iIº’î5‹®á{FÄ™´-‡¼J'câ¨N+ò I$”Ðè\™ÚÊ]6ù Øw¬¡{ÞTÝTè¦íà%P¾ ^Ybs”q¤î e©ÖE§ ,{û˜.LÂì7¥vT1wÙn'ÿ…xòÉöIÝÉëØCÜÍBH-Þ?u!¶,Ìõ\Q}ÞZå@q ÁºCе€,]KÚazYÕ²4EÚ>¬ôgUÈ¡¼¢?`©–ý»æ¡ïòf}"6ÄÓÀv»´ÛøFÔ–ý¨­8¤¶·ý,1 6£ër×s~Rú;øîðÅì÷»Òߟ9Pßž2ï³Fþ?ÙŽ~NæÀ_ˆ¥Ð!ëGÄt!7=9 Ð«JG>6ÿ•ž—0Y4¸4;7`0pˆÿ¡P±: endstream endobj 890 0 obj << /Type /Page /Contents 891 0 R /Resources 889 0 R /MediaBox [0 0 612 792] /Parent 821 0 R /Annots [ 887 0 R 888 0 R ] >> endobj 887 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 354.355 293.438 365.14] /Subtype/Link/A<> >> endobj 888 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 336.422 292.332 347.207] /Subtype/Link/A<> >> endobj 892 0 obj << /D [890 0 R /XYZ 72 744.907 null] >> endobj 893 0 obj << /D [890 0 R /XYZ 72 666.037 null] >> endobj 894 0 obj << /D [890 0 R /XYZ 72 588.329 null] >> endobj 895 0 obj << /D [890 0 R /XYZ 72 536.698 null] >> endobj 896 0 obj << /D [890 0 R /XYZ 72 476.922 null] >> endobj 94 0 obj << /D [890 0 R /XYZ 72 408.114 null] >> endobj 897 0 obj << /D [890 0 R /XYZ 72 323.481 null] >> endobj 898 0 obj << /D [890 0 R /XYZ 72 294.976 null] >> endobj 899 0 obj << /D [890 0 R /XYZ 72 269.615 null] >> endobj 900 0 obj << /D [890 0 R /XYZ 72 189.525 null] >> endobj 901 0 obj << /D [890 0 R /XYZ 72 129.749 null] >> endobj 889 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F39 133 0 R /F46 173 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 904 0 obj << /Length 1277 /Filter /FlateDecode >> stream xÚíYKoÛ8¾ûWè(1+Š¢(ö°‹´ èb×5ÐCZŠÄ8BmÉ‘¨$í¯ß¡HÊ¢¢$v6(ÒEO|x8Î|óð(ðÖ^àý9ûc5{ý–ÄG<couá!̸ǢEœx«Ü;óÿþ¶­òóyøm3_„,ñO«¬ÝŠR¦²¨Ê½·‘6B/0 æŸWïfoV³«†Ë{,¾bó²íììsàå°ÿÎ á‰wÓQm½(N`ÜxfÿÌ#h08ŽóqàÇp€´¼âVd­J€×o£d@a”DXutŸh":$¢ˆ3fi²ªNðô[9Á/L£á€î^mÅåòõ”Sê-0FÆîв-Ae4ðS=ÜÔ…zÚåzc浘cꯋFŠÚî\µ¢‘æðzNý´(ûµòT¦¬jáˆ5áQC àÅ{‹ Ž æxÖéVÀõÃÌ;[Ð Ðz²:r^0Dy¯¢0Œµh«K1–±[ÊjâyuµiÖy¡/Y`†81¨] ÙÖJieóQG8êMø»1FQ²H7Åw‘[qš]U6âD/;kÜfb§@omEÓ¤kóŠJ^Šú¦hăRã ±™£êl“6ÍÔBáØÊ¹ë\²mP-4&¾t¸ùb„@šã)˜c&ýK?*ú–†ÇÒ¼qâvcpRz€ç$–&ÍózN˜ýöWUŠ hБžþd‚áBS8snZqLj#B¢YÍ3Ö„¯^}½I;i×Sº' £»9á`VØZpÄ’Dêá^Võ6ݸ 2˜kŒï@‰ìÒøBua|ãR8ÎqbW€ôÒ"5½èCÄ€~=:-¥¹ò2U!îZ™çB”û€¤hôd[ëz¹Èª| F„¢$> E*:Lá‘!†ÉáaÒ¸‹£S-ä“bï.;ª(äôÚ¯c£î‘Aw¯繌¢(d„\'¶Ú€»7Ú}Võ)ÛDŽ”¢<ØÖš Û(q]óMùÂÍtLžéÛ›B«/¿#ß³¦ úŸÓqÒÆûv#‹Ý>q4˽"ï8*Aä¥ä…"í‚Õ` ø½ÙÊØ½ðÓÙD“87=o6‰‚Jq:Ê&*Yûm™éD­´«Ê|û{Û(p©™òs5ïQÓTç› p×S™F3*e±n«Ö°ú–ÙR¶Ä.ët·««[4_Ä!÷qLœížÛm¥¾2µTÛJ×콨Í&óú×E¦ñ9N6ã¨fÉä-½¸V,Tá`s¬-7›ÈŠOAÚcE9k÷Ūžug69<–€Çžöû6‹÷¾lÓ²Ù—7j×ÄÜóoÒJ´Û×õƒ0F©ÿ|‰7þ•xêÄK”eÞøé™—þʼ÷Øqs÷¯ñ#͆èe4âÝlxÞ^þ!ÍüônùIº ? l¼¿á®FÂäï9´pÄWŽØ„YÕ–ò–CÂÉc-Ert‘^$b0#ºHä=üÉmÝ=hFUϾ®ÔÝ‚Üb±‘i-!V׃¾OlçÂà÷ªM¡~”ßÜöŨˆ<¶¹>Wéq¢ÿkóbŠœñ·HXa|ð§»ß.€E"Œ N`ʧ"¤QB{ŽhæÙŽ ˆ7ðÈ0dö»Go> endobj 905 0 obj << /D [903 0 R /XYZ 72 744.907 null] >> endobj 906 0 obj << /D [903 0 R /XYZ 72 720 null] >> endobj 907 0 obj << /D [903 0 R /XYZ 72 648.105 null] >> endobj 908 0 obj << /D [903 0 R /XYZ 72 596.473 null] >> endobj 909 0 obj << /D [903 0 R /XYZ 72 536.698 null] >> endobj 910 0 obj << /D [903 0 R /XYZ 72 476.922 null] >> endobj 911 0 obj << /D [903 0 R /XYZ 72 383.447 null] >> endobj 912 0 obj << /D [903 0 R /XYZ 72 323.672 null] >> endobj 913 0 obj << /D [903 0 R /XYZ 72 263.896 null] >> endobj 914 0 obj << /D [903 0 R /XYZ 72 186.187 null] >> endobj 915 0 obj << /D [903 0 R /XYZ 72 134.556 null] >> endobj 916 0 obj << /D [903 0 R /XYZ 72 74.78 null] >> endobj 902 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R /F39 133 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 922 0 obj << /Length 1507 /Filter /FlateDecode >> stream xÚÍX[“›6~÷¯àÑž ’@¶ÓÜ›LÓIw=Ó‡4“Ñ‚v̓ƒpœý÷=B`#Œ·8»ÉôÁƒ9Hçú‹x7^ཚ=]ο¤Ì‹QÌó–×' cê-SïýüÝíºL¯$˜oõÂ'\ÌŸ—Év­ŠZÖYY<²´ •+©•}Á(X|X¾™½XÎ>Ï0È U±ÕðBÕÛʈ.ô]ÖÑátþX®Z­­ÿÒ#"Œšø1ìUðáˆxñÊ‚ ‡ˆ†ŒX—!ŠbîùT ˜XÙ‘`Üæ3ä0zr ØAÜÙ¦UõEUH߉Ņ«S .ät B¸…ñrÁ ¶&|©%\àUeÒ¡,Ê.#Þ2äÒ‚ÔA"áâ‹"føœÁ[o³m!¹­WeõÓ§Àí•ÌUaéÏÊ<Ï m_~¶2ÁÆ-!Ü"„õÕ¾WR¯veõé·›µÌr””kGOXñ«!À‹9Ši«Ú¥JLÎÿ/tûf06¹Ü¬ƒF0Døž@ôÞûQÌŸ¼{=R,ï£÷6 6‰ÀˆØªýz½ÉÕAD#µ¼¶OÙ"yU)™v0žŠÖLj‡äaÌŠOr©õH¹%…&¾vÕ¦é"[z¹ˆì.§¢c°,"ûÌ}Ûlº°eñwY¤¹Õq 㙘öj»]夸ŒónMµ |~(·ƒÚhò‘ání£f!ŠÅ~A’gàÿ2M-ÛÎ!ψ#ÒÉ<­£ÆYŒìm½»‡Q8¬8Ml™6Më®ú;½Þô3д’}«ÊºLÊÜ©´k!Y»k«Õ±.»NÒcEsôƆA€íÎÒ>³NÕCësûÕn4üÎpÐ5P8U“G…pÝš5Ä\ûn#ÇxR€h89lEÃÉãy£®vFnb¸³[Ÿ=A¼3\d%× F=(¿M}kì½Q#01 ëuSf•Û•4ÑßãmŠ=ýýV¢ÚûÍ@鸊éÍKÙ¢õϲh)Ùu;GUUYÝFŸÇˆÅŽßÕW•lë1ô„‰Cñø¦çc8žŽF§ÂÐ…ãÞ[‰Ìó+*'o µ³z5HÞJém^ììºô[Q;¶ qºê ‹ï€®ƒ ªc ã¡«D˜;¿ÎŠL¯ð´ÂÝ =sbsÝÁu·ê¦5y\MÓL'eQÀ¬§ï´‡€,ܼ³•øGÙs0c·V¢²‰æ¶Å´6æå¶µNÖòl³´*Ò±,jùðm‚NNL¨ ¡®.²'›„QS×ä^ÃÜÉÙ}~ªÞ a ?;)\'ÙÎaõ¸—èMYèÿHJJP€C·hè›ñª)„8 Ä¢HÉùèöÁ!A"¸?`:_&›ËS³ª€ÇÂsÚX ¢øõÄ\ñé3ôu3øª_š=2”‡ˆ“h2Ã,…dõmc/Ìø,œž½”Á¹EOÆÑ\Îpæ-Ù çê£}dDÿ{!‚¦*RTŠ•ª2³b(‘©¥–fsË!/ej?ÛÂ@âèà| uiéõJ¶œwíÎD–`"\•y·®ûÜ¡½‘fLÕöƒÔŸ<ïSÌÔoü³/ôÆŠV©îÎ(t잯-Úàó×Ö}Y¡kY$êœcœ‰(b«“ÓÖùiðާãU€âéGI õϰRÎÚŠµñd×XLsÞW8óïÖlºçðJòŸ°É7Ò£·å!A·dÜ´(<¶’{ïËÅœ …ϹKÿ#»ª¤½~¹µ” 7G`Ä¿R¼×- endstream endobj 921 0 obj << /Type /Page /Contents 922 0 R /Resources 920 0 R /MediaBox [0 0 612 792] /Parent 917 0 R /Annots [ 918 0 R 919 0 R ] >> endobj 918 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 603.247 293.438 614.031] /Subtype/Link/A<> >> endobj 919 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 585.314 292.332 596.098] /Subtype/Link/A<> >> endobj 923 0 obj << /D [921 0 R /XYZ 72 744.907 null] >> endobj 98 0 obj << /D [921 0 R /XYZ 72 657.006 null] >> endobj 924 0 obj << /D [921 0 R /XYZ 72 572.373 null] >> endobj 925 0 obj << /D [921 0 R /XYZ 72 543.868 null] >> endobj 926 0 obj << /D [921 0 R /XYZ 72 518.506 null] >> endobj 927 0 obj << /D [921 0 R /XYZ 72 472.115 null] >> endobj 928 0 obj << /D [921 0 R /XYZ 72 394.407 null] >> endobj 929 0 obj << /D [921 0 R /XYZ 72 334.631 null] >> endobj 930 0 obj << /D [921 0 R /XYZ 72 306.8 null] >> endobj 931 0 obj << /D [921 0 R /XYZ 72 274.855 null] >> endobj 932 0 obj << /D [921 0 R /XYZ 72 215.079 null] >> endobj 933 0 obj << /D [921 0 R /XYZ 72 187.249 null] >> endobj 934 0 obj << /D [921 0 R /XYZ 72 127.473 null] >> endobj 920 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F39 133 0 R /F46 173 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 937 0 obj << /Length 1202 /Filter /FlateDecode >> stream xÚíYKsÛ6¾ëWðHe"âuè!M›ÎdƦR§‡4ãAHÈÖ„&Š­ß%AÑ$EËò#MÝú ¡‹ÝÅ~ßG88 pðËäÇÅäÕ[&´ "X,Æ#D¤d¤P¤Y°H‚áoÛ‹<ù4¥8ÜÓ•*ü)76+M¹Ê³—¾íw›ZSXCž~\¼›ü¼˜|™˜ $ìFHbÄ“q@û»#¦UpY÷º"¡àšóÉû nÅ=‡uÇaÂ9RŠ’`„™ôÿE)¯§ß{õ6êF ± êÎnJTh¿llQVcöCŠÓ]_0, @ŽÃŹõ\3´¾)s=7Y’Úž Á,¢ˆQ̈DšÑ{x§+Èùˆ“”#ÉØ'M’8[þ&_6Îîž^Ûí¸!¢Î3ÍQÁŒ2¤ ñÓÖ}µî4NóÂzŸ"Õ" øDZŸ0Çð#ƒIzQÌ"‰ AiÎýÀ7&M?™ø³÷t™;ÿ§(óõz• "q›,k[+§„‡ÖÊð,HѨY¶Ë´líE)ŠˆØµ®q±)O*¶YŒü¨^ö¬§í2žÔƒþHÖózØÈ4J#:¹óx·GZʶ4ò¬´B¯FëC!.Ûúx9b,BZµ‹µtUÚÌ…u?üšgvÄ °„¤Ñm[ج¨¯U¹í˜ëÅË%ëÄ{¸N˜Ðˆƒõ^¡¼ö‹Þcªº4œ5‰MüÝ&Y7µ‘CIÚr¬V`Ö^9ü9UÐz2®²sëVÕ0FB€¹oÍ«Á…47‰ìë’ ¦c½éêŽEîÛËsÓX¾lFÆ&ó Õ »<ÝõÛ=öÐmf«B-üSìì¤i·¥â¦*?~ú&ŠÆ)@NÚ4䣓k_mðøªIß*+J“Å"‘AR×.“N]—{÷aÀŽ€ºær&oær€ähtévÈìšn-WBu †cx 2›Ù¸l9Ìø  †òð²G×òîM?¼øŽ($€—=•7ú^•¦qøÒºbÔØPuA—X9Ÿ€æ²§«¹Ï’û’ z»2éªK°D\‹»ðͲKñ Ñ_÷8RwùñG„·o¯'¼cö`À¢”È‹Ÿ/MM¶gckÆbòx‚¤²â](u$\žåü)Êy ÈS` ;Ž.)aã§îDN°µƒ­è¿H6C5ï!FêûÈæý?¨&ûTSí©&¼DßOç¥qå">°eH(yÏèj# ªÔwÛ5b(Ó£»Æeå¸ÌÝ¶Ž¢JTS™YÒ®]eÊx=.V7¬ë-LCÁ½á·–»s •ku÷MýW›«x&’Ñ Ÿ´y˜·x3ԘĔÀáö¨ÂÂ=»²óœ#©}Ï_7 š¯«ï™&mT°6´Üî*Ámâr3ðwˆQnóªÈ:üóŒüJô_Ǩx<Œ¶oŠß£Qö ÑoÑá졨8úbÿˆLÀë! ©RDñšaóÇx[ùè7AwM Ø(Òî[‘ô§(‹©Àáåª(ë 4Ì«ñPgç®Z¼<Ëw2'ƒšyµŽ_ë«ÄB‡RÓò7 es endstream endobj 936 0 obj << /Type /Page /Contents 937 0 R /Resources 935 0 R /MediaBox [0 0 612 792] /Parent 917 0 R >> endobj 938 0 obj << /D [936 0 R /XYZ 72 744.907 null] >> endobj 939 0 obj << /D [936 0 R /XYZ 72 686.008 null] >> endobj 940 0 obj << /D [936 0 R /XYZ 72 654.082 null] >> endobj 941 0 obj << /D [936 0 R /XYZ 72 596.364 null] >> endobj 942 0 obj << /D [936 0 R /XYZ 72 500.702 null] >> endobj 943 0 obj << /D [936 0 R /XYZ 72 468.777 null] >> endobj 944 0 obj << /D [936 0 R /XYZ 72 401.27 null] >> endobj 945 0 obj << /D [936 0 R /XYZ 72 305.609 null] >> endobj 946 0 obj << /D [936 0 R /XYZ 72 273.684 null] >> endobj 947 0 obj << /D [936 0 R /XYZ 72 178.043 null] >> endobj 948 0 obj << /D [936 0 R /XYZ 72 82.401 null] >> endobj 935 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F46 173 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 954 0 obj << /Length 1598 /Filter /FlateDecode >> stream xÚíY[“›6~÷¯à­8+º!A§í4Ù6i2ÙÌ6ë>m2;Z`×L0¸€ãøß÷ cXìÅÙNÛÌô ,KGçò+عs°ójòb>yö’ '@ Â™ß:’:’ûˆÌ™GΕ{±]æÑÍ”bw]NgTúî/y¸^ÆY¥ª$Ïžšµ÷q«26?ÂÓó7“_ç“?'îÁ1t9’X:árrõ;¬¿q0bïlô®¥Ã…ÏÔ¹œü>Á–G¼Ï+÷{¼Œ0“†×•æu]¢2.>Ç*·YˆjVž½|ï¡òhTõ±ËJÕe\$*½ÔÍ‘ÎU$ÈîÈìa³ËÛßå¡@úÍž0Ϫ¸VÜ—êÇwy›ý,Øß/£»O(rø¤ÙD ö¤Ú$'‘à|4¹'O>mT1eÒ½+Df>bRìIL´Usôîž1‰µRg„ Àó̩瀻·S‚]Vy±5 Unže­zóª²È¼ëÌ®˜Ç:Z™—ëÃ`¬)ñ\c°{xiåû§À‘ÎŒ2xZŒkö µŒ«¸(‰õ¤¥á –U¢€YOù@©×ÓPçbp*ÑÇ—jÀˆŒ!.wF¢ÂH;_Äæå|§†ËÎ4Öàõ‹Õf¤*U‚¾ãGÎŒƒôœ>‚ñƒCœ{ˆpÿ>çÏ­=óU1Tj~B·ÛÅ:¬Ö=~1"@#*X(q øãÞâûW&ŠŽTÑ€„°7บ'Š(ÆÊ A(Ù»„Æ»°`㎪UP‡+‚%¢­‚Ф ™ó©Àî&)«82 Ï5 €å¢¨mgy~Ï{á@0ÁÆÏ=7èyþŒp þLø ˆœš ¶N-®ÔºZäÅ÷Ój¯T[³åišdÖç~0w€kÍpP0hÆ÷ºú‹ .¸ãF•‹M^|úùn©’…ù²‹É+÷§z~tPy‡5\þ¼}5 u(Ô¦éÀ³}ÁZ$¢Çbѹšyüëâõ@f~ ß-l€mH–gèõr•ÆíúÖüÖ<Õ(ìèSs$lF SUe+J'»lÕ¯ÔÁR€äáݹs}j®.мÊÃ<=šò &žMy¢§¢²“ÅŽ$/-¢+«ºî½žG¡»T’_y›—Õ€j|†„/F”.²Ù£‹(è ŠúšHz£Ë‚€C-)ºUÁ™JÓ~²ÅAnU°YÄÝ̦ èÑ&´¤´‚–§d{â#ÁˆÍö§{ëQÅ”øûòwDò99’  ÛßM=ϵÆ. ­®¸­DIvwÌÞ³º^%u¹±W³´V?WQ<ÊêÚJ$Eðx[íªÖßåU\7 Œ»e’…µÊ ^ëŽwÕø^îÖ°ª,Ë+ó~cϪ0ŒËRG›úH1\“²4qæõu’%Õõµeb·k'm•kþ[Zkaö”“XûÖ÷C1ª²åL¾¶zjn³A †4¡£ÆnêÓ}¥ÕÕÜû8Œ“Ï&2ôû °#§8xMp¨Ë‘H>Ú·%ƒØéÃK “Mܸˆ1¥žk¢_Sû×?¶méúÕ.>ÖÃ[tD’âTñm}Îðî7MSÓ ÇJj¨Æ0òý^`ú'rÛQ'·=¾Í­» .ÔIäy⡆”6n‹`µâ`‡Ë‘¤ü1 sG\Ý0³ZÜz0âw?2ßïºØ¿!ñïE…;ù‘Èx 9ÅœCG6Zñ*Ч(P/ñ`´¾ ÄBÎÿ0ßT€1#´—6k`€@Öèf«o>ÄŒO©,€&Õã]È¿X'iÔÄ cÃn5¦Æ Èúác¾hÊ•–v·ÈӨ쭰ÚÑ’ù;oö+ פyZJ+p‘¦¬ÑW›¸ñÚ¶Ò*OhyV#º³]µ§H•&Ê2]«`¿î~ë‘âP0›iAoQ7ÆÝé(@ŸÓq}c;~ƒ„èÚáDw“±5\ypf=ºïYxõÁqé¿åÔ—ˆ Ò5èoqºjpiŠëƒóåêþüÔŽ'õ¨ÍD᳋££åþ ˆTŒþ 1ø±…SDˆ…µôáv€qÏjUiÁ¤ïR4 J>żMn e¦)[;7|xBüÆot3 endstream endobj 953 0 obj << /Type /Page /Contents 954 0 R /Resources 952 0 R /MediaBox [0 0 612 792] /Parent 917 0 R /Annots [ 949 0 R 950 0 R 951 0 R ] >> endobj 949 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 567.381 293.438 578.166] /Subtype/Link/A<> >> endobj 950 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 549.448 292.332 560.233] /Subtype/Link/A<> >> endobj 951 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.369 107.109 252.981 117.39] /Subtype /Link /A << /S /GoTo /D (section*.362) >> >> endobj 955 0 obj << /D [953 0 R /XYZ 72 744.907 null] >> endobj 102 0 obj << /D [953 0 R /XYZ 72 621.14 null] >> endobj 956 0 obj << /D [953 0 R /XYZ 72 536.507 null] >> endobj 957 0 obj << /D [953 0 R /XYZ 72 508.002 null] >> endobj 958 0 obj << /D [953 0 R /XYZ 72 482.641 null] >> endobj 959 0 obj << /D [953 0 R /XYZ 72 454.183 null] >> endobj 960 0 obj << /D [953 0 R /XYZ 72 394.407 null] >> endobj 961 0 obj << /D [953 0 R /XYZ 72 334.631 null] >> endobj 962 0 obj << /D [953 0 R /XYZ 72 274.855 null] >> endobj 963 0 obj << /D [953 0 R /XYZ 72 244.967 null] >> endobj 964 0 obj << /D [953 0 R /XYZ 72 185.191 null] >> endobj 965 0 obj << /D [953 0 R /XYZ 72 137.371 null] >> endobj 966 0 obj << /D [953 0 R /XYZ 72 108.105 null] >> endobj 952 0 obj << /Font << /F36 131 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R /F39 133 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 971 0 obj << /Length 1979 /Filter /FlateDecode >> stream xÚíY[oÛÆ~ׯ úR*µ¶Ü+I£ §Mœ)ÒZçÉ 5¹²S¢JRõÑ¿?³R\ŠV¬ÅôÀ½ÎÎÎå›™UÜQðnr5Ÿ|ÿ–Š E© "˜/ÊÂqÄ,A,¥Á<nÃûu™ßMIîêéŒÄIøS™íÖjÓÈfUn.ìØïªP²V¶ƒQ4ý4ÿeòó|òçÃaQ€ƒ˜]†â(²õäöSä0þK!š&Á£Yµ˜Hà[7“ß&‘cô©¯¹ë_ã%„1ŽPDcw)ŽBYɵjTUƆ”@i ‰Q”¦Á Ç(¥ÄRùƒîí 3¬›ÅY¹iÔ¿½~@šRÄbÖ®¢DÆ£p¾T¶Q«ê¯)桪l?—´-CTOYÂ#ÁŒa”2âø¥_Áï*u®û1†9Â,9ƒaK«ÙÛ^SÚï®vËÿˆx”«…ÑÉ®hì Zo›=Ìà!ÇI“†ûq`nF(J±ehk¬sW#͆ª¬÷› Ù{Ö7 ŠÛs¹idÕü+ßÞ˜mv½wZB‘HDwoàÚ.âýE¥±¯tí'£Š' âàníÅ1†Òûiö/-7j„=#ÁX½¡è¼Ãg$‰AßXË¥\Ø]תضZçX–¹¯³ZËÈ µÚþÐÇè¿Öò¶Mæ‘EŒ9ÛI€3{Êý›žKÿï¹ÿ«ž n»’œ΋#ŒNŸá½ñÞKOy/æˆbqÂ}=zÆ}¿L¯ãnQiE#T/È Yƒ÷á(‰ÂÏÛýLoÖ"(a©ópn·"7j,…M-3¶ÑÁ‘_×ÙjõvÊyØòí«žqL '!lÖ®ñ¸xñâáQVS‡÷õˆ²h‚h,žeœ$¥(N’’‘ô€d$±6 c-’AÓ"4<$ƒ~‹d°ÍZ—ÛÚwè_n5 Ùvϯ/a$MomKZ0ð7SlÝùÒ(5òñúr\Ó혀öB#n7¢¸‡vôœ#†&fé7B4!"¨Î!HU0q4øû;›­b†(Äš„L†X .Û#ˆñ°sÆ9€?½sH£Kšªó!ç#O)°r ÝØ¦Äó)xko«|¦Pi •E¡Œó¹ {a+Ûñpß3Î!*†‹@´ds0 JØNKr®Ñ*ö,ÔÆŽ¿óWѰg ¢Ñ 1p> 8š‘Öp¤gÜÉzùXV?Þ¯åª@Y¹öÃÜmøª³ç^ »Q­,þû¼}µ9œ3ª‡ÛxÏþ›–ÜÎxIÖÇ÷#•×)”ŒŒsPáX ±/ìráüسHi)—Ê»¦Í\}*(ÕF§c”i°C|ì‚ƒÑØO8â¤s¾fÓM™=¨ÆÂÉhð‡d#=+uÏUVæ£Ä(0œº U×òÞ­_¹Y¹±“®¦”ƒaÙÑ¥’¹Åv>.W–ôaÓÎ eKfä v`¾oÜúbŠÃ¢œ>Öv¨åÓÆ7©WÅÞ‹wNpµÒA©Qm…bÎrB”î»i9ò&fNp àP¶Nä½BàH+ž$…t)MN>Eèð"p÷Ñ'"d:1á“ï°,_@ag‰³w<âDÉ{é ÍÖ1Ê‘®T°¥}úå$R"@Ç8M pè:1]¾q;qðóáJÛ‰€Öµ³3üÉŽÝÚÏÛÝÆáˆî½wòç2 0ûZ]G ë´;KÏ5«Ü-rÛá†ûféíºE†4Ð…+xäÉØe+¼K ¡!D¿ñëK©-S˜ºDœCSLj†g–6“Òã¯ì'²)læ8õ™K^t·»è_ëóÅàB/íx—5h·ÙÊìAŸúÍ«ëëë«oÜò>}Ù:¦+õçNÕGÑm¹.c;ðݱxgöƒ?j¬óýÕÛ/»´Äf­` "d‰‹™/zêƒiáNê“y9;iáYŸsoÚdß}b¨!HròT“øÎb5-íWfY¹Û8±.t¶ffÛm‹wûF=M_+N)Ê¿gŽÇ%ø,Ä… />Šê sn^ËŒàdžÏKÔFÞ­ j‹ù9±ÙØ™Ž—÷&hŽÅhȺøóßÂâÑAü:Ï]ý‚æc_óŒ%¡·!“œ¼m„äm¾SZµÝD–w¿Œƒ÷cCû0±Lran„HP·»­"%›}_+gà—^uú}«º}SwjÆÖé>N|¿ä¡`H}ҿتâñÆè?ã¨X²4ÇÎÕYaÊ2ˆ©³Ìä*ç•*š•³ÀÙ[«ý?5öÖV+Û¼Uºokân{¢–kü”û¶Ôêú¢€m_÷ÕÙ $7‘•2 Tv£ôKÛmâ¢Ýp2‚v#Á‰[^µ|Ý6»^ŒãõBÄþ,všëüëBÙ*fÔ¨»sF…’ñh8½w/XÐWÕ3[¦ŠŸõL*Y"'Ú•uË­=äÏ}“r–†ü"çD’eæ8Ì…jJÑ•HŒ.èwÛ¦¯VH+šZ—uO#C,C»j6eLwÛ²#QRv*¯5’ì©ÒÍë;ëHÃ\ãHŽÝö¬Rª¦ÙxR0dU(ë ’÷[“ZÀv°{{+*¥O‚ mhÉgÔ Ý´„§uc¼vp“òãtwäJÖN—Îõ6$q°>Ë8öÙ¿¼‹0öŸÁ æ‘$ößaÿƈÍ+‡·1úPâ_ÀÌoU×Ws©ò\’%oHµCÊhaê¿$Nñ›³¬ ˜ˆù4\>‘Ê݉÷N“i‹: —z{®ۼ†ŒÕG·WEùGˆû[Øoye½nÚ-”`¶a_®ÈzhõCËf°®ßQWéâ™­ÿ@V‡XËÆ[?ÙÌ,€¼y.±O3ø µÈ³ü é*Ñ4¨ÚÆ$ºuÑìàø¿XÆqÈD±——K('¶A®L5‰éÝŒ”ˆÉl@â"¯ª‡¼0Ñò8_ ¦âí¥ O'µà8Aj´©ØÇBŒ´ã|[(iJ€Ð Dàæ¾ÎQF7†]ŽÐÌGëA‡Š3àaq[î'~¾Z‘ÈR“膾 îÃá>%‚¢o[UÓÄèwÀiçdÖ”1öª5§†0 —2÷Ò¸„U4[@y‚y¹ž$‡ØeX+8¥eÚÂÁØ€ÛCÚPØ£¢ÖÍ U¬W(7±¯WtÙ9`J*y;9R¹K(@Â"¢ìP×dp…ð·°F¥¨ƒÐb´¢;”€<©ê9«êˆêØú?ßà‡ª·“îû «³–jX=¹vèÒÕ7eÝéa•†² gó!¦&a骸“úx5»¾K¹û¾Û:—ÙÐ¥ƒ÷è걺8‚ªc>zc¼´¹Hüílü:€¾p¶AHòi-ÈŸàÁda˜’ÈWðÊ€&?ÅgAãYQ Vš;‘œzó"=Ç<×.Oæ>ò²²ón<Þr1wÃðà:?å“tRTy×Í×j‰½}áè;f{n5b8k’±lÒÀýg;çV÷¶Žoç ¨™’‹Þ\Õ:“X¸]¾9±À~1)³iùóóI™s»HÀÿ¥ªðx¯1·¶©*Õ~kážùk76nœ>ìðÀØz"öäÜ뎀dšðá…g,$a<àÉâ(:'$0îËI2žk÷œ2]'„(­cŒbœM’[å­gE¿„ öi –y ÆK Y˜x"‚*8¤p¼_,¨µ~Ó¹©Mó÷¼¤ÖgüÜX­Z‹a–H3n†sbz×#Ž?ßÛ›ÉxÆõíõ‘À4~0‡ê¸¦¥}¶XΡ¼K T db‰š†,Fæb›Þ-ÃÚüa±Œ#jHCù¡a®q2q>|^ ¨NrÆ#f‚d„Q|IPN|:}àÂçF‡JpÂÊßd[AªoÕÚäh'g@Ä|51Útj˜0ÛÚ©_úŸ?Æ“ $íJ[ˆ4Ý<¶RóOÙ嵩5žhv€ tÂCHé¬2%©G¥ÐJŽ#1úвŒ¤Ì”"ª®1‘¤»fbu’ 0¦(m"úŸQJá`Žc›Kí¢@´Tn…ÆqQš¥‘~´ˆû¦¬5­} ñ;À5SÚŽnËÍÆÞÂpª%ª¶mˆD%ìN¤GK¨ž5sö{•:tsöëmÓo†Ë¡{0pW„ªÔº"d·2¯MAÈG©Ð=´ŒŸJ(QíÍr(§No®k€i|Ê€ù"´æiÆx˜~Zs¸ïÀi8BÍ£¿H¿ ­GëŒÑš$ÿÿК'Ö!÷>TMñ¸lê¥)µíÞšâ+ær •XˆȃÃ]VØ`p¥ªüižÑ´_æ ½“˜†ÏnŽ›žä¾ N/wÖLÔ€Œà¼Œ‹á”óˆe©<¹HL"›¢>¨NÎUîŽW§6wS9>Îå=ŸôÐM§s«µ'“†WwöŽRZó„ôí¤þ:ý¥+Ž˜HÞüC×ìp‘`œÓÅ! `edÜ¿Þæ{mq î¡@$bæ'ºÿ”m\`îR>|ú8óëÝ©Êýâ endstream endobj 980 0 obj << /Type /Page /Contents 981 0 R /Resources 979 0 R /MediaBox [0 0 612 792] /Parent 917 0 R >> endobj 982 0 obj << /D [980 0 R /XYZ 72 744.907 null] >> endobj 983 0 obj << /D [980 0 R /XYZ 72 720 null] >> endobj 984 0 obj << /D [980 0 R /XYZ 72 666.037 null] >> endobj 985 0 obj << /D [980 0 R /XYZ 72 638.207 null] >> endobj 986 0 obj << /D [980 0 R /XYZ 72 578.411 null] >> endobj 987 0 obj << /D [980 0 R /XYZ 72 504.643 null] >> endobj 988 0 obj << /D [980 0 R /XYZ 72 444.867 null] >> endobj 989 0 obj << /D [980 0 R /XYZ 72 291.487 null] >> endobj 979 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R /F39 133 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 992 0 obj << /Length 1950 /Filter /FlateDecode >> stream xÚÍYK“Û6¾Ï¯àm¥Z &‚¹Ø‰½ÎÉ;ÑÍ›ÊbHHb E*|dvþý6^Aqdw]ɸÊ$@°Ñ¯¿nPq´âèãÝ»íÝÛ$‹Y’EÛ]DhŠ0K9J‰¶EôeõùùØë$^ Ýz“0¾ú±É‡£ª{Ù—MýÆÎÝ«JÉNÙFñú×íÏw?mï~¿Ã°Yáˆ% 7E,fQ~¼ûòk0ÿs#"xôdV£4ãp­¢_îþy;Eq¤ÿµûÈÞÜœHÅ1E‚‘ˆaŠ8ήŠNR°â§B2°;‰¨aôª~° ”eNÆ¥jוÍaxº~ ÃYê%·*Ú-Šöþ˜_M Sq0Ä@ q,D–ÁE`jã¸ùŸÿtL#Š68„üNÅzƒ“8†ÀÓ|½Éàî_11·÷eßÁÃ"C,"óEz“ÍdþÕ׊;Í0?D¬Bí©6Šà,Ëgµ˜8”‘r-#Iõê¹-˜ ü "%̶³8b"vî$o‹0ÆVñ=°`%×ô <%¤Òª•0zMÀÿ)N3©Ønùî¹Wöî{ézÙöööïö2S0’mÙ?S]ßœiO^^Ѐú­Æ»Fª¡=S¨Jº:•7ua„ÝÄcR#Fè7ÐÒ%?j réü¸H3À/çz!2$€Î)cÈÌhÉ¢Ø6ZyTÚhÃKç¨@ŒQ6ºÓ.¢Á"P˜û5GÕurãÕ^ÿ·$`Ni:‘i(dÔJœ˜¬Þ€®0±Á\êTÞJ]Ù(„ëÐ UagvMëfûƒ») ÂEYïíèéPVîAÕht™µ½‚kf/·*Wå:¡«?Ö˜®ü>…ìeøæø‚­Ã;ýÊNµvî ë¢R(°z~ŸM =æ(#8Ú$êƒ+óÚ¿RÇ­Wm‹¾l(`Ù„bïB8U`”ö|’$³šm½ÚǦëÏ×îþ$óGÕ~±dLã4ÐVÈ:W/áŒaH<ÅÙWq‘ èR W2y,!„DU£ãcƒÔÚ}“mkLÒƒV¨Ð÷éè/óÀbLßɪjtüžÜxpWk}í^½éd£t%wý»ÒÕ“[8VÖÈl“8µMR<K¡¦õ PËZ–}¸h®—eíòHÖöªÚDPºB Dú“[-«®ñàíB¨·ªSý õ£µz`\ì^V²³à*030œ!AkøÃPVÅg¢á G™&MòøŠÜÌWªxÆÂh½o•ì•÷µ÷›,žCWvª.|þM:ø—3ï/Ƨæ4T`há-ü}P]ÿrjêN-ûZÉ*¼½X}5X„!NyH"ïõ.Yv3`×Jgä.‘]ŽÁ5i t!ËJ>hš7‰t¯ú¡uI¶]ºjîñ¤üÕ—¬nÈuŽï† Ýž&î¸WýKnJÁæäU^J cbzéãEÒ¿è›]Û¯—ÅoÅ´C…q®Þ¼î®ÙAÂéhùˆÚ‰¦çBî‰ôß»êzèÎ3 ´ìŒßïMv·Bç1Ê~2añÃ&-Ìi’/ëÁ=1»¯š}™ÛE®›bÄ0sTÒ7sÙ Sî=×\éŵ}"íÐ7R0ã)¿†ù¸Ã£IóÓ§ƒþ1­nÎ<ÏÕȬ .8µ-Ìó«ÛY^v}YUó —õuXš:ø§`s‘!lÅÈèêÐb¡2»e6û©ìÔ+ëYû¶8_b*ZƳWa6˜g" ª5ê¢äÍ#l{„n¡ns¦uvj e±P¢pé5ºñÍ‚82ñQH¥ê%!)ÌÆ:"ëbA œ´9c~MÞ® [åË ‘só€ìw )M1Ûj8¤_hL e+Ë©R·rK.`ï¹—éTµÓëÑo¿y$cî v¦ÕŠñ©‘fï'8tÛF…i=¤-k8He_B§ÚÔ~b¸àh—R–.¨t˜à+T… ó³6„×y‚Se•x:ç4uõlŸhÄwhR<«â¶WƒïºvžÑ=¾fœ!=cÔÍØóNZ´ ª(Ê\M•­ÉH4²Ø$¢ï”e'<‘>%)ÏMçxŽ4ÕÊÒg¶ïÎ?Õ…ÅÃO¦M÷äp„$¦(fâq‘ð°^ÅÑ)Ï>_«Æ¡UÝ ûÃWZý‹‰Ð?žxîU7TýMÄóB7?&¢É×Ä% ¢1½¹‘1J(QõÙ©ÜÍÐ0ï×[«Ãôг±éT[ÊjÞê;YE3‹Ap6ôÁóå!kLܦ¾8vþ x*Uñý¿F´s“ÐÎæ1ðØµ£Ex†¸õCtÆ$ Oîàü©Î Õý‹'I M/MÄkÀg‹…ÀÃéÍ5 ¯z¹ñãòi”%·ŸFq–¡8f³J> endobj 993 0 obj << /D [991 0 R /XYZ 72 744.907 null] >> endobj 994 0 obj << /D [991 0 R /XYZ 72 595.558 null] >> endobj 995 0 obj << /D [991 0 R /XYZ 72 533.626 null] >> endobj 996 0 obj << /D [991 0 R /XYZ 72 491.783 null] >> endobj 997 0 obj << /D [991 0 R /XYZ 72 432.007 null] >> endobj 998 0 obj << /D [991 0 R /XYZ 72 404.176 null] >> endobj 999 0 obj << /D [991 0 R /XYZ 72 344.38 null] >> endobj 1000 0 obj << /D [991 0 R /XYZ 72 270.612 null] >> endobj 1001 0 obj << /D [991 0 R /XYZ 72 192.903 null] >> endobj 1002 0 obj << /D [991 0 R /XYZ 72 115.195 null] >> endobj 990 0 obj << /Font << /F36 131 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R /F39 133 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1006 0 obj << /Length 2019 /Filter /FlateDecode >> stream xÚÍ]oÛ6ð=¿Bo•·™I‰’öÖekÑ¡º4{J Œ‘èX¨,y¢Ô¬ÿ~G)K¶â&Ý€ Bòt<ïûè(¸ ¢àÕÅ×Ï_rä$L×› eAg$Îyp]7áÛÏ»¶¼]±(ôjÍÒ,ü±-†jzÙWmó®T­¤V¸ $Z}¸þùâ§ë‹?/(œéÆ$Ò Ø]Ü|ˆ‚à?áyÜ[¬]‹ Æ:xwñÛE4ã1ŸðHiF2)HÄSdôz[YiØË+…Ê.£°j,qhÔŠ%á=.:õç tˆ{YàÆÞÞJ„²,ɪw¤[7nî)†®Y ô@׺ï”ÜybMéŽQݦívŽò¦“»ª¹ÃEÛøãÈjÍS—’½‘%bM)É“/[iK5 ‹­*>ZR,ª8)ÚݾV½ÂÕNi-ï”vª’+A‰ŸÚ¦P~ïД㾪k„î»¶€Ý¸5——=‚ÔŠ&á_•FvŨnẵr '-`ÌÙ ï·ªYºØ½ùžD YâìW¾Åâ`ozX>Ÿ¡ú ãÊòjFúj玨ݧ´GŒ\¥$çÜÛ–Ûã¢Ü.Ô)LJU´¥‡ßù½ƒÞzkGfô„ î­1»Ú Ma¼kŽ|ÐÆôh­šÒ2=:Ìñøüe<õppÁi°fœäÔ9¹±Z –FÓéEbÇ.!‚Ï…ôž±d¶õè\Ø!rø`‘KÙKƒ|D–Q©Ç‚ï8Êÿà¼I4w7£Gsd4ÍÿŸ£^Nyå@“¦g˜]֢ї·q=Ô½?ŸáiMyDÀ÷fZ+j©52gS!2SáÛÛ>hÒw²ÑÒrDp—ˆ§e IØxñ_즺¨ª—Æ0º…ƒhD‰ ã–÷Q!V2ÅJHžŽbBGY"ƨ~J‹ d&ü5 ¢ŸG_¦© „úâÝåë×8µ—ÀéeÛô][תûÞAó×Ý8¹z5Ï\427àA"8áÙùôÅbàŽ)lJD 90Î@ÊY|ŽH¤)Qìhœrw ™ŸÃÁN&瘆1 êv”;lI?Bluep= cuŸ(’$wqâ¿Y­E…ïzÙõ8ýpó¢,;´p‰ãËÑ3¦ÐÿÎ o®.瀟ŒïX€ ÓÿS& äÈ-Vë8 “$žN~… „ œ2kG|+Î\9ñÍäJ’t¶,…tÜß*#Ö8°¿XÂæVbLÏ¡ <-[:s9ñ|~y’pÙ,võ Lšƒ `å*J³H°˜ân\jƒ)f¿)’6FæŠr˜ém;Ôî¤[G Í!`@ꈶÍרŸÀê~[ÕîCݶ{‡Û"%9½Ä‚†;U¨ÊpöÉ\ÍWÂhN‹5ñÉe†}Å××»_.xƒp’±ˆ_¨ûr Oœ©ûv­î}…W`s8¯VÏØ2zŒD4žz²ü$¡S{ȦS ±ŒNmú‹6ç$3‡Lõóîcå9[£ ÔR‡ô~˜Œý®YXÃ3^OLÔ|p fP>·¶dwëÁFÙfÔO>trPÊMí,nˆmóhP¶ÒXÒZŒ5ó.®êçMeiŒšò±YÛúÒcPAB¸[1!Ɖ$ M7 š~í#X­[o½znëPùîåÐ ŽO ¦‡8”­[hVçnpÖ •2íÉ,4ÞU]¾CD#<²#‘rοù£cc%¶HçÚºì”ìÇnúð<ðù¡¾jwÔ÷‰?€ú±í7Ñeþ —ɸ·¢ç ³}Ûhõ¿ŽA'ÌžÊê\»éè£ñ0<åÊT—Ƨ}N‡<'4fžµ‘!ÕL^H Óœ@ñ7š}ïz|rÿ¢ÁAŸ}Ï Îœ±øÀsä©Í¬PÙx®þÁCÌ×ÉxÅ“°ü³–³bÿ’¦‡Â»Í`*9Û ú“Ü÷®ÓÝWZ=UwªH1H•=I âJtÔÍ¿: KwÓvw¾zø/ô2>=”;>ÛüñìÑ‚÷o¿VìW6þûÕ€˜;Aê,JähQ°ËH€™LX5ƒû2z Ìëö®*É? cãf ;%÷ênc€nÝ>Wú‡{ø"qé‹O€¸âs|Ü÷j‡O“‚Õ¼µo¥^,|$¬OlÌ¿PÙÅǦ<8 ã3Þß\zÐ=>›Ï”<¶4X¥-þ7!³ªH§DŠã_y’˜0ñèy€Š¡ÔeŒÁá,Ê(X¤ÜÛçóƒK0¶ðóԛ궓€éå3B^¼}½ðËÕñ%þîb±K endstream endobj 1005 0 obj << /Type /Page /Contents 1006 0 R /Resources 1004 0 R /MediaBox [0 0 612 792] /Parent 1003 0 R >> endobj 1007 0 obj << /D [1005 0 R /XYZ 72 744.907 null] >> endobj 1008 0 obj << /D [1005 0 R /XYZ 72 602.321 null] >> endobj 1009 0 obj << /D [1005 0 R /XYZ 72 453.141 null] >> endobj 1010 0 obj << /D [1005 0 R /XYZ 72 381.41 null] >> endobj 1011 0 obj << /D [1005 0 R /XYZ 72 339.567 null] >> endobj 1012 0 obj << /D [1005 0 R /XYZ 72 261.858 null] >> endobj 1013 0 obj << /D [1005 0 R /XYZ 72 202.8 null] >> endobj 1014 0 obj << /D [1005 0 R /XYZ 72 144.344 null] >> endobj 1004 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F46 173 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1017 0 obj << /Length 2212 /Filter /FlateDecode >> stream xÚÍËrÜ6ò®¯àÍœ]L<øÚÛÚ‰³NUR^Y9)>@$FâšCNøˆ¢rùß·8‡IÙJÕÖð ÐÝèw÷DÁm?\¼½ºxóž'ANò„%ÁÕ.à± 4̓TdDä<¸*ƒëðãþ-o6, Ç~³ei~×ã^5ƒª¶y{—ªV²W¸ $Ú|¾úñâû«‹ß.( ‹¤ à ’FiPì/®?GA û?áyÜ›Sû@$Œuðéâß‘%4šœˆÁyBr€—ÒˆD;½Í#Âbl)%y[[’5ã(îN<ÆÃºCÌüNÉRu ÙÒ”äÜ ìÊ]ïUWÉçY|ÙÐ(Tƒ…U¶86­(5ªß74í}ÙèчÏÌ«f×v{#~G±´P* ¾h•*‰ÇŠåŒœkÍHÂ3Œ“œZÆh’e'÷jP]ï ®·q,ÙÐl.ÙYDÒô(iÆ$nâ0ôÐ6½Zòh!AOç¶”Q™ð=tm¡úþCS´ûª¹ýÔš²P“˜å/Q¾Rråm ñ” wèõ AòŒº…¬ëYhY¡‹s’2þl%¦IB"0Ÿ¡iK9¤©ðŠZöýz’!èÉ&%{2t²é¥¡ˆà-/T-¢ÍOæÒÛª‘ÝÃ{­ÝZ„ŠRçÏI²IðÆTÖ€q²ÏžVX’L}÷ñÓ"BòqnÓwm3tm]«î ô¯» prùƒŸŒÒH¿€"ŽI Ì?—‘2¤ ²Ò9Òeð,#QžŸI )5³0N©;ÝññpP”ýAFAÞr§‚Ý*èÇœˆI§A\ÀóÀÙç !’½þ_o¶ äYŸÙ 8ý|ýϲìŒÛÂ%Žï'Ó˜ï~§ ØÛywùÎßø^Ù0ŽryÈÙ¶0ä±¥Þl¶"ƒz‹ùäg˜ð&ÌMà“³µpÀ¸DfKƒ¿ÍP»'–…´äß(#¶8°?XÌìÉ;‰^uBD¡`ÙYDʽTÇTªôêÛ«—é’¨¬^}}õˆÒ?a1¸ ”ÇBƒOM‰gà<_bHžzŽÀ–u<WŸdó€-ò°*•ü"ƒ’¤s{=Ž6™É:àL¥ó¦=æs怽q¹IÀ­ÿ‚‡ mÑÖ¯ñÛ]k"’‰KXu…öë1É€…I2`mB«QT6ûÀ˜¿Á¦M pQªÎ pêõZ&è¦ÑÕ%Vâ^Ž0E—æ1Ĩtà7kíÅ«©V›j4é0|]@ÿ¦]µÎ!¿•}¥CæÃ&‰ <n´¹ØÍÌ;ï0ÄÄqHΔ©RÖúgŽ ay NŠRac.œ0ùÍPÜY ã€d9L2y©™Ifœ”7$ˆÔw4}µP-¬ozˆ‘­lÎjö0~Øù+(ÇfPÝÞ´"壈Œ^1«± m­²8/ÇC Ìì'¶×h£4üW{P»±vÝe›è W׋È#ÈÀh{{ØÓ1UñB+Õ`˜&œNx û¹PVžfu¡®†¡VN/_]oî}eŧ]£Ÿ×CÁPoÉ}ÿv·_U¥Ö~]æþk±Æ%æ7Vÿ#!i"};v…ÒJB³ðV‘F ä\µ¿ÀõãU‹iÇi¶ëÌ4~IædTQ+ò­Iz× ó8ÏΠÒ\®/ ÖÀ€Ì)7L,ò\Í!‹Ñºægw6Å‚)¦a;íwF›`Ï–‡0ëA~µÅtc¡XV™`¹ZÀ6y#úÊLÛiUÛuÛìÙ!Éù#VT®S…ªŽþºœUëÕÙÉcLLW¸}ÏÊqü´þÈ)*çVû¶œuÕ^и‚t<¢Â¯eù»l õ˜N§:×é'uPä$ÓXæòùô¥‰òœ‡­‹ªfÇÈDO¦Î‹^Å+õ|¦¢úªœžAPÂHm×£µ°õØ¿é „‹Ý¾/ ×Gîžp~‹~Âä÷:çœ@+ A˜ÚÇÎéà:§Çˆdz°ï»®µ±ÔDåÎWÖ}ë´·÷uRtWF›SsËèÓî.šÁYM‚š^×Éžk¼«º|´ñ™@ó¿À7>¿i™q(n=Ëwd¦¾û1î”øË–ü#¦÷æ6lSôßïö^w®Saá{â¾TÃØi+hús\ç9¡‚9Ò&‚tR2uמé¾Òœ@ ä9/Ó+}~8~RExJ2ÐH_E4ŽÕæà¶fau2®9²å¿a± ,yöŸa§ÿÖÁ¥¶WÇckM –~´’YÇÈ*Òd’AùÉgŠ”bÿøj“ošMÓrѶ`Ú.úíöÿÁÙìpáÿ}H“”'aÆ—Œù/3v%" endstream endobj 1016 0 obj << /Type /Page /Contents 1017 0 R /Resources 1015 0 R /MediaBox [0 0 612 792] /Parent 1003 0 R >> endobj 1018 0 obj << /D [1016 0 R /XYZ 72 744.907 null] >> endobj 1019 0 obj << /D [1016 0 R /XYZ 72 720 null] >> endobj 1020 0 obj << /D [1016 0 R /XYZ 72 648.105 null] >> endobj 1021 0 obj << /D [1016 0 R /XYZ 72 494.725 null] >> endobj 1022 0 obj << /D [1016 0 R /XYZ 72 295.88 null] >> endobj 1023 0 obj << /D [1016 0 R /XYZ 72 224.149 null] >> endobj 1024 0 obj << /D [1016 0 R /XYZ 72 182.306 null] >> endobj 1025 0 obj << /D [1016 0 R /XYZ 72 104.597 null] >> endobj 1015 0 obj << /Font << /F36 131 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R /F39 133 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1030 0 obj << /Length 1659 /Filter /FlateDecode >> stream xÚÅX[“Û4~ϯðΰQ-ɶl†a(…–v€Y¶ËSé Z[Ù˜uìÔ—M÷ßsts,Ç6-L2Ž.>×Oß9ràÝz÷jñÃõâÙK{)Jc{×k… Sê]çÞ;ÿòa[ç7Kø}»\–ø?ÖY¿UÇ»¢®.ôÜ•(o…`,ß_¿Yüt½ø°À 'ð°–"0/Û.Þ½¼æßx¢iâíÕ®­Æ qˆh͵v0H0MPj|&ˆÐyŒœ¡ªÚHP0„©‡ö²è Ñš89%î”É(PLóÌO»†ë¿¿NnÈ?‹8R²Ë´kt*PGŒ"(L+¾2ã,ëKÛ’ôݦn¾z ¼â¥0ÙxQ—%€|«u ‚¥×!d¼NŠéä•l%üÞnöus÷ýí–%º4þwsDòVŒñ…mûd¬)ºV™ï HâÊþLœ™>íùåë™%Ÿc÷6¾ÒÇ\ìý1Æòcü'EÂcS¶žÛ&¶,ǶUåp<41Øò%lhl$}™›M_­XÛ³ ºô÷Ûöb¶M6¡)ë%®Cc îÔG«¾EƒéHm§µÅ˜¢ˆ…©‹5Ÿ¿ó„%!=§‹½çe?÷õ*DÄÒ§7žQ‚‚˜M¾Ý²Šè›Kåt;YOàXÚÞü ù¼°| WÙêèB¾VÅs¸gÝKÙÊx‡–yVɧe©9ï¦s¯v Qtâ·`еkâ…-^·3$&þƒÃ'ŠëÊhvÀvÎ7zôýêDÆÂ¦ßR£8âÉŸRg?ó†al¸/ A9 ì¿Øð]§n¾@&:㘙À¿7 ‡]pzæ ”Nü$NŽA endstream endobj 1029 0 obj << /Type /Page /Contents 1030 0 R /Resources 1028 0 R /MediaBox [0 0 612 792] /Parent 1003 0 R /Annots [ 1026 0 R 1027 0 R ] >> endobj 1026 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [201.752 274.48 293.438 285.264] /Subtype/Link/A<> >> endobj 1027 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [200.646 256.547 292.332 267.331] /Subtype/Link/A<> >> endobj 1031 0 obj << /D [1029 0 R /XYZ 72 744.907 null] >> endobj 1032 0 obj << /D [1029 0 R /XYZ 72 702.62 null] >> endobj 1033 0 obj << /D [1029 0 R /XYZ 72 644.164 null] >> endobj 1034 0 obj << /D [1029 0 R /XYZ 72 570.396 null] >> endobj 1035 0 obj << /D [1029 0 R /XYZ 72 492.688 null] >> endobj 110 0 obj << /D [1029 0 R /XYZ 72 330.276 null] >> endobj 1036 0 obj << /D [1029 0 R /XYZ 72 243.606 null] >> endobj 1037 0 obj << /D [1029 0 R /XYZ 72 215.101 null] >> endobj 1038 0 obj << /D [1029 0 R /XYZ 72 173.236 null] >> endobj 1039 0 obj << /D [1029 0 R /XYZ 72 97.565 null] >> endobj 1028 0 obj << /Font << /F36 131 0 R /F46 173 0 R /F39 133 0 R /F64 204 0 R /F48 174 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1042 0 obj << /Length 1753 /Filter /FlateDecode >> stream xÚÕY[oÛ6~÷¯Ð[m fyIiÀÚ¬íP`ÖyOi(2‘-O¢›d¿~‡"%‹²ìÚNò0ˆ$š:çã¹|‡<ÂÁm€ƒÏ£³Ñ»OL1ŠÁl0""ã@† cÌæÁÕø§e1¿™P<ÞT“)•Ñø—"Ý,ÕJ':+VoíØ¥ÊUR)û@ž|›}}œþP†H rC$± Òåèêæ0þ%ÀˆÅQðPÏZ¡ˆàšþaw‡Q°‘I0ÂLZ¬ëë¦Bå™ÎT… wŸDØy‹†¸ AfýÒŒR-‹ áãïæŸêhÂã¼ÐVe±pØŠªÊnr7ë¦ÈrXmjì¶ðÖ¼Îíò8,¯˜+äÙ¬‹ ßâ1‘`JW—ÀÊd©´*«Aa=GP‰p(‰bæ’ì+¥¼ç¯6…–bL`âÁV‡ƒ>r?†GÊh°ýz#—µ¯IÎþ¹¯œ\χ¼C„Y1ÞH-fü)É+Õ“b÷GJ¨UÎÊÍó…¼”omåÅÙ±5x©ªM®O1¹ØgqŸÒ c™‡–pމB/k8ÃH2~FÀïfž‘AÈI™wÔN7F‰#þŒB°Yý°Ä„œR öŠ£œž°=¬,:P z”¹§xã\î?šü»«÷ÖÄIx`Wãªàyžh‡Ý8É(VÚßÒٰ̕ÚRA0C¡Ù¬tJÕ¹¥"Äа{yV©`2F2f¯^*:z^¢TÃs{Ë ƒl!ÔYÿæÉªpGÖºÃr2§Á *odKoÎxÑa­ß7Áe®Ûà:U˜ %ýèÅI¿O_ ;[yw=Ô女kÆé‘¿›‚Fï Ž4¡ Ï¨××·j¥J`áë´L‰¸Ö‰9iìýáxȱ얎s>÷€.ó?;}þ Vnoó¢¸ß¬7vÐtÙûÇ¸ß m9±öŒ»-”™mO€Ø‡¬9«Ü•šGÜ=æÍ„T<Æñ°ÞRxxÎrEZ,×­../ÌÏ!åIg¸ºà øQ"IØñg8sÎa=^X¤Æ˜¯=‰Öƒ0R¬ìÕàá·uRUµAa0s?Ú$G“iHÄø“©mEiÙ–NÓ}ŒyÓW2/W†Úmp·q‚E_+‡ EÝ®ª¬T Gu;Çt]¤EîdcZPÂjè*¥IÓm¸œ¨òávfÇõí`ž-Ll¡JeBÈŘ~PjÕß!¼™p°ÇN"$«¹—#«¢\&ùδ&¨õ]¢‡÷•NJ]õš* ÁÞ|70“|£z[®F~´ ÙþC¯~èÞ¯èæ(¤òÀ®k»ËjEM¿sk`[y¨cäô=¿eÔL“<Ýä[zqÙþêM¢ôN¥÷ÃÔJQrJ|wjj î}§ÆˆËDêýЃ¢ð”nS$‘àÂå­pLe–í"?[ô2`,ËDƒúÒ²VÝëö×ÿ¢£¼Ç_´î‡¾Z2ÁªMÑjzÊô àuÔ!(bñèÆMrGrÙÜà?Ø$†¨Ãâ%R~Âø¸lXµ >ió·ö©.{u—ÆB¯|ÈzM›þg6ØpPqôW¶ÝÏ€¦×E8‡PÁ9@ 4BŒ·«i¹Æy£+ Ä·>oHûðã£.{û[ï£û2E\Ùã,G¼¿êÿÑ*ͪ endstream endobj 1041 0 obj << /Type /Page /Contents 1042 0 R /Resources 1040 0 R /MediaBox [0 0 612 792] /Parent 1003 0 R >> endobj 1043 0 obj << /D [1041 0 R /XYZ 72 744.907 null] >> endobj 1044 0 obj << /D [1041 0 R /XYZ 72 600.284 null] >> endobj 1045 0 obj << /D [1041 0 R /XYZ 72 490.918 null] >> endobj 1046 0 obj << /D [1041 0 R /XYZ 72 379.395 null] >> endobj 1047 0 obj << /D [1041 0 R /XYZ 72 329.417 null] >> endobj 1048 0 obj << /D [1041 0 R /XYZ 72 223.858 null] >> endobj 1049 0 obj << /D [1041 0 R /XYZ 72 108.964 null] >> endobj 1040 0 obj << /Font << /F36 131 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R /F39 133 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1052 0 obj << /Length 1327 /Filter /FlateDecode >> stream xÚÕXMsÛ6½ëWðHÍX~€ìÍqëN29¤¶zr2ˆ„,L(R%Àºê¯ï‚)’¦m9ît¦\-owß.@bïÞÃÞ¯‹÷ëÅ»ë öR”Æ4öÖ[Q… ÓÀ[çÞÿù¸¯òÍ’b¿QËe‰ÿs•5{Qj®eU^XÙ(WÂ>„—_׿¬,ìƒ=b톈aæeûÅÝWìå ÿèa¤‰÷Ðjí½0N`,¼ÛÅo ì0â!Ö0™`%á€Y¬‡k£P£e!µ  ï®ãp°ŠE,›í¢¬Ú->Ý\YÝÑQŠ‹;Õ/8ÂV))¡”%NÎ5Ÿ1Db$"mˆ:ß é@{EE$aBPEvÕïJäËUE¾®ìè°;áÎMŠª¼—ºÉeÉ +©EÞ”9/³%‰ü£[»Ùw;å÷Kø‘¥ÒîÙJײ¼GËUL#½“ÊŠ»±*‹ÖTl˜A|‹-ñ޵ì€\Þ^}ø`<¯G¾ I¤Ä?Ô•®²ª€ÝYø—FŠýmSöÿ\¨¬–CA+¨¶vÔ'ÌäþPˆž¨V–q7Ù;n+Œ5.Ý_üpe.L¨þ²’÷Ó-ÜâÞ3˜²/9F#<ŽF˜Ž@ a]R°I˜·¢ŒÄÕæ’`Ÿ×|/´¨ÕÈšw·Š0°qÄ/¡²ž”ÆÜÚxÖ¢4ëÚ™¡š!*&ÝíÔEõ*c.€¯ÜfÀi”Ôny#tS›—ê¹:RD¾jzt/²¦àÚPÎ<»Ú"ÖÞ* '±û±^ajf¾S„ %”œÑ)ØãN1ò¹íýŽ3VB”&ýNmI,ñï3¸(…^Ÿßx†â(søÊ¸íø+·.é]2\©.¦„ÆôÙs AS“5Ó„½†û/Rÿ ×(C8M ƒžùÑsÜ…s0N^È€ È«J©§ï¸bWÔ6ÉÚ–>1F žÆhò1‚øçZ>/d>E9-îè…㣸—œS³ÌÒ'¿°Oׯ(/”Sª€OõƒTâ¿)þZ7׆p·òo1ShŒ ×\ì¹°]’Äߊz.otNzv+صø–Á¦¿*5’ÑÙ½€F ¨=¹„\uäÖ´2a±y™¶ˆ­ ›nxß,ªr¢fð» 1N ÿ_‹p}"› J"úLv|€Ò3\$àþ"K¸‡ÍÅM:H £oÁ?à‘qÒÑÓ>ÈÓÍi– “ ýD$à∞íB‡(œ¶Î·Ý1^¢s ú¼ìÄ#Ú,èºÉÂÄL»ÚσԻÑj6<_0¦¢èŽX·î@ãÇGµ[ü4æDC{"}D¶)`Z+}Ñìàón¢ª®–w‚çOÆÉ%¿ÀEÁå9é‚ðãö¹êΔҽg³­«›evºgnŽÏ#½˜GGŸ7¼¥SÃWÝlÎÝf¹ 4­Ûp”׎‚ËÄêc2¸lÀ#u;€¼ëÉ 3T7 'ðΤí¿-ˬz-¶Uí)[“u&Eé”ÛK(ÛÂ×5Ϻ?vnÓÅ@]Yq[ ÿ] Z—pº„;`<Ï'‹ AÉöÅnæ¥ëT²]¹ ¬™ü–ùI<ˆ¿,³º}eœÖS³Ÿ2 ┡ª|DÚ+hÝÕÛ0Ó-+úÌ÷,š~e‰BDã³?²Ì~ )"ı61žâ„À[?è¶ÞYâÓö³ù<ôInjZàÈÑJ.?˜ùr4uâwÈž\ endstream endobj 1051 0 obj << /Type /Page /Contents 1052 0 R /Resources 1050 0 R /MediaBox [0 0 612 792] /Parent 1003 0 R >> endobj 1053 0 obj << /D [1051 0 R /XYZ 72 744.907 null] >> endobj 1054 0 obj << /D [1051 0 R /XYZ 72 626.232 null] >> endobj 1055 0 obj << /D [1051 0 R /XYZ 72 511.338 null] >> endobj 1050 0 obj << /Font << /F36 131 0 R /F48 174 0 R /F64 204 0 R /F58 191 0 R /F39 133 0 R /F46 173 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1058 0 obj << /Length 305 /Filter /FlateDecode >> stream xÚRËNA¼ÏWôqç°ítÏûÈcˆ! sC†E4ŒzÐÏw–Åö`„SO¦«ºª 6 àF¨C|”£ÏàI¡ÕV[±X*¨óÿÆèàkÚ‚q!ÇW˜‹{ÑOâê:Ã#FÇÒ˜H,‹Œ¤,¤Å`Ô»KÕL.Ód'ƒÚ8nð¥Bã¡d‘¨E§Ñ¬ª¬¨Ò©;g=zmÎuÇ}ðÔȱ·¨|§9ØO‡ãA5—%ûPô¦Ãö‘dTE¯›—ßqåŠñ¨ß’‚Cg l¬Ó|`¶’ G¤€ÆÅœØƒ7’T±Þ½ìêµdU|ÿ©Ö2Kò5_ ¶}«ÏÒ}®?¤öÅJ’-žOhÝ­Zƒì|w©:†žœå€‘L¾8: %ø®äÙ¤E endstream endobj 1057 0 obj << /Type /Page /Contents 1058 0 R /Resources 1056 0 R /MediaBox [0 0 612 792] /Parent 1060 0 R >> endobj 1059 0 obj << /D [1057 0 R /XYZ 72 744.907 null] >> endobj 114 0 obj << /D [1057 0 R /XYZ 72 720 null] >> endobj 1056 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F58 191 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1063 0 obj << /Length 218 /Filter /FlateDecode >> stream xÚIO1 …ïù>&'d¹–MôÄ’[ÕC:I¡Ò,@§Bü{Ò8p²ýl=¿ánÙ"²óm!È`ÉBÜ‚#pÆK4Ä +~ÿÙy#ùa/rž_í¡/Ô¦Ý8œUí±t%íK”D±ŽKvÙSóU}tè íÙjg} (uððñsÕƒ±~®<±†ÇŒ§úËëÂH²ÿ¶úÓTŠ*¥÷¢ÑÊâ—/éu*ï•DKÑX"~7ä][Žüiȵ™ÒF(äݼøÆ=圱¿æVPÝ endstream endobj 1062 0 obj << /Type /Page /Contents 1063 0 R /Resources 1061 0 R /MediaBox [0 0 612 792] /Parent 1060 0 R >> endobj 1064 0 obj << /D [1062 0 R /XYZ 72 744.907 null] >> endobj 1061 0 obj << /Font << /F36 131 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1120 0 obj << /Length 1142 /Filter /FlateDecode >> stream xÚÅš]oÛ6†ïý+x™ó‰øMÞMº[»X…b3™€ØÎ$%mþý(‹N%•"ãŠÂn,Ãô>xÉsÈWr†nQ†Þ,²ÁñÇÕâü’ DH%1ZÝ BµýNàpÆÑjƒ®Î~û°úéý»WK"ÕÙ¯ï_ÿõËEûýçw¯/þ~õqõöùŽç—’#Ì€2AšÛ-±‡û-u„Òö~×Í5ç—L! ZÑœ™Ùs-†vgõ§Òä›O[SUù­i/ ºsæ ¶×.ø¡Å’)”•Ì~2%Pi¹¾ùñ7 tµ$<;£z€ßcZ:%Æ 9ÿŠö¹,j`ÃõD8†#Þ:•%±xXµZëqo•3w}W˜] yõ´[{à%I§ËDÐX§Ñ3Öa­÷Ûí~çã²3)›ì) sµ-—èq¸%8HN§R± •Ó軵ßUu¾«+§¶Ô¼#è4†LµùR{ˆŦºÄ#õàDzå°‰¶šM^ç×yå+c&A‘©•ÀIÐG§Ñóqc‹µˆrPBL ÷<§Ñ*òÛ@»KÒ18 î±etG×DG×<$±R¨“­4_Öæ¾.lõú ×.¾r2TÄH'²$2ÆZ©›¨‘7ùºÞ—OsÕ¶ÈÂUâJ»ëäMqgæž”Bœ>)‹¨—…m“¥5ÔÌ7ð ç@‡{åÞî7¥ÙÕl­’F¼ë•jÙJí£ÔûúS†&JŠ}E¬èŽ‹®á÷Qôû̓ow)AÓ©'Ãç4zwÿd'ÀõÃlÃ/ñÉ+å ^0Ó`ûݬÁiŒ3F“çÀìÜNœ”N¤·Í}ÆŒÄL8p¢çÝ]EüFÆ’¦x&ç G‘ ☇Xçd* ïàF“YK™Ò€òâÎ$p=ÙN¶Ó‰øíl6é•ÝÏí¬V©C†¯tT3*°À@5O½V8HŒ3°¤ÿœb”³4Û}=[Wd´`WÿÊxøô"RhöëGE[‘ȱDi›lÞDé4FÈÂÑò°ÐÔÑҳ،ìu`4@&q. ƒÎ“$Ƹd©wµ㜈¿xÇãá±*™71: ¿u‘è˜dÒ ùý“. ->ah‡|±H•f›}Á ˜?:%y(‰N½gÛâ(ÍmQÙÁ†©’©›YלˆßÁgÔhfQh6u£ #£íDü¬•)M9sf‘jBfq„³ UNÄX—ù®²-»ðGª4€zàC]Üuaæ‹(ŠÅ^¸¹ŒÒ}ŠSFŸâ¼¸æÓÌRrú,}y©'iìò;ûhHóN„„Gþø$¿;ðUtà#])Íb¤N~ÑnE‡—lÞNä4úT#é‰hPJ¥Oƒñu"½ñ­£ãn™iŒÔað£“]ð‡ø_B½4ÉÛãA+=/V‹Í”ÏF’ ΀‰ÖÛÅÕÇ mìÏo›Zùχ“¶ˆ‰åý¹øýùÿ*V¢@‚ŒIK¬ú–YÉÿ$?E endstream endobj 1119 0 obj << /Type /Page /Contents 1120 0 R /Resources 1118 0 R /MediaBox [0 0 612 792] /Parent 1060 0 R /Annots [ 1065 0 R 1066 0 R 1067 0 R 1068 0 R 1069 0 R 1070 0 R 1071 0 R 1072 0 R 1073 0 R 1074 0 R 1075 0 R 1076 0 R 1077 0 R 1078 0 R 1079 0 R 1080 0 R 1081 0 R 1082 0 R 1083 0 R 1084 0 R 1085 0 R 1086 0 R 1087 0 R 1088 0 R 1089 0 R 1090 0 R 1091 0 R 1092 0 R 1093 0 R 1094 0 R 1095 0 R 1096 0 R 1097 0 R 1098 0 R 1099 0 R 1100 0 R 1101 0 R 1102 0 R 1103 0 R 1104 0 R 1105 0 R 1106 0 R 1107 0 R 1108 0 R 1109 0 R 1110 0 R 1111 0 R 1112 0 R 1113 0 R 1114 0 R 1115 0 R 1116 0 R 1117 0 R ] >> endobj 1065 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [171.626 544.552 183.582 554.833] /Subtype /Link /A << /S /GoTo /D (section.2.1) >> >> endobj 1066 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [177.604 532.597 189.559 542.878] /Subtype /Link /A << /S /GoTo /D (section.2.2) >> >> endobj 1067 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [147.716 495.889 159.671 506.171] /Subtype /Link /A << /S /GoTo /D (section.2.5) >> >> endobj 1068 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [153.694 484.029 165.649 494.215] /Subtype /Link /A << /S /GoTo /D (section.2.3) >> >> endobj 1069 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [141.738 471.979 153.694 482.26] /Subtype /Link /A << /S /GoTo /D (section.2.4) >> >> endobj 1070 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [129.783 460.118 141.738 470.305] /Subtype /Link /A << /S /GoTo /D (section.2.6) >> >> endobj 1071 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [117.828 448.163 129.783 458.35] /Subtype /Link /A << /S /GoTo /D (subsection.2.7.2) >> >> endobj 1072 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [123.806 411.456 135.761 421.642] /Subtype /Link /A << /S /GoTo /D (subsection.2.7.4) >> >> endobj 1073 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [111.851 399.5 123.806 409.687] /Subtype /Link /A << /S /GoTo /D (section.2.9) >> >> endobj 1074 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [147.716 387.451 159.671 397.732] /Subtype /Link /A << /S /GoTo /D (section.2.8) >> >> endobj 1075 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [111.851 350.838 123.806 361.024] /Subtype /Link /A << /S /GoTo /D (section.2.15) >> >> endobj 1076 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [135.761 338.788 147.716 349.069] /Subtype /Link /A << /S /GoTo /D (section.2.12) >> >> endobj 1077 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [117.828 300.953 129.783 311.235] /Subtype /Link /A << /S /GoTo /D (section.2.10) >> >> endobj 1078 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [147.716 288.998 159.671 299.28] /Subtype /Link /A << /S /GoTo /D (section.2.14) >> >> endobj 1079 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [135.761 252.385 147.716 262.572] /Subtype /Link /A << /S /GoTo /D (section.2.11) >> >> endobj 1080 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [123.806 215.678 135.761 225.864] /Subtype /Link /A << /S /GoTo /D (subsection.2.7.5) >> >> endobj 1081 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [153.694 178.875 165.649 189.157] /Subtype /Link /A << /S /GoTo /D (section.2.13) >> >> endobj 1082 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [93.918 142.168 105.873 152.449] /Subtype /Link /A << /S /GoTo /D (section.2.16) >> >> endobj 1083 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [123.806 130.213 135.761 140.494] /Subtype /Link /A << /S /GoTo /D (section*.304) >> >> endobj 1084 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [225.425 118.257 237.38 128.539] /Subtype /Link /A << /S /GoTo /D (subsection.2.1.1) >> >> endobj 1085 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [231.402 106.302 243.357 116.584] /Subtype /Link /A << /S /GoTo /D (subsection.2.2.1) >> >> endobj 1086 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [201.514 94.347 213.469 104.628] /Subtype /Link /A << /S /GoTo /D (subsection.2.5.1) >> >> endobj 1087 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [207.492 82.392 219.447 92.673] /Subtype /Link /A << /S /GoTo /D (subsection.2.3.1) >> >> endobj 1088 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [195.537 70.437 207.492 80.718] /Subtype /Link /A << /S /GoTo /D (subsection.2.4.1) >> >> endobj 1089 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [422.563 559.601 434.518 569.882] /Subtype /Link /A << /S /GoTo /D (subsection.2.6.1) >> >> endobj 1090 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [470.384 547.646 482.339 557.927] /Subtype /Link /A << /S /GoTo /D (subsubsection*.108) >> >> endobj 1091 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [476.361 535.69 488.316 545.972] /Subtype /Link /A << /S /GoTo /D (subsubsection*.121) >> >> endobj 1092 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [476.361 523.735 488.316 534.017] /Subtype /Link /A << /S /GoTo /D (subsubsection*.127) >> >> endobj 1093 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [464.406 511.78 476.361 522.061] /Subtype /Link /A << /S /GoTo /D (subsubsection*.115) >> >> endobj 1094 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [458.428 499.825 470.384 510.106] /Subtype /Link /A << /S /GoTo /D (subsubsection*.91) >> >> endobj 1095 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [404.63 487.87 416.585 498.151] /Subtype /Link /A << /S /GoTo /D (subsection.2.9.1) >> >> endobj 1096 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [440.496 475.915 452.451 486.196] /Subtype /Link /A << /S /GoTo /D (subsection.2.8.1) >> >> endobj 1097 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [404.63 463.959 416.585 474.241] /Subtype /Link /A << /S /GoTo /D (subsection.2.15.1) >> >> endobj 1098 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [428.54 452.004 440.496 462.286] /Subtype /Link /A << /S /GoTo /D (subsection.2.12.1) >> >> endobj 1099 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [410.608 440.049 422.563 450.33] /Subtype /Link /A << /S /GoTo /D (subsection.2.10.1) >> >> endobj 1100 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [440.496 428.094 452.451 438.375] /Subtype /Link /A << /S /GoTo /D (subsection.2.14.1) >> >> endobj 1101 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [428.54 416.139 440.496 426.42] /Subtype /Link /A << /S /GoTo /D (subsection.2.11.1) >> >> endobj 1102 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [446.473 404.184 458.428 414.465] /Subtype /Link /A << /S /GoTo /D (subsection.2.13.1) >> >> endobj 1103 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [386.697 392.228 398.653 402.51] /Subtype /Link /A << /S /GoTo /D (subsection.2.16.1) >> >> endobj 1104 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [494.294 380.273 506.249 390.555] /Subtype /Link /A << /S /GoTo /D (subsection.2.18.1) >> >> endobj 1105 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [500.271 368.318 512.227 378.599] /Subtype /Link /A << /S /GoTo /D (subsection.2.19.1) >> >> endobj 1106 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [440.496 356.363 452.451 366.644] /Subtype /Link /A << /S /GoTo /D (subsection.2.21.1) >> >> endobj 1107 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [434.518 344.408 446.473 354.689] /Subtype /Link /A << /S /GoTo /D (subsection.2.20.1) >> >> endobj 1108 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [434.518 332.453 446.473 342.734] /Subtype /Link /A << /S /GoTo /D (subsection.2.22.1) >> >> endobj 1109 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [422.563 320.497 434.518 330.779] /Subtype /Link /A << /S /GoTo /D (subsection.2.23.1) >> >> endobj 1110 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [440.496 283.79 452.451 294.071] /Subtype /Link /A << /S /GoTo /D (section.2.18) >> >> endobj 1111 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [446.473 271.835 458.428 282.116] /Subtype /Link /A << /S /GoTo /D (section.2.19) >> >> endobj 1112 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [350.832 259.974 362.787 270.161] /Subtype /Link /A << /S /GoTo /D (subsection.2.7.3) >> >> endobj 1113 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [386.697 223.172 398.653 233.453] /Subtype /Link /A << /S /GoTo /D (section.2.21) >> >> endobj 1114 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [380.72 211.217 392.675 221.498] /Subtype /Link /A << /S /GoTo /D (section.2.20) >> >> endobj 1115 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [344.854 199.356 356.809 209.543] /Subtype /Link /A << /S /GoTo /D (subsection.2.7.1) >> >> endobj 1116 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [380.72 162.649 392.675 172.835] /Subtype /Link /A << /S /GoTo /D (section.2.22) >> >> endobj 1117 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [368.765 125.941 380.72 136.128] /Subtype /Link /A << /S /GoTo /D (section.2.23) >> >> endobj 1121 0 obj << /D [1119 0 R /XYZ 72 744.907 null] >> endobj 118 0 obj << /D [1119 0 R /XYZ 72 572.094 null] >> endobj 1118 0 obj << /Font << /F36 131 0 R /F75 1122 0 R /F48 174 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1125 0 obj << /Length 211 /Filter /FlateDecode >> stream xÚÍjÃ0„ïzŠ9Ú*+Y–¬ki(ôG·C)mÁ¶±iûö–s(ôÐÓ°Ãîì|„7ÖìÖ±åªÒ°Üj©áN0F5\Ù Îc[<}wÑJIÅx)o¤iŠ»x»Ðûá#ö‹ì½„6ì/!‚S¹svïØ™‰ô‡ r®â† ŽÛî>ù¯lƒÏi«ƒÒMÒ¯ì™ÑÜñª¿²jÅ¥þwÔŸ˜Jr!d¦´ScTÚp[ët7³ï±ÏPÑí øÐûPŠºøš0¯ýîiQNí endstream endobj 1124 0 obj << /Type /Page /Contents 1125 0 R /Resources 1123 0 R /MediaBox [0 0 612 792] /Parent 1060 0 R >> endobj 1126 0 obj << /D [1124 0 R /XYZ 72 744.907 null] >> endobj 1123 0 obj << /Font << /F36 131 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1168 0 obj << /Length 1141 /Filter /FlateDecode >> stream xÚÕ™moÚ:Çßó)ü¤‘ù!vì—Ú©Ó½WåJ“º ¹‰ÛF7$]âtbŸþ:@€PÒ¼€$Vœs~9‡ãÿ1<>vàÆq0鼿& `Ûr¸ƒÀäØÌ¶0Å€Ql1ÂÁÄwÝ›FW_{ß'Ÿ–3ß_; Û"6ÃÙ´>8Ÿ×ÐI1ïv>»‚$›iì ,Á0Ëof;¢¸o:}T¡Š¥VS7v›jy¨oBóA½>Æ¢›]ù¡9'°;‹¼4PÅøsÁîÜŒÜ÷0즉•j?𵯒lî»Âk C EÛ|ÛœØ`l Ž?v§Èx‡,Aiá§kììöMÓX§Ó‡XÎÔ4ñeΉÒë-Ÿ½Ôú{y9VÉs&*³Qk^jûÅÍZýÔ]SØuо¶16a² ÛöÆNz^%P©GöTÓî‹ïª ù×UI2ŒBGA1y¦ôSäµ9*DÎ\½ôíªPWPÄo`))Aäþ·3’Ð!dð5¶ä™ô8ìF×Y6®ˆ`¬žÈµŠ²Qi"bݬ°òGÄGÂÍTD8åëDý®.Xu,ÃDºÚÂj*&®ï—°}.Ð!dp£z‘5Aø¡Œç FlŒµÈH`$6©OËzޱN•GVÞfÅÄäcÅÛ¢¯<À2{-ÿŒ}Óæ¶oÚF¯›Î_«xŸ³NG.~ëÕ~‰‹\Ì÷’íT)û×í]Jûp…ù'ÈÖëã‰gÒ좚vœm­ÊÇà Â6˜kUînQßÔ0Wœ—*b¸[EðRF¸2pÓÀ¬\‹<º5«  †ð[›ðoâ`Yè­±Yp®ý‡èKªRµ¶å·Q¨+šåðúé4ˆévDo²ùyôïÎäl“Éi’§Ã'>ª¼’߄ϩ©ÀŸ™u7«©JŒDä„T'v.°²á·è+_>.B|e JË+Le6õYÓ½ ‹*>7¨Ef<©\ðîª&Û9Öj!ÙL&Xã]e£ sñÛÛÛG+Üö¸s’áxXÑ{å+_4FËÿTꚤÃþS)Zÿ®YÅÃö#x™š´¦MJO)C[€y]y²S*Ï:ó˜KÜn!0g¦/s¢¿þl™bµ;9W“Î2§ à`@m 3¸³ÎÝw<3ü){ ààg~Ó ØÌ˜¸í|YþѾ sK` ll!„ z±tcòb:în endstream endobj 1167 0 obj << /Type /Page /Contents 1168 0 R /Resources 1166 0 R /MediaBox [0 0 612 792] /Parent 1060 0 R /Annots [ 1127 0 R 1128 0 R 1129 0 R 1130 0 R 1131 0 R 1132 0 R 1133 0 R 1134 0 R 1135 0 R 1136 0 R 1137 0 R 1138 0 R 1139 0 R 1140 0 R 1141 0 R 1142 0 R 1143 0 R 1144 0 R 1145 0 R 1146 0 R 1147 0 R 1148 0 R 1149 0 R 1150 0 R 1151 0 R 1152 0 R 1153 0 R 1154 0 R 1155 0 R 1156 0 R 1157 0 R 1158 0 R 1159 0 R 1160 0 R 1161 0 R 1162 0 R 1163 0 R 1164 0 R ] >> endobj 1127 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 534.544 122.809 543.391] /Subtype /Link /A << /S /GoTo /D (page.85) >> >> endobj 1128 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [152.159 508.95 164.114 519.48] /Subtype /Link /A << /S /GoTo /D (page.71) >> >> endobj 1129 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 460.806 122.809 469.772] /Subtype /Link /A << /S /GoTo /D (page.59) >> >> endobj 1130 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 435.331 160.996 445.861] /Subtype /Link /A << /S /GoTo /D (page.60) >> >> endobj 1131 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 411.421 160.996 421.951] /Subtype /Link /A << /S /GoTo /D (page.62) >> >> endobj 1132 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 377.239 122.809 386.086] /Subtype /Link /A << /S /GoTo /D (page.82) >> >> endobj 1133 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 341.373 122.809 350.22] /Subtype /Link /A << /S /GoTo /D (page.83) >> >> endobj 1134 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 315.779 160.996 326.31] /Subtype /Link /A << /S /GoTo /D (page.81) >> >> endobj 1135 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 281.478 122.809 290.444] /Subtype /Link /A << /S /GoTo /D (page.79) >> >> endobj 1136 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 256.004 160.996 266.534] /Subtype /Link /A << /S /GoTo /D (page.62) >> >> endobj 1137 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 221.822 122.809 230.668] /Subtype /Link /A << /S /GoTo /D (page.82) >> >> endobj 1138 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 185.956 122.809 194.803] /Subtype /Link /A << /S /GoTo /D (page.83) >> >> endobj 1139 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 150.091 122.809 158.937] /Subtype /Link /A << /S /GoTo /D (page.81) >> >> endobj 1140 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 114.106 122.809 123.072] /Subtype /Link /A << /S /GoTo /D (page.79) >> >> endobj 1141 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [279.988 75.46 291.943 86.364] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1142 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [432.086 548.239 444.041 558.77] /Subtype /Link /A << /S /GoTo /D (page.48) >> >> endobj 1143 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [512.483 535.91 524.439 546.814] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1144 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [425.361 523.955 437.316 534.859] /Subtype /Link /A << /S /GoTo /D (page.39) >> >> endobj 1145 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [428.688 512 440.644 522.904] /Subtype /Link /A << /S /GoTo /D (page.41) >> >> endobj 1146 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 488.463 399.977 498.994] /Subtype /Link /A << /S /GoTo /D (page.62) >> >> endobj 1147 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 464.553 399.977 475.083] /Subtype /Link /A << /S /GoTo /D (page.82) >> >> endobj 1148 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 440.643 399.977 451.173] /Subtype /Link /A << /S /GoTo /D (page.83) >> >> endobj 1149 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 416.732 399.977 427.263] /Subtype /Link /A << /S /GoTo /D (page.81) >> >> endobj 1150 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 392.822 399.977 403.352] /Subtype /Link /A << /S /GoTo /D (page.80) >> >> endobj 1151 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [510.829 380.493 522.785 391.397] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1152 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [409.88 320.279 421.835 330.809] /Subtype /Link /A << /S /GoTo /D (page.67) >> >> endobj 1153 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [409.88 296.369 421.835 306.899] /Subtype /Link /A << /S /GoTo /D (page.70) >> >> endobj 1154 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 272.085 442.109 282.989] /Subtype /Link /A << /S /GoTo /D (page.56) >> >> endobj 1155 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 248.174 442.109 259.078] /Subtype /Link /A << /S /GoTo /D (page.56) >> >> endobj 1156 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [496.345 236.219 508.3 247.123] /Subtype /Link /A << /S /GoTo /D (page.70) >> >> endobj 1157 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 212.683 399.977 223.213] /Subtype /Link /A << /S /GoTo /D (page.59) >> >> endobj 1158 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [490.118 200.354 502.073 211.258] /Subtype /Link /A << /S /GoTo /D (page.85) >> >> endobj 1159 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 176.817 399.977 187.347] /Subtype /Link /A << /S /GoTo /D (page.62) >> >> endobj 1160 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 152.907 399.977 163.437] /Subtype /Link /A << /S /GoTo /D (page.82) >> >> endobj 1161 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 128.996 399.977 139.527] /Subtype /Link /A << /S /GoTo /D (page.83) >> >> endobj 1162 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 105.086 399.977 115.616] /Subtype /Link /A << /S /GoTo /D (page.81) >> >> endobj 1163 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 81.176 399.977 91.706] /Subtype /Link /A << /S /GoTo /D (page.80) >> >> endobj 1164 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [489.56 68.847 501.515 79.751] /Subtype /Link /A << /S /GoTo /D (page.86) >> >> endobj 1169 0 obj << /D [1167 0 R /XYZ 72 744.907 null] >> endobj 122 0 obj << /D [1167 0 R /XYZ 72 572.937 null] >> endobj 1166 0 obj << /Font << /F36 131 0 R /F75 1122 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1227 0 obj << /Length 1361 /Filter /FlateDecode >> stream xÚÝš[s›8€ßý+ôhÏÔª$$¯±“L:Élk{gÒN‡€’2‹ÁÑ6ÿ~%À6à{ v¼/q$lÎùt.::€À @à¶s5é|¼18°¡Í “g``R RÛÆÐf,¿mA =ßyù>Iâ¼u?ü!£C K«oRõ—ZÄJß•ÉÑm<ö C]ÆóßX ¿Nf %™Ea¶¬™ЬœE_x§,®f¹þÕì«Ü%Ñêk‘½>¦˜f4 Åê‰aÝ_¾+àÃbv…2Ž‚« rÿ]ä¹°©?"ï$Žö5Ï_9R§af©…Ça˦e+ecm¥ìŸ3XÉz;ÒÜó0·Q…‰ÛyeÎÀdïåy¾ò8è$¯¡ÛË~¦ ”šipŒlZózTuÁBªM§QؤXc½Ø*lÓ¬t«ÐA&t(ÜÈñRì<=©¡_,ÁJŒ?ë Ç•QüzL¸â‹%b™q ƒ²õ§´zðJma¥´“]Ùè×G'Êöòä Qζ›cÉ0±ï›HøI Ó ‡LÜÙ{4ˆÁ¸yÆßÞûÀ¨$•†¾›q*ÛK'–‹­Zù™UÙÈô0‹g‹®Æs¾e—vùÃ_ç•|¯DMg©ƒÑ #E.š' <nHF©ô_ú"9bÁ-¶=å:Þ¿oÃP¸²¤ )¶ý‹I˜| ¶n.ež6׬X…›öE&͆ÉE¥Íªw©œy%e'#&©í®ˆËU*¬‡Ñç8’‘Ù]PÓ¬|£5Úˆ¸8bôë®0+G]÷©ª Ž@¬-–id×ÇeVBŒÿ­]`}ÿvU¥*KNp \.¯zIþÈ…²êqçãɦРœèŽ˜Òhsšë0Ô_®5Í´šš–Ås¤ó”·ìÓ‘l= ‰ãÀÑΑ»Í ²ZÙµŽ]qŽÔé5(Q‰:` 8Ç<«zü®lÛ€2f.W÷%v¦#á Y{¥hÃJJÃѦvÉe´©ZádÑÆ–Ô[ˆ FñNb~Q¹´ âËʨ^Öò©”×ë ¹'_~…ãÍ{~p¤W¾œ7èõÙ¡½jÎØçôØKÞ'½4˜ß±/Å‚æ=zHéÏ•‘ý`ñ¥½j›4ík»‘Š–ö™˜2ÓØ_r¢öm„OÌs^êtåp¨¡:&ùîX:2m?€mÒ8;XÚ6 co€¡hC/¤Ú„Û[­1mï_nr6“Ò½ ®õbáÝû‰¡ªÄÙ‰6Xi~ºØ´¬pÊmhÝ=Zmkëy­®°aØl/GbIQ«þNa†·”iÔæ@Eè•Ì@í£ŽëV¡Y_*=RìqU4¯y:yü²ãƒÜho]Ç‹z¿a}ÑAúnܾ"DQ©|oüçèK*RÑú.ÇÍ&wìí(morÛY6e#Î7d#_ï…3‰Þ-èÜÚ#ySahbãP’­¢öÐŒ&.’?D¼ð·[!u-²H¬Åk^ík²TÜ“©õhâíAéÄpýÇ3]1ž¨žç´=Ç[ËÓºX›šE±¬d‡;¯}ñµàúk®ŒBÂ÷~Ëuí¸”@ŒI®¶ZÕsÄT¿ËfïB/Ï…•”*ÿ – Ï endstream endobj 1226 0 obj << /Type /Page /Contents 1227 0 R /Resources 1225 0 R /MediaBox [0 0 612 792] /Parent 1060 0 R /Annots [ 1165 0 R 1170 0 R 1171 0 R 1172 0 R 1173 0 R 1174 0 R 1175 0 R 1176 0 R 1177 0 R 1178 0 R 1179 0 R 1180 0 R 1181 0 R 1182 0 R 1183 0 R 1184 0 R 1185 0 R 1186 0 R 1187 0 R 1188 0 R 1189 0 R 1190 0 R 1191 0 R 1192 0 R 1193 0 R 1194 0 R 1195 0 R 1196 0 R 1197 0 R 1198 0 R 1199 0 R 1200 0 R 1201 0 R 1202 0 R 1203 0 R 1204 0 R 1205 0 R 1206 0 R 1207 0 R 1208 0 R 1209 0 R 1210 0 R 1211 0 R 1212 0 R 1213 0 R 1214 0 R 1215 0 R 1216 0 R 1217 0 R 1218 0 R 1219 0 R 1220 0 R 1221 0 R 1222 0 R 1223 0 R ] >> endobj 1165 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [191.172 694.929 203.127 705.833] /Subtype /Link /A << /S /GoTo /D (page.56) >> >> endobj 1170 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [191.172 671.019 203.127 681.923] /Subtype /Link /A << /S /GoTo /D (page.56) >> >> endobj 1171 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 647.482 160.996 658.012] /Subtype /Link /A << /S /GoTo /D (page.60) >> >> endobj 1172 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [191.172 623.198 203.127 634.102] /Subtype /Link /A << /S /GoTo /D (page.58) >> >> endobj 1173 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [191.172 599.288 203.127 610.192] /Subtype /Link /A << /S /GoTo /D (page.59) >> >> endobj 1174 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [162.31 587.333 174.265 598.237] /Subtype /Link /A << /S /GoTo /D (page.46) >> >> endobj 1175 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [174.495 575.751 186.45 586.281] /Subtype /Link /A << /S /GoTo /D (page.43) >> >> endobj 1176 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [157.887 563.422 169.842 574.326] /Subtype /Link /A << /S /GoTo /D (page.44) >> >> endobj 1177 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [247.131 551.467 259.087 562.371] /Subtype /Link /A << /S /GoTo /D (page.61) >> >> endobj 1178 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 527.93 160.996 538.461] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1179 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 504.02 160.996 514.55] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1180 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 480.11 160.996 490.64] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1181 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 456.199 160.996 466.73] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1182 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 433.853 122.809 442.819] /Subtype /Link /A << /S /GoTo /D (page.69) >> >> endobj 1183 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [262.215 419.96 274.17 430.864] /Subtype /Link /A << /S /GoTo /D (page.85) >> >> endobj 1184 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [261.657 408.005 273.612 418.909] /Subtype /Link /A << /S /GoTo /D (page.85) >> >> endobj 1185 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 384.468 160.996 394.999] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1186 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 360.558 160.996 371.088] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1187 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 336.648 160.996 347.178] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1188 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 312.737 160.996 323.268] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1189 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 276.872 160.996 287.402] /Subtype /Link /A << /S /GoTo /D (page.46) >> >> endobj 1190 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 242.69 122.809 251.537] /Subtype /Link /A << /S /GoTo /D (page.78) >> >> endobj 1191 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 205.141 160.996 215.671] /Subtype /Link /A << /S /GoTo /D (page.46) >> >> endobj 1192 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 170.959 122.809 179.806] /Subtype /Link /A << /S /GoTo /D (page.78) >> >> endobj 1193 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [152.079 157.32 164.034 167.85] /Subtype /Link /A << /S /GoTo /D (page.46) >> >> endobj 1194 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [144.179 145.365 156.134 155.895] /Subtype /Link /A << /S /GoTo /D (page.51) >> >> endobj 1195 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.299 107.611 161.254 118.142] /Subtype /Link /A << /S /GoTo /D (page.52) >> >> endobj 1196 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [208.039 83.701 219.994 94.231] /Subtype /Link /A << /S /GoTo /D (page.53) >> >> endobj 1197 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 685.031 361.791 693.878] /Subtype /Link /A << /S /GoTo /D (page.78) >> >> endobj 1198 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 647.482 399.977 658.012] /Subtype /Link /A << /S /GoTo /D (page.46) >> >> endobj 1199 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 613.3 361.791 622.147] /Subtype /Link /A << /S /GoTo /D (page.78) >> >> endobj 1200 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 587.706 399.977 598.237] /Subtype /Link /A << /S /GoTo /D (page.39) >> >> endobj 1201 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 563.796 399.977 574.326] /Subtype /Link /A << /S /GoTo /D (page.39) >> >> endobj 1202 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 539.886 399.977 550.416] /Subtype /Link /A << /S /GoTo /D (page.42) >> >> endobj 1203 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 515.975 399.977 526.506] /Subtype /Link /A << /S /GoTo /D (page.42) >> >> endobj 1204 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 492.065 399.977 502.595] /Subtype /Link /A << /S /GoTo /D (page.41) >> >> endobj 1205 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 468.155 399.977 478.685] /Subtype /Link /A << /S /GoTo /D (page.42) >> >> endobj 1206 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 444.244 399.977 454.774] /Subtype /Link /A << /S /GoTo /D (page.54) >> >> endobj 1207 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 420.334 399.977 430.864] /Subtype /Link /A << /S /GoTo /D (page.55) >> >> endobj 1208 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 396.423 399.977 406.954] /Subtype /Link /A << /S /GoTo /D (page.69) >> >> endobj 1209 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 372.513 399.977 383.043] /Subtype /Link /A << /S /GoTo /D (page.69) >> >> endobj 1210 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [518.123 360.184 530.078 371.088] /Subtype /Link /A << /S /GoTo /D (page.68) >> >> endobj 1211 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 336.648 399.977 347.178] /Subtype /Link /A << /S /GoTo /D (page.68) >> >> endobj 1212 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 314.301 361.791 323.268] /Subtype /Link /A << /S /GoTo /D (page.69) >> >> endobj 1213 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [523.084 300.409 535.039 311.312] /Subtype /Link /A << /S /GoTo /D (page.61) >> >> endobj 1214 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [524.579 288.453 536.534 299.357] /Subtype /Link /A << /S /GoTo /D (page.60) >> >> endobj 1215 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 264.917 399.977 275.447] /Subtype /Link /A << /S /GoTo /D (page.67) >> >> endobj 1216 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 241.006 399.977 251.537] /Subtype /Link /A << /S /GoTo /D (page.67) >> >> endobj 1217 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 217.096 399.977 227.626] /Subtype /Link /A << /S /GoTo /D (page.61) >> >> endobj 1218 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 193.186 399.977 203.716] /Subtype /Link /A << /S /GoTo /D (page.63) >> >> endobj 1219 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 169.275 399.977 179.806] /Subtype /Link /A << /S /GoTo /D (page.65) >> >> endobj 1220 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 145.365 399.977 155.895] /Subtype /Link /A << /S /GoTo /D (page.66) >> >> endobj 1221 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 121.455 399.977 131.985] /Subtype /Link /A << /S /GoTo /D (page.64) >> >> endobj 1222 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 97.544 399.977 108.075] /Subtype /Link /A << /S /GoTo /D (page.65) >> >> endobj 1223 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 73.634 399.977 84.164] /Subtype /Link /A << /S /GoTo /D (page.66) >> >> endobj 1228 0 obj << /D [1226 0 R /XYZ 72 744.907 null] >> endobj 1225 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F75 1122 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1290 0 obj << /Length 1270 /Filter /FlateDecode >> stream xÚÕšK“›8€ïþ:ÚU±"Ðuž;©MUbÏÖ&©Í„Z ˆdçßG266~0Œ-ÙžƒX`õ×Ý’º¥Fà pÛ»¸ï½¿q)àS‡‚ûGàzbÆ#>$Ü÷xèzždÑ÷ƒúe1:Ìï_ea9© dœ¥ïªßF"A!ª Ñàëý‡Þõ}ïg«ÎÀ€9ê dˆpÒ{øŠ@¤~ÿt¹~ÏîšB}õ™€qïs5å+‚êÿÂ"—U2F"Ì"ñyH½ð`ˆ9êë«é£þŠü0“?Dþm"Š"xp$¦Y.ÇI [ °×wÑHÓ,U(ЏúCŒ!÷¼ª³‰?²HwônF‰ ƒ|Áˆz'>¹’{ãÇÑm< õ)­ž›ãá¶^Ö‘#Û‘¦Q ¯ÿÅT[£^Ýï"é6‘ÞCnW|·àwI"´ÞŸ^?HnÊ4¬(~–¢› †,°‰~p'ÈÇúòÓÕ?Õ½‡K‰šR¢vùv:}^©8.¤òý\ÑÊ”¿/[‹¹æ/›þÏœ=´o†®§Ä£FñþÍc)>–‰Œ§‰ØfI‹˜îñ¬Ø†i{¶fÄçoUƒžÒ–ž5—íÎhÝÞ1 9ŽÓ§&¢}+’SžÖ„;üÔ¥îvÂBäUЗ<¯‹ç4\YÇçæú+H£DÝa©[4ø¨¥ÔÈÙ HâtpdQ™ˆêûl)ã$–±(Õ'/$[„Üé_Q ƒBf¹€Õ»^”—Ú¿R­IþgOñ„wp¦«%S±Ôx˜Åür¡ü ¾P ¤ò…ö‚ÂŽ×ÿ‡b)Zåôéñö>ãài1˜ìµÝµ®T¯iVÈ8«´³œJ‡±†Itþ9³‰n¨2ÜéFµ;®päØ:š7iY›ƒæiÞ1·ÂÑ m8ÔŽ·Î8žÌV÷¹‰°ëº«LêÚ™1ÍN`¢C˜†ÂØá (}=ƒÒ_Î*ŠCùmšgS‘Ëç3\G xVïg¬®â¬Kö]{¾ßîþÇûæL K¨£7¥j•;êìWÏ\ëGÖv¯4.ÌçÕ-"í‘}åfjtËö¬ÝÔž‰Ë;x˜ –cäèæaZòœË,NìçpÄ9>Ò®xŸ¾%¦*‘ÑDömÔœAÖ?æbP—@B©y<Ûù™Y{­.pWh¥=’£fYl¦Sx°CßS¥³ÔYå7ð2›LÊ4ƒù „ ry½h¶˜8£}œÒ ê:•"Ñßzg#Uñ…h!25ÝÑ}V¥Çk@ËiIb朂ú­¹ÛºÀ®Ë½NÄ$“*æE¼l8‚c­ót,œ¾j,ÒhÅ „V)79}}AÈID#X»‰³Ï¥(…õ™˜2“«J;Šõ³_£,Íãì[!õT\ûÕeV¦ÇØw¦žIWëÈt‡ô{BiŸ«SÜ#…3Ô;2uYôº…!¶MdÕåηÔe锥.ô¥.#±ˆtv•7Y b‡îl D á{È0Ï¢@d?ηU b›ñ, DŒAžkˆUÀ³(iÙÚyÕ 3³7¸ï³¾Ž7ú1xèþÂÆÍ–Pë5êQQÁÝ (u,ä3ª²u‹~½”Ý#С+Ù7KíÕ_bìT8wiT͆ñ(䎣Ÿ5òæBªDù~…J? endstream endobj 1289 0 obj << /Type /Page /Contents 1290 0 R /Resources 1288 0 R /MediaBox [0 0 612 792] /Parent 1292 0 R /Annots [ 1224 0 R 1229 0 R 1230 0 R 1231 0 R 1232 0 R 1233 0 R 1234 0 R 1235 0 R 1236 0 R 1237 0 R 1238 0 R 1239 0 R 1240 0 R 1241 0 R 1242 0 R 1243 0 R 1244 0 R 1245 0 R 1246 0 R 1247 0 R 1248 0 R 1249 0 R 1250 0 R 1251 0 R 1252 0 R 1253 0 R 1254 0 R 1255 0 R 1256 0 R 1257 0 R 1258 0 R 1259 0 R 1260 0 R 1261 0 R 1262 0 R 1263 0 R 1264 0 R 1265 0 R 1266 0 R 1267 0 R 1268 0 R 1269 0 R 1270 0 R 1271 0 R 1272 0 R 1273 0 R 1274 0 R 1275 0 R 1276 0 R 1277 0 R 1278 0 R 1279 0 R 1280 0 R 1281 0 R 1282 0 R 1283 0 R 1284 0 R 1285 0 R 1286 0 R ] >> endobj 1224 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 695.303 160.996 705.833] /Subtype /Link /A << /S /GoTo /D (page.66) >> >> endobj 1229 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 673.076 122.809 681.923] /Subtype /Link /A << /S /GoTo /D (page.71) >> >> endobj 1230 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 647.482 160.996 658.012] /Subtype /Link /A << /S /GoTo /D (page.71) >> >> endobj 1231 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [265.862 635.153 277.817 646.057] /Subtype /Link /A << /S /GoTo /D (page.70) >> >> endobj 1232 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 611.617 160.996 622.147] /Subtype /Link /A << /S /GoTo /D (page.72) >> >> endobj 1233 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 587.706 160.996 598.237] /Subtype /Link /A << /S /GoTo /D (page.72) >> >> endobj 1234 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 563.796 160.996 574.326] /Subtype /Link /A << /S /GoTo /D (page.73) >> >> endobj 1235 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 539.886 160.996 550.416] /Subtype /Link /A << /S /GoTo /D (page.74) >> >> endobj 1236 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 515.975 160.996 526.506] /Subtype /Link /A << /S /GoTo /D (page.75) >> >> endobj 1237 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 492.065 160.996 502.595] /Subtype /Link /A << /S /GoTo /D (page.75) >> >> endobj 1238 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 468.155 160.996 478.685] /Subtype /Link /A << /S /GoTo /D (page.74) >> >> endobj 1239 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 444.244 160.996 454.774] /Subtype /Link /A << /S /GoTo /D (page.75) >> >> endobj 1240 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 420.334 160.996 430.864] /Subtype /Link /A << /S /GoTo /D (page.76) >> >> endobj 1241 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [235.535 408.005 247.49 418.909] /Subtype /Link /A << /S /GoTo /D (page.84) >> >> endobj 1242 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 384.468 160.996 394.999] /Subtype /Link /A << /S /GoTo /D (page.49) >> >> endobj 1243 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [231.649 372.14 243.605 383.043] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1244 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [140.752 360.558 152.707 371.088] /Subtype /Link /A << /S /GoTo /D (page.59) >> >> endobj 1245 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [170.888 348.229 182.843 359.133] /Subtype /Link /A << /S /GoTo /D (page.54) >> >> endobj 1246 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [191.172 324.319 203.127 335.223] /Subtype /Link /A << /S /GoTo /D (page.54) >> >> endobj 1247 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [191.172 300.409 203.127 311.312] /Subtype /Link /A << /S /GoTo /D (page.55) >> >> endobj 1248 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [191.172 276.498 203.127 287.402] /Subtype /Link /A << /S /GoTo /D (page.55) >> >> endobj 1249 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [191.172 252.588 203.127 263.492] /Subtype /Link /A << /S /GoTo /D (page.55) >> >> endobj 1250 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [261.647 240.633 273.602 251.537] /Subtype /Link /A << /S /GoTo /D (page.84) >> >> endobj 1251 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 218.78 122.809 227.626] /Subtype /Link /A << /S /GoTo /D (page.71) >> >> endobj 1252 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 168.433 160.996 178.964] /Subtype /Link /A << /S /GoTo /D (page.39) >> >> endobj 1253 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 144.523 160.996 155.053] /Subtype /Link /A << /S /GoTo /D (page.39) >> >> endobj 1254 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 120.613 160.996 131.143] /Subtype /Link /A << /S /GoTo /D (page.42) >> >> endobj 1255 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 96.702 160.996 107.233] /Subtype /Link /A << /S /GoTo /D (page.42) >> >> endobj 1256 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 72.792 160.996 83.322] /Subtype /Link /A << /S /GoTo /D (page.41) >> >> endobj 1257 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 695.303 399.977 705.833] /Subtype /Link /A << /S /GoTo /D (page.42) >> >> endobj 1258 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 671.392 399.977 681.923] /Subtype /Link /A << /S /GoTo /D (page.54) >> >> endobj 1259 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 647.482 399.977 658.012] /Subtype /Link /A << /S /GoTo /D (page.55) >> >> endobj 1260 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 623.572 399.977 634.102] /Subtype /Link /A << /S /GoTo /D (page.70) >> >> endobj 1261 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 599.661 399.977 610.192] /Subtype /Link /A << /S /GoTo /D (page.69) >> >> endobj 1262 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [518.123 587.333 530.078 598.237] /Subtype /Link /A << /S /GoTo /D (page.68) >> >> endobj 1263 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 563.796 399.977 574.326] /Subtype /Link /A << /S /GoTo /D (page.68) >> >> endobj 1264 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 541.45 361.791 550.416] /Subtype /Link /A << /S /GoTo /D (page.69) >> >> endobj 1265 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 515.975 399.977 526.506] /Subtype /Link /A << /S /GoTo /D (page.67) >> >> endobj 1266 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 492.065 399.977 502.595] /Subtype /Link /A << /S /GoTo /D (page.67) >> >> endobj 1267 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 468.155 399.977 478.685] /Subtype /Link /A << /S /GoTo /D (page.65) >> >> endobj 1268 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 444.244 399.977 454.774] /Subtype /Link /A << /S /GoTo /D (page.66) >> >> endobj 1269 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 420.334 399.977 430.864] /Subtype /Link /A << /S /GoTo /D (page.65) >> >> endobj 1270 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 396.423 399.977 406.954] /Subtype /Link /A << /S /GoTo /D (page.65) >> >> endobj 1271 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 372.513 399.977 383.043] /Subtype /Link /A << /S /GoTo /D (page.66) >> >> endobj 1272 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 348.603 399.977 359.133] /Subtype /Link /A << /S /GoTo /D (page.66) >> >> endobj 1273 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 326.376 361.791 335.223] /Subtype /Link /A << /S /GoTo /D (page.71) >> >> endobj 1274 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [504.843 312.364 516.798 323.268] /Subtype /Link /A << /S /GoTo /D (page.70) >> >> endobj 1275 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 288.827 399.977 299.357] /Subtype /Link /A << /S /GoTo /D (page.72) >> >> endobj 1276 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 264.917 399.977 275.447] /Subtype /Link /A << /S /GoTo /D (page.72) >> >> endobj 1277 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 241.006 399.977 251.537] /Subtype /Link /A << /S /GoTo /D (page.73) >> >> endobj 1278 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 217.096 399.977 227.626] /Subtype /Link /A << /S /GoTo /D (page.74) >> >> endobj 1279 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 193.186 399.977 203.716] /Subtype /Link /A << /S /GoTo /D (page.75) >> >> endobj 1280 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 169.275 399.977 179.806] /Subtype /Link /A << /S /GoTo /D (page.75) >> >> endobj 1281 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 145.365 399.977 155.895] /Subtype /Link /A << /S /GoTo /D (page.74) >> >> endobj 1282 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 121.455 399.977 131.985] /Subtype /Link /A << /S /GoTo /D (page.75) >> >> endobj 1283 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [527.179 109.126 539.134 120.03] /Subtype /Link /A << /S /GoTo /D (page.69) >> >> endobj 1284 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [379.036 97.544 390.991 108.075] /Subtype /Link /A << /S /GoTo /D (page.68) >> >> endobj 1285 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [493.426 85.216 505.381 96.119] /Subtype /Link /A << /S /GoTo /D (page.71) >> >> endobj 1286 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [396.44 73.26 408.395 84.164] /Subtype /Link /A << /S /GoTo /D (page.64) >> >> endobj 1291 0 obj << /D [1289 0 R /XYZ 72 744.907 null] >> endobj 1288 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F75 1122 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1348 0 obj << /Length 1307 /Filter /FlateDecode >> stream xÚÝšMsÚ<€ïü ñLq%Ù–íc !Mß7M Ìôv:®­MLe¹-ÿ¾’MÁ¦@A$ህ?öñ~hw%Æ‚‹VwÔzÝwí`F·ÀÇÀwÛ 0JÀMûÃl’%ß, ÛEnu°´{Y\L(—‘dU hJ£œVȆ֗ѻÖù¨õ£…Ôs @Õ}]Û‡>ˆ'­›/$jü€¶àWyÖ¸$Pß)¶>¶`CÆpEFmèø•ŒÔB^û·þ q!égèAõ‡¬ a[M-Û5û“_’¯šçјÚup–±4ÐÍ¥Nh„ìÐóªgM¨¼Ë}ûW%$´1 4…ïªO7 @(±ÿ\´ÀM{°íV/ç/è¬{ʈz,•ô’O ù¢Ñ~ &é‚í“>º*Rɦ)Ý®6r(6|L¶!ããŠÌ¼ÒÐcÀÜÀ_§LÅ;Êg<¶¯ãgåð‘É,ÎRƒTä1TØñÉV¬’ª«"Þ*™AôŒ`h 3²¯-"žG±ŽòWW¦) ªË \¢ñ±Îî">¦oò˜±2öhÊ&ÊׄqóÈèÔ,β‚+ óÑðhD×?Ëë…(x g^Wy²ž:àY.YDÌøÐúqO[½ƒ&yvôý {¿VÎJCã ’×¶YW÷A{®ÉÎÛ ×nÑv±®ÄK»®þº®½äɼ«PI‰òÂÑ endstream endobj 1347 0 obj << /Type /Page /Contents 1348 0 R /Resources 1346 0 R /MediaBox [0 0 612 792] /Parent 1292 0 R /Annots [ 1287 0 R 1293 0 R 1294 0 R 1295 0 R 1296 0 R 1297 0 R 1298 0 R 1299 0 R 1300 0 R 1301 0 R 1302 0 R 1303 0 R 1304 0 R 1305 0 R 1306 0 R 1307 0 R 1308 0 R 1309 0 R 1310 0 R 1311 0 R 1312 0 R 1313 0 R 1314 0 R 1315 0 R 1316 0 R 1317 0 R 1318 0 R 1319 0 R 1320 0 R 1321 0 R 1322 0 R 1323 0 R 1324 0 R 1325 0 R 1326 0 R 1327 0 R 1328 0 R 1329 0 R 1330 0 R 1331 0 R 1332 0 R 1333 0 R 1334 0 R 1335 0 R 1336 0 R 1337 0 R 1338 0 R 1339 0 R 1340 0 R 1341 0 R 1342 0 R 1343 0 R 1344 0 R ] >> endobj 1287 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 695.303 160.996 705.833] /Subtype /Link /A << /S /GoTo /D (page.40) >> >> endobj 1293 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 671.392 160.996 681.923] /Subtype /Link /A << /S /GoTo /D (page.40) >> >> endobj 1294 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 647.482 160.996 658.012] /Subtype /Link /A << /S /GoTo /D (page.42) >> >> endobj 1295 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 623.572 160.996 634.102] /Subtype /Link /A << /S /GoTo /D (page.41) >> >> endobj 1296 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 599.661 160.996 610.192] /Subtype /Link /A << /S /GoTo /D (page.46) >> >> endobj 1297 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 575.751 160.996 586.281] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1298 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 551.841 160.996 562.371] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1299 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 527.93 160.996 538.461] /Subtype /Link /A << /S /GoTo /D (page.56) >> >> endobj 1300 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 504.02 160.996 514.55] /Subtype /Link /A << /S /GoTo /D (page.56) >> >> endobj 1301 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 480.11 160.996 490.64] /Subtype /Link /A << /S /GoTo /D (page.58) >> >> endobj 1302 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 456.199 160.996 466.73] /Subtype /Link /A << /S /GoTo /D (page.55) >> >> endobj 1303 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 432.289 160.996 442.819] /Subtype /Link /A << /S /GoTo /D (page.56) >> >> endobj 1304 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 408.379 160.996 418.909] /Subtype /Link /A << /S /GoTo /D (page.55) >> >> endobj 1305 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 384.468 160.996 394.999] /Subtype /Link /A << /S /GoTo /D (page.57) >> >> endobj 1306 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 360.558 160.996 371.088] /Subtype /Link /A << /S /GoTo /D (page.57) >> >> endobj 1307 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 336.648 160.996 347.178] /Subtype /Link /A << /S /GoTo /D (page.57) >> >> endobj 1308 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 312.737 160.996 323.268] /Subtype /Link /A << /S /GoTo /D (page.56) >> >> endobj 1309 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 288.827 160.996 299.357] /Subtype /Link /A << /S /GoTo /D (page.55) >> >> endobj 1310 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 264.917 160.996 275.447] /Subtype /Link /A << /S /GoTo /D (page.58) >> >> endobj 1311 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 241.006 160.996 251.537] /Subtype /Link /A << /S /GoTo /D (page.58) >> >> endobj 1312 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 217.096 160.996 227.626] /Subtype /Link /A << /S /GoTo /D (page.57) >> >> endobj 1313 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 193.186 160.996 203.716] /Subtype /Link /A << /S /GoTo /D (page.58) >> >> endobj 1314 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 169.275 160.996 179.806] /Subtype /Link /A << /S /GoTo /D (page.57) >> >> endobj 1315 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 145.365 160.996 155.895] /Subtype /Link /A << /S /GoTo /D (page.67) >> >> endobj 1316 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 121.455 160.996 131.985] /Subtype /Link /A << /S /GoTo /D (page.65) >> >> endobj 1317 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 97.544 160.996 108.075] /Subtype /Link /A << /S /GoTo /D (page.65) >> >> endobj 1318 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 73.634 160.996 84.164] /Subtype /Link /A << /S /GoTo /D (page.66) >> >> endobj 1319 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 695.303 399.977 705.833] /Subtype /Link /A << /S /GoTo /D (page.71) >> >> endobj 1320 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 671.392 399.977 681.923] /Subtype /Link /A << /S /GoTo /D (page.73) >> >> endobj 1321 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 647.482 399.977 658.012] /Subtype /Link /A << /S /GoTo /D (page.73) >> >> endobj 1322 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 623.572 399.977 634.102] /Subtype /Link /A << /S /GoTo /D (page.74) >> >> endobj 1323 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 599.661 399.977 610.192] /Subtype /Link /A << /S /GoTo /D (page.75) >> >> endobj 1324 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 575.751 399.977 586.281] /Subtype /Link /A << /S /GoTo /D (page.75) >> >> endobj 1325 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 551.841 399.977 562.371] /Subtype /Link /A << /S /GoTo /D (page.76) >> >> endobj 1326 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [494.671 513.714 506.626 524.618] /Subtype /Link /A << /S /GoTo /D (page.61) >> >> endobj 1327 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [496.165 501.758 508.121 512.662] /Subtype /Link /A << /S /GoTo /D (page.61) >> >> endobj 1328 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [382.094 489.803 394.049 500.707] /Subtype /Link /A << /S /GoTo /D (page.60) >> >> endobj 1329 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [405.446 477.848 417.401 488.752] /Subtype /Link /A << /S /GoTo /D (page.66) >> >> endobj 1330 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 454.311 399.977 464.842] /Subtype /Link /A << /S /GoTo /D (page.76) >> >> endobj 1331 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 430.027 442.109 440.931] /Subtype /Link /A << /S /GoTo /D (page.56) >> >> endobj 1332 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 406.117 442.109 417.021] /Subtype /Link /A << /S /GoTo /D (page.56) >> >> endobj 1333 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 356.078 399.977 366.609] /Subtype /Link /A << /S /GoTo /D (page.40) >> >> endobj 1334 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [433.471 331.794 445.426 342.698] /Subtype /Link /A << /S /GoTo /D (page.65) >> >> endobj 1335 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [433.471 307.884 445.426 318.788] /Subtype /Link /A << /S /GoTo /D (page.65) >> >> endobj 1336 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 284.347 399.977 294.878] /Subtype /Link /A << /S /GoTo /D (page.60) >> >> endobj 1337 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 250.166 361.791 259.012] /Subtype /Link /A << /S /GoTo /D (page.60) >> >> endobj 1338 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 224.572 399.977 235.102] /Subtype /Link /A << /S /GoTo /D (page.60) >> >> endobj 1339 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 200.661 399.977 211.192] /Subtype /Link /A << /S /GoTo /D (page.62) >> >> endobj 1340 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 176.751 399.977 187.281] /Subtype /Link /A << /S /GoTo /D (page.82) >> >> endobj 1341 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 152.84 399.977 163.371] /Subtype /Link /A << /S /GoTo /D (page.84) >> >> endobj 1342 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 128.93 399.977 139.46] /Subtype /Link /A << /S /GoTo /D (page.81) >> >> endobj 1343 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 105.02 399.977 115.55] /Subtype /Link /A << /S /GoTo /D (page.80) >> >> endobj 1344 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 81.109 399.977 91.64] /Subtype /Link /A << /S /GoTo /D (page.72) >> >> endobj 1349 0 obj << /D [1347 0 R /XYZ 72 744.907 null] >> endobj 1346 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F75 1122 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1411 0 obj << /Length 1513 /Filter /FlateDecode >> stream xÚÕZKw›8ÞûW°4çÔª$@ËIÒ´é™4ÇMÚ%åƒ ¸“üû‘6`ƒ ±‰ÉÂ}ß½W÷^= ö¨Aíãèl6ziPF1ÕfšAL€,¦Y¦ Lfh3O»ß>Ï#áx™èlÙã‹È]Îy˜:©…ïTÛ”ÜI¸ª õï³Ï£³ÑïA iß5-Íî¿CÍíŸ5 fkÿeOÍ5“Ú¢ ´»Ñ?#XÊJ@å·аÆGžþ«#„ÆN°äÉ7H ø!}‚ËÚBGp\â<'u’4Š9p£0å:"ã§\¯¸ yñGÞàçÅ#’–f0`¤MŒ…`ÎÓŸ‘'{}—Q‡C[r³LñoÚT‹™­ÆéÇ‘v?ÁŽÜ콜³6©ëåžòê‡Ð¸È/N@Ñè—¢hŒ¹ç'`*ÿÛó£o„_ÌçQÊ;Yœ@}¸_zêÿL˜f1¥±ž‘û«?Z&{ Z%×Â/…óô`9úVÉ-œ8ᯠ5 F‰UOÌÃ(~­ŽËpuÊ€@«Þäý¥E4däX†À5aŒELŪÓOò•X .v™zâ§z/DA0«EÂcE6Ö ƒä9tKzžJ#NÒOÙ×âþ„`ÑîB¸Ú+„« ÈÔ÷¨a—ËЕ™LÎIÏ:–2q'ITÕU¹%¦…·<„ÞFЇUñ•ìï‚»‘'$Ý \Õœ±ÅÖ8/cg~:˜¸&Ý‚Ù8œOÜØ)ßj?Fe™ æžôê+uN¹ã=—ÂF¬«ûÌÍ¢?gÙ&7i cÈQá3iºbšÆN˜8™Û(yÅ¿×÷W#Àfè^¶k‚à™:ñózŒÛð óžms §érE‘&QƒhüîdV&¾ÁÓµÙQµ[lèJ2ï ØAýZ.n½å¹ôÊ…`0¦¸Þå^H¤§Ï:ùËyà‹¤{EûóCm”ÜÈ¡–lîÖÉY##:F¶ÕÕp›£H‘QÑh7¢G+£½Þk´¥¸à "‰L3â(è–bx\ÇdüÇw ó„í ó ¬ Ì’³9çFÛ,5aM€ÙH£¢\¯ý5E'BY¾¬Jø²<:ü:s¶Û¬à•ƒº”£:è“U`Š„B2Í[ ÜìuàÈyÚÑ(˜tZOáÚÊÌÄFë±xY6w£ùüˆ 0_¢€Û8J#WŽRÑA™€¬J²‚ô·ñ+÷¢Ö]^ßÇPØÒÇ\¬{»òä:Ø7±›m%áb«"sL•ÌEó±Q×™2a$þAÅà°ã¬QáÎ_=hÖh·ù‡'—/”„»•sâüÕƒ€š]&Z…)à]ݼIÞ^|}UÂ+/[ ‡CÀF°­5E–YÉ DÒ© Öe5Ë¥G V·@0åÉBXä*ͯO_¶µ„º=š ¢ —‹ºÊi²»&8´:ÁÙØ>é{{§L+”T¢ÓMþy‚ób³Îá’Öò—@‹Yn7°Ç‹¹i[íAçÝV”)Üp#å~²q‡yqí™…ÞNÐfz•IŠQ¤Èj–²±ýŠ9nºlÙÝÆBó€e’ŠÂd=S˜¼¬Â¶n-õ«âÀˆe¨)±…_gJl±n·±™<ÁU½Fù¬RÞx‘fõÀ­ö¡K”R']vLn…ñ$©sØTÁlëÄfîâ IJ˜¨cdTp"5Q—Íýy {is[>gàD3Ú2QŸé>àÚ ÇlWIJìJ$ëY$/:ŒÁ~LfÏøûê½ó( ó&”T×1Í|“ ;À ÃNàV;$ù©Á#n«“ÝGw`v^eu¾öËÞ„/Qz5_\ž¹å^y ªPeÝ_Ýè0ûZŸhÞI15Lâf¯nä’ÙC¡¡æ êc­“¦±¯ÚSÞ_½ ‡‡qUnÆ&&À´õYìíÃââ&­N]…ž²¢ÌØLBÃX¼žÝdUÏ' üJp endstream endobj 1410 0 obj << /Type /Page /Contents 1411 0 R /Resources 1409 0 R /MediaBox [0 0 612 792] /Parent 1292 0 R /Annots [ 1345 0 R 1350 0 R 1351 0 R 1352 0 R 1353 0 R 1354 0 R 1355 0 R 1356 0 R 1357 0 R 1358 0 R 1359 0 R 1360 0 R 1361 0 R 1362 0 R 1363 0 R 1364 0 R 1365 0 R 1366 0 R 1367 0 R 1368 0 R 1369 0 R 1370 0 R 1371 0 R 1372 0 R 1373 0 R 1374 0 R 1375 0 R 1376 0 R 1377 0 R 1378 0 R 1379 0 R 1380 0 R 1381 0 R 1382 0 R 1383 0 R 1384 0 R 1385 0 R 1386 0 R 1387 0 R 1388 0 R 1389 0 R 1390 0 R 1391 0 R 1392 0 R 1393 0 R 1394 0 R 1395 0 R 1396 0 R 1397 0 R 1398 0 R 1399 0 R 1400 0 R 1401 0 R 1402 0 R 1403 0 R 1404 0 R 1405 0 R 1406 0 R 1407 0 R ] >> endobj 1345 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 695.303 160.996 705.833] /Subtype /Link /A << /S /GoTo /D (page.51) >> >> endobj 1350 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 671.392 160.996 681.923] /Subtype /Link /A << /S /GoTo /D (page.53) >> >> endobj 1351 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 647.482 160.996 658.012] /Subtype /Link /A << /S /GoTo /D (page.53) >> >> endobj 1352 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 623.572 160.996 634.102] /Subtype /Link /A << /S /GoTo /D (page.52) >> >> endobj 1353 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 599.661 160.996 610.192] /Subtype /Link /A << /S /GoTo /D (page.49) >> >> endobj 1354 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 575.751 160.996 586.281] /Subtype /Link /A << /S /GoTo /D (page.49) >> >> endobj 1355 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 551.841 160.996 562.371] /Subtype /Link /A << /S /GoTo /D (page.50) >> >> endobj 1356 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 527.93 160.996 538.461] /Subtype /Link /A << /S /GoTo /D (page.63) >> >> endobj 1357 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 478.483 160.996 489.014] /Subtype /Link /A << /S /GoTo /D (page.76) >> >> endobj 1358 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [268.641 440.618 280.596 451.522] /Subtype /Link /A << /S /GoTo /D (page.71) >> >> endobj 1359 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [269.059 428.663 281.014 439.567] /Subtype /Link /A << /S /GoTo /D (page.61) >> >> endobj 1360 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [264.088 416.708 276.043 427.612] /Subtype /Link /A << /S /GoTo /D (page.62) >> >> endobj 1361 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [288.506 404.752 300.461 415.656] /Subtype /Link /A << /S /GoTo /D (page.63) >> >> endobj 1362 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [153.623 393.171 165.578 403.701] /Subtype /Link /A << /S /GoTo /D (page.61) >> >> endobj 1363 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 369.261 160.996 379.791] /Subtype /Link /A << /S /GoTo /D (page.62) >> >> endobj 1364 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 335.079 122.809 343.925] /Subtype /Link /A << /S /GoTo /D (page.82) >> >> endobj 1365 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 299.213 122.809 308.06] /Subtype /Link /A << /S /GoTo /D (page.84) >> >> endobj 1366 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 263.348 122.809 272.194] /Subtype /Link /A << /S /GoTo /D (page.81) >> >> endobj 1367 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 227.482 122.809 236.329] /Subtype /Link /A << /S /GoTo /D (page.80) >> >> endobj 1368 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 177.397 160.996 187.928] /Subtype /Link /A << /S /GoTo /D (page.61) >> >> endobj 1369 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 153.487 160.996 164.017] /Subtype /Link /A << /S /GoTo /D (page.61) >> >> endobj 1370 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 119.305 122.809 128.152] /Subtype /Link /A << /S /GoTo /D (page.62) >> >> endobj 1371 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [277.786 80.802 289.741 91.706] /Subtype /Link /A << /S /GoTo /D (page.59) >> >> endobj 1372 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [286.892 68.847 298.847 79.751] /Subtype /Link /A << /S /GoTo /D (page.82) >> >> endobj 1373 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 696.986 361.791 705.833] /Subtype /Link /A << /S /GoTo /D (page.83) >> >> endobj 1374 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 673.076 361.791 681.923] /Subtype /Link /A << /S /GoTo /D (page.46) >> >> endobj 1375 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 649.166 361.791 658.012] /Subtype /Link /A << /S /GoTo /D (page.43) >> >> endobj 1376 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 625.255 361.791 634.102] /Subtype /Link /A << /S /GoTo /D (page.46) >> >> endobj 1377 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [512.354 611.243 524.31 622.147] /Subtype /Link /A << /S /GoTo /D (page.60) >> >> endobj 1378 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 589.27 361.791 598.237] /Subtype /Link /A << /S /GoTo /D (page.59) >> >> endobj 1379 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [480.504 575.377 492.459 586.281] /Subtype /Link /A << /S /GoTo /D (page.68) >> >> endobj 1380 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [515.214 563.422 527.169 574.326] /Subtype /Link /A << /S /GoTo /D (page.64) >> >> endobj 1381 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [491.573 551.467 503.528 562.371] /Subtype /Link /A << /S /GoTo /D (page.71) >> >> endobj 1382 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [525.724 539.512 537.679 550.416] /Subtype /Link /A << /S /GoTo /D (page.64) >> >> endobj 1383 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [467.224 527.557 479.179 538.461] /Subtype /Link /A << /S /GoTo /D (page.70) >> >> endobj 1384 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [479.398 515.602 491.354 526.506] /Subtype /Link /A << /S /GoTo /D (page.70) >> >> endobj 1385 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 493.749 361.791 502.595] /Subtype /Link /A << /S /GoTo /D (page.76) >> >> endobj 1386 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [485.486 479.736 497.441 490.64] /Subtype /Link /A << /S /GoTo /D (page.71) >> >> endobj 1387 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [519.238 467.781 531.193 478.685] /Subtype /Link /A << /S /GoTo /D (page.80) >> >> endobj 1388 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [432.086 444.244 444.041 454.774] /Subtype /Link /A << /S /GoTo /D (page.49) >> >> endobj 1389 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [522.844 431.915 534.8 442.819] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1390 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [525.844 419.96 537.799 430.864] /Subtype /Link /A << /S /GoTo /D (page.77) >> >> endobj 1391 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [441.899 396.423 453.855 406.954] /Subtype /Link /A << /S /GoTo /D (page.51) >> >> endobj 1392 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 374.197 361.791 383.043] /Subtype /Link /A << /S /GoTo /D (page.78) >> >> endobj 1393 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [441.899 348.603 453.855 359.133] /Subtype /Link /A << /S /GoTo /D (page.51) >> >> endobj 1394 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 326.257 361.791 335.223] /Subtype /Link /A << /S /GoTo /D (page.79) >> >> endobj 1395 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [432.086 300.782 444.041 311.312] /Subtype /Link /A << /S /GoTo /D (page.50) >> >> endobj 1396 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [493.794 288.453 505.749 299.357] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1397 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [515.094 276.498 527.049 287.402] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1398 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [529.041 264.543 540.996 275.447] /Subtype /Link /A << /S /GoTo /D (page.78) >> >> endobj 1399 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [518.093 252.588 530.048 263.492] /Subtype /Link /A << /S /GoTo /D (page.76) >> >> endobj 1400 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [417.152 228.678 429.108 239.581] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1401 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [516.758 216.722 528.713 227.626] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1402 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 194.869 361.791 203.716] /Subtype /Link /A << /S /GoTo /D (page.78) >> >> endobj 1403 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [519.757 180.857 531.712 191.761] /Subtype /Link /A << /S /GoTo /D (page.77) >> >> endobj 1404 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [391.06 169.275 403.015 179.806] /Subtype /Link /A << /S /GoTo /D (page.53) >> >> endobj 1405 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [416.724 119.226 428.679 130.13] /Subtype /Link /A << /S /GoTo /D (page.64) >> >> endobj 1406 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [513.261 80.802 525.216 91.706] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1407 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [511.856 68.847 523.811 79.751] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1412 0 obj << /D [1410 0 R /XYZ 72 744.907 null] >> endobj 1409 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F75 1122 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1477 0 obj << /Length 1376 /Filter /FlateDecode >> stream xÚÝšMsÛ*†÷þ,™F ZÞ4Mo:Ó™\Ç]¥Œ"Q—©-¹n›Aȶ$cÇÓM¢/£÷áÁ@ð¡w5ì½½ (ˆ¼ˆú ¿Ð!fŽ0LÁCÿîy’§O>ìÏÊ‹K?dýë<™Mx&c)òì¹6àc—Üœ ^|~ì½ö~özÈ”‹½† ™ô¾Bªëô‚ˆßÕS€)SÿÇà¾÷_¶4Fz0Æ\~çÅã„—e<Òì*á³1WGèM¥z>dú…!V1£ PoX¹8øÐU!›ßÕBÞÞ„ ì˜úZ—~àswúáŽRõˆúA™'¦qòãñIÈR"i‰ZÜR°È̱nާöx3)ÆB ^ÆH ¬’‰¼ˆ9 "ý¸ˆ'\òâýŸ„OµÕKÉ8.Ks:—¼"“ë2æ?-Ý™°A«ÏK}+b%AÆ™,½kþMߌgcYÄR¹a”‡4nÕ9Mg[f·¿,ŸÎƱäÿò8åŲ¡!Ÿ°]E—º D#¨.„šBæ•'‹8+ãDûè}ZÔé@Înt )T©ºPÕV¾çé‹:âj³]¶Äu¼TV. £ˆ®o"SmÙxŸðÒ»]’Ô+ÕP¿é°ºûð Ÿ1æÆ±ÊDˆ¹g,B.- ŽŽHöA¼Y\<Ï}]2âó°ñL;žCïóä‡.ƒË…“‘S'áœwy!Ï4¢ÔÍdZäj$+ þsÆË–"[+J^üÒ¡TU7!}¯|Î’†%Ãdz¿|àÑÐwhHH÷ix'Äýœ¶q $.qÃm¸mwo³$Ÿ¨É¢™æÌûE#ÞauÝ’}g(¼ Ó¡“ûû{ àÓÅCjO üúÑñÔÄGˆ•Ô&Vžš÷,"§¢–y’U£(Ø>Ç‚gÒ‹;ãî»êò©'™ÏëÁYŒ¥ûÅ5‹üõ@xÒ[F“SÂ…Ì®G‘n™ï¨åkçîú³Û©ÎŽõì]­ñ6=óE~G“Ë?²z³÷$¤š*ÅéÒbAÔ’Òýü€yC€Äcl›Àß…ü ±ee5‡#—2è‹d¨h1i¦Û×¼H‡ëÚ Û—3û³K vý×Kc—j¬æZ49MéPA/Õ£žLÊÝ™˜à¥bÔÅ‚§¢< 1ŸäÒi½øv w)¥úë²ù2ËJá>éÿ‰Óêˆ,Eˆxt„1ž`;¦*ªI˜Û1…2[!Í‘ãmWíR§ª©¨CЮ·|Ðc@-ÇúvZÄ¥ËAþDÛ™;uœv˪ûö¢ê'#Q*WŽ5“ ý4kòb»´nééRŽåð¶&³êR唺‘'q©Â2.öìj`V' v~œPu±$㲑ZàÖüY©¬$p>‰?";Þ†*öÈÙT8jJš\õR‘Mg² )òmsnûBÓò’ï÷|œŠlôاÔQ°¿ºiråí&dÿæú-Å|%d90û`Wf H)m}šCih¾ÍÑ7,лi#wi¢]4åT͆yƒiÐÂQƒšÁÑ7,¶Ð_çCkwôVfMunœ©n¼Ž3Ú¡kôfÆ-[ôye‹>8Gºd¹®cÁm þ¨×òGªgîO—in a˨꼂Ò¯…ö„Z|px/c9k8EiAéó JX@µÖW×S›Þ ÑÂ'ÌP 33ÎU7^‰¬Míw¿ä%ØóéÞò®ýÈûB¾¡ˆªI=ÀúšªßUWo³Ô¤JZ’””ÿ0(Ì endstream endobj 1476 0 obj << /Type /Page /Contents 1477 0 R /Resources 1475 0 R /MediaBox [0 0 612 792] /Parent 1292 0 R /Annots [ 1408 0 R 1413 0 R 1414 0 R 1415 0 R 1416 0 R 1417 0 R 1418 0 R 1419 0 R 1420 0 R 1421 0 R 1422 0 R 1423 0 R 1424 0 R 1425 0 R 1426 0 R 1427 0 R 1428 0 R 1429 0 R 1430 0 R 1431 0 R 1432 0 R 1433 0 R 1434 0 R 1435 0 R 1436 0 R 1437 0 R 1438 0 R 1439 0 R 1440 0 R 1441 0 R 1442 0 R 1443 0 R 1444 0 R 1445 0 R 1446 0 R 1447 0 R 1448 0 R 1449 0 R 1450 0 R 1451 0 R 1452 0 R 1453 0 R 1454 0 R 1455 0 R 1456 0 R 1457 0 R 1458 0 R 1459 0 R 1460 0 R 1461 0 R 1462 0 R 1463 0 R 1464 0 R 1465 0 R 1466 0 R 1467 0 R 1468 0 R 1469 0 R 1470 0 R 1471 0 R 1472 0 R 1473 0 R ] >> endobj 1408 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [174.206 706.884 186.161 717.788] /Subtype /Link /A << /S /GoTo /D (page.64) >> >> endobj 1413 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [264.974 668.757 276.929 679.661] /Subtype /Link /A << /S /GoTo /D (page.85) >> >> endobj 1414 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [284.013 656.802 295.968 667.706] /Subtype /Link /A << /S /GoTo /D (page.64) >> >> endobj 1415 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [261.188 644.847 273.144 655.751] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1416 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [129.942 632.892 141.898 643.796] /Subtype /Link /A << /S /GoTo /D (page.70) >> >> endobj 1417 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 599.083 122.809 607.93] /Subtype /Link /A << /S /GoTo /D (page.81) >> >> endobj 1418 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 573.489 160.996 584.02] /Subtype /Link /A << /S /GoTo /D (page.62) >> >> endobj 1419 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 539.308 122.809 548.154] /Subtype /Link /A << /S /GoTo /D (page.83) >> >> endobj 1420 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 503.442 122.809 512.289] /Subtype /Link /A << /S /GoTo /D (page.84) >> >> endobj 1421 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 467.577 122.809 476.423] /Subtype /Link /A << /S /GoTo /D (page.81) >> >> endobj 1422 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 431.711 122.809 440.558] /Subtype /Link /A << /S /GoTo /D (page.80) >> >> endobj 1423 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [254.145 417.699 266.1 428.603] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1424 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 383.89 122.809 392.737] /Subtype /Link /A << /S /GoTo /D (page.76) >> >> endobj 1425 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 348.025 122.809 356.603] /Subtype /Link /A << /S /GoTo /D (page.77) >> >> endobj 1426 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 312.159 122.809 321.006] /Subtype /Link /A << /S /GoTo /D (page.63) >> >> endobj 1427 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 276.294 122.809 285.141] /Subtype /Link /A << /S /GoTo /D (page.83) >> >> endobj 1428 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 240.428 122.809 249.275] /Subtype /Link /A << /S /GoTo /D (page.84) >> >> endobj 1429 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 204.563 122.809 213.41] /Subtype /Link /A << /S /GoTo /D (page.81) >> >> endobj 1430 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 168.697 122.809 177.544] /Subtype /Link /A << /S /GoTo /D (page.80) >> >> endobj 1431 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [152.159 143.103 164.114 153.634] /Subtype /Link /A << /S /GoTo /D (page.46) >> >> endobj 1432 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [152.159 119.193 164.114 129.723] /Subtype /Link /A << /S /GoTo /D (page.78) >> >> endobj 1433 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [280.068 106.864 292.023 117.768] /Subtype /Link /A << /S /GoTo /D (page.70) >> >> endobj 1434 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [279.599 94.909 291.554 105.813] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1435 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [156.214 82.954 168.169 93.858] /Subtype /Link /A << /S /GoTo /D (page.71) >> >> endobj 1436 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [230.086 70.999 242.041 81.903] /Subtype /Link /A << /S /GoTo /D (page.39) >> >> endobj 1437 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [472.394 706.884 484.35 717.788] /Subtype /Link /A << /S /GoTo /D (page.41) >> >> endobj 1438 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [444.998 694.929 456.953 705.833] /Subtype /Link /A << /S /GoTo /D (page.46) >> >> endobj 1439 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [457.182 682.974 469.137 693.878] /Subtype /Link /A << /S /GoTo /D (page.43) >> >> endobj 1440 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [440.574 671.019 452.529 681.923] /Subtype /Link /A << /S /GoTo /D (page.45) >> >> endobj 1441 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [434.766 659.064 446.721 669.968] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1442 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [465.321 647.108 477.276 658.012] /Subtype /Link /A << /S /GoTo /D (page.51) >> >> endobj 1443 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [470.442 635.153 482.397 646.057] /Subtype /Link /A << /S /GoTo /D (page.53) >> >> endobj 1444 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [473.221 623.198 485.177 634.102] /Subtype /Link /A << /S /GoTo /D (page.53) >> >> endobj 1445 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [463.807 611.243 475.762 622.147] /Subtype /Link /A << /S /GoTo /D (page.52) >> >> endobj 1446 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [455.508 599.288 467.463 610.192] /Subtype /Link /A << /S /GoTo /D (page.48) >> >> endobj 1447 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [423.439 587.333 435.394 598.237] /Subtype /Link /A << /S /GoTo /D (page.59) >> >> endobj 1448 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [453.575 575.377 465.531 586.281] /Subtype /Link /A << /S /GoTo /D (page.54) >> >> endobj 1449 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [422.741 563.422 434.697 574.326] /Subtype /Link /A << /S /GoTo /D (page.68) >> >> endobj 1450 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [440.146 551.467 452.101 562.371] /Subtype /Link /A << /S /GoTo /D (page.64) >> >> endobj 1451 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [425.8 539.512 437.755 550.416] /Subtype /Link /A << /S /GoTo /D (page.60) >> >> endobj 1452 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [449.152 527.557 461.107 538.461] /Subtype /Link /A << /S /GoTo /D (page.67) >> >> endobj 1453 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [436.31 515.602 448.265 526.506] /Subtype /Link /A << /S /GoTo /D (page.61) >> >> endobj 1454 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [456.893 503.646 468.848 514.55] /Subtype /Link /A << /S /GoTo /D (page.64) >> >> endobj 1455 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [412.63 491.691 424.585 502.595] /Subtype /Link /A << /S /GoTo /D (page.70) >> >> endobj 1456 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [488.275 479.736 500.23 490.64] /Subtype /Link /A << /S /GoTo /D (page.72) >> >> endobj 1457 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [491.602 467.781 503.557 478.685] /Subtype /Link /A << /S /GoTo /D (page.74) >> >> endobj 1458 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [446.502 455.826 458.457 466.73] /Subtype /Link /A << /S /GoTo /D (page.78) >> >> endobj 1459 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [442.079 443.871 454.034 454.774] /Subtype /Link /A << /S /GoTo /D (page.76) >> >> endobj 1460 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [441.401 431.915 453.356 442.819] /Subtype /Link /A << /S /GoTo /D (page.79) >> >> endobj 1461 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [427.583 419.96 439.538 430.864] /Subtype /Link /A << /S /GoTo /D (page.84) >> >> endobj 1462 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 370.252 399.977 380.782] /Subtype /Link /A << /S /GoTo /D (page.43) >> >> endobj 1463 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 334.386 399.977 344.916] /Subtype /Link /A << /S /GoTo /D (page.43) >> >> endobj 1464 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 298.521 399.977 309.051] /Subtype /Link /A << /S /GoTo /D (page.43) >> >> endobj 1465 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 262.655 399.977 273.185] /Subtype /Link /A << /S /GoTo /D (page.43) >> >> endobj 1466 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [445.645 238.371 457.601 249.275] /Subtype /Link /A << /S /GoTo /D (page.39) >> >> endobj 1467 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [445.645 214.461 457.601 225.365] /Subtype /Link /A << /S /GoTo /D (page.39) >> >> endobj 1468 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [445.645 190.55 457.601 201.454] /Subtype /Link /A << /S /GoTo /D (page.40) >> >> endobj 1469 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [445.645 166.64 457.601 177.544] /Subtype /Link /A << /S /GoTo /D (page.40) >> >> endobj 1470 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [445.645 142.73 457.601 153.634] /Subtype /Link /A << /S /GoTo /D (page.40) >> >> endobj 1471 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [445.645 118.819 457.601 129.723] /Subtype /Link /A << /S /GoTo /D (page.41) >> >> endobj 1472 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [433.471 94.909 445.426 105.813] /Subtype /Link /A << /S /GoTo /D (page.64) >> >> endobj 1473 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [433.471 70.999 445.426 81.903] /Subtype /Link /A << /S /GoTo /D (page.65) >> >> endobj 1478 0 obj << /D [1476 0 R /XYZ 72 744.907 null] >> endobj 1475 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F75 1122 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1537 0 obj << /Length 1689 /Filter /FlateDecode >> stream xÚÕšYs›8Àßý)x´gjª =6W›îd»3³m'C±’2‹Á±MöÓ¯„ÀFľj'1âÒÿÇÿÔœG8ï{gw½·W˜:ÜåQçîÁÁq!ã#¾K8vîÆÎ—þ_Ï“tü}€@¿ÈCÄüþE‘È@FiòÆœ»±raЃow{—w½Ÿ=¨:tRï%.Ì '½/߀3Vç?:ÀÅÜw~•wMB}õ;£Þç°å Aõ» pfFÆ[Œ¯¢‡ôs! q+~"—J‚hÿ+ð@y^µ£¤:˜ èk¶¡–ÕÁÜõ=è !t¹ç™×VÔîWP,î'"σG¡Þß”|ÀEÀ׌¨ÿħN¦$~qòö}Ïù2DèSfž«Àœá¢^[0ù4MÊ«þ,„°¡ÑNšæC£äñV  ×Œr)²|¦%è)Ž—j“’«¼°Š‹Ú\Yãõ÷™êvˆ í„Xëb%b“Cnõ… twFÜ‘ñ:™r‰9´ŒSµq–^3àL… p‹ßê Ç6Ó®„‹´wVÆmÈ©okV|úàØDÛñåU€Œ[Ô«¨.¼@Ýׯ,’â¾)B®™t׊"lŒt{ ãHår7L'“4qof¹þ¼<=©o¤ßla&BþHÇ; ÒQ§kä›"–Ñ4‹ÃOË7k×\ç™§{VQÖ1È^S75¦þ=6æJeÒ9æó ¼ß²Ñ™%*J$2oXáHU™eÝ© ¤Ì"s^î",iÕ- ­ %H"B™w’õB<è‹RÝï–så£8Ðý«U*ÎÓD–Ê}R>³R²jÃÒ\Ê ØË8A.ÓL¸êöLwº“‡;º%:Wˆ¥ˆ÷qm«–&è@²RÁ™˜¤rŸýyhAéþ”ù‹h€¼Ê¦.ÍO"çrÔÕ,Ç^£„­FÌß œd—A†ßAæ‘HƯCХʓ²]J­¶rWc?{´—…Q]÷¢! Ó¼AÔC‹cçØ|ó(@ÿ. •éO’¥ñRÈ«wÊÇÓ4“–®®ÇóijXUMÑ5Ö–¦RùC—=ŒÃéöL³Ù¹=WRP˃“ƒšÙ^.d£þåK²õÜ'¹Ï5«Œeny8;„òà¦xúè»î¸ÕÁÈðæd*V¬#«kwuÙs8,²¹=bÆÖa™í®É‡£B1Có_»ç>¦-ò,NÃGCø¦ÇéYªÊO†C=Ëvz‘Q°•÷,±³H™Pf†'*»º×ÇŒxÔŽ í_rîBìPŒ]ŠHô,êb|ß#i \=ÚTÚ³@‡3=Ð)«1c&ÏÓɤH¢°\?É?MõϬºÀ¾5 ‡}“†õéMŠÀ(xÜCö¼NÃúTu}ÁªPXÏаj†F6XË™š>ù;°fÊR£xqòs²È’3˜_ÿ2ËÒì<-9Ÿ!ƒÖRƒ¬_Íë-b ÷ôáY'{ZÇT° ¥›'ÕVÔåS(¦K”¤jm»u ~ ßß«ÖÅÖxœ"Óôé#8ÿF6xcú³õ!²×‚ 6š*/AUtUÙHµŽ¸½VΑ™[æ›­”ÿ~Û»P=&i.£°9i>W`¶ªt»T•>85UÑ.dµÆ|f­øÌlnЧOÚ?"{Ö£y܃ԪÊvùôÁ©•=Ë`f+ÅœâÖJ*©WRñéqšÆè ü#È‚PYÝ's*ËŠ¤•²¸U®"Î’‚ýÎ ÛqV»Z ÇËØ–Î0Ÿñ¹•¾°w£èvi ú`ƒ¥ãÓ!›ùäÔÎÌœU™™%‘m‰¶°ìàö-¯fh9ä§¢WÍ\ zöðÄä2}úö¸%ÚŸìõßýÑr6l§é²m¶~'Qûû€›ûƒ¶¿1Tùƒ¯‰.Õë# ©Å‡íª#M𚪘ž\Þ¤«L³•0ûÖ%#Æ“LWY0£è¿æJc5ASg`³N¾$2Š#‰]–z}»Ø}{Å<Šôîdà v)­6'ôÍ­ Ìz‡¸Œ›;r‘ŒSœ˜.Y8ÍEV)Ïë»ùs6æ¦+½~’q¬î8ØT'ë2„™‰zÿf5ö¬ÉI]»[­em«?ÁHdQæ÷ }N^W’··§{ÄE´óîô—ÛçÕ+r!DFäëdl&ÕK(âQ—#¤//rÖå‰Ô"¼ endstream endobj 1536 0 obj << /Type /Page /Contents 1537 0 R /Resources 1535 0 R /MediaBox [0 0 612 792] /Parent 1292 0 R /Annots [ 1474 0 R 1479 0 R 1480 0 R 1481 0 R 1482 0 R 1483 0 R 1484 0 R 1485 0 R 1486 0 R 1487 0 R 1488 0 R 1489 0 R 1490 0 R 1491 0 R 1492 0 R 1493 0 R 1494 0 R 1495 0 R 1496 0 R 1497 0 R 1498 0 R 1499 0 R 1500 0 R 1501 0 R 1502 0 R 1503 0 R 1504 0 R 1505 0 R 1506 0 R 1507 0 R 1508 0 R 1509 0 R 1510 0 R 1511 0 R 1512 0 R 1513 0 R 1514 0 R 1515 0 R 1516 0 R 1517 0 R 1518 0 R 1519 0 R 1520 0 R 1521 0 R 1522 0 R 1523 0 R 1524 0 R 1525 0 R 1526 0 R 1527 0 R 1528 0 R 1529 0 R 1530 0 R 1531 0 R 1532 0 R 1533 0 R ] >> endobj 1474 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [186.749 694.929 198.704 705.833] /Subtype /Link /A << /S /GoTo /D (page.67) >> >> endobj 1479 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [186.749 671.019 198.704 681.923] /Subtype /Link /A << /S /GoTo /D (page.67) >> >> endobj 1480 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [225.872 647.108 237.827 658.012] /Subtype /Link /A << /S /GoTo /D (page.72) >> >> endobj 1481 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [225.872 623.198 237.827 634.102] /Subtype /Link /A << /S /GoTo /D (page.73) >> >> endobj 1482 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [225.872 599.288 237.827 610.192] /Subtype /Link /A << /S /GoTo /D (page.73) >> >> endobj 1483 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [225.872 575.377 237.827 586.281] /Subtype /Link /A << /S /GoTo /D (page.73) >> >> endobj 1484 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [225.872 551.467 237.827 562.371] /Subtype /Link /A << /S /GoTo /D (page.72) >> >> endobj 1485 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [225.872 527.557 237.827 538.461] /Subtype /Link /A << /S /GoTo /D (page.72) >> >> endobj 1486 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 492.065 160.996 502.595] /Subtype /Link /A << /S /GoTo /D (page.44) >> >> endobj 1487 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [225.872 467.781 237.827 478.685] /Subtype /Link /A << /S /GoTo /D (page.73) >> >> endobj 1488 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [225.872 443.871 237.827 454.774] /Subtype /Link /A << /S /GoTo /D (page.74) >> >> endobj 1489 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [286.155 431.915 298.11 442.819] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1490 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [283.465 419.96 295.42 430.864] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1491 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [210.818 396.423 222.774 406.954] /Subtype /Link /A << /S /GoTo /D (page.53) >> >> endobj 1492 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [205.588 384.095 217.543 394.999] /Subtype /Link /A << /S /GoTo /D (page.72) >> >> endobj 1493 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [208.915 372.14 220.87 383.043] /Subtype /Link /A << /S /GoTo /D (page.74) >> >> endobj 1494 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [142.664 360.558 154.619 371.088] /Subtype /Link /A << /S /GoTo /D (page.52) >> >> endobj 1495 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [271.749 348.229 283.704 359.133] /Subtype /Link /A << /S /GoTo /D (page.68) >> >> endobj 1496 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [259.983 336.274 271.939 347.178] /Subtype /Link /A << /S /GoTo /D (page.68) >> >> endobj 1497 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [201.404 312.737 213.359 323.268] /Subtype /Link /A << /S /GoTo /D (page.52) >> >> endobj 1498 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 288.827 160.996 299.357] /Subtype /Link /A << /S /GoTo /D (page.59) >> >> endobj 1499 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [194.49 264.543 206.445 275.447] /Subtype /Link /A << /S /GoTo /D (page.66) >> >> endobj 1500 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [194.49 240.633 206.445 251.537] /Subtype /Link /A << /S /GoTo /D (page.66) >> >> endobj 1501 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 217.096 160.996 227.626] /Subtype /Link /A << /S /GoTo /D (page.51) >> >> endobj 1502 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 193.186 160.996 203.716] /Subtype /Link /A << /S /GoTo /D (page.53) >> >> endobj 1503 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 169.275 160.996 179.806] /Subtype /Link /A << /S /GoTo /D (page.54) >> >> endobj 1504 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 145.365 160.996 155.895] /Subtype /Link /A << /S /GoTo /D (page.52) >> >> endobj 1505 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 121.455 160.996 131.985] /Subtype /Link /A << /S /GoTo /D (page.49) >> >> endobj 1506 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 99.228 122.809 108.075] /Subtype /Link /A << /S /GoTo /D (page.60) >> >> endobj 1507 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 73.634 160.996 84.164] /Subtype /Link /A << /S /GoTo /D (page.63) >> >> endobj 1508 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 695.303 399.977 705.833] /Subtype /Link /A << /S /GoTo /D (page.40) >> >> endobj 1509 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 671.019 442.109 681.923] /Subtype /Link /A << /S /GoTo /D (page.55) >> >> endobj 1510 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [447.867 647.108 459.822 658.012] /Subtype /Link /A << /S /GoTo /D (page.55) >> >> endobj 1511 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [504.742 635.153 516.697 646.057] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1512 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [457.73 611.243 469.685 622.147] /Subtype /Link /A << /S /GoTo /D (page.57) >> >> endobj 1513 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [457.73 587.333 469.685 598.237] /Subtype /Link /A << /S /GoTo /D (page.57) >> >> endobj 1514 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 563.422 442.109 574.326] /Subtype /Link /A << /S /GoTo /D (page.57) >> >> endobj 1515 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [447.867 539.512 459.822 550.416] /Subtype /Link /A << /S /GoTo /D (page.57) >> >> endobj 1516 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 515.602 442.109 526.506] /Subtype /Link /A << /S /GoTo /D (page.56) >> >> endobj 1517 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 491.691 442.109 502.595] /Subtype /Link /A << /S /GoTo /D (page.57) >> >> endobj 1518 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 467.781 442.109 478.685] /Subtype /Link /A << /S /GoTo /D (page.56) >> >> endobj 1519 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 443.871 442.109 454.774] /Subtype /Link /A << /S /GoTo /D (page.56) >> >> endobj 1520 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 419.96 442.109 430.864] /Subtype /Link /A << /S /GoTo /D (page.55) >> >> endobj 1521 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 396.05 442.109 406.954] /Subtype /Link /A << /S /GoTo /D (page.55) >> >> endobj 1522 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [457.73 372.14 469.685 383.043] /Subtype /Link /A << /S /GoTo /D (page.58) >> >> endobj 1523 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [467.972 348.229 479.927 359.133] /Subtype /Link /A << /S /GoTo /D (page.58) >> >> endobj 1524 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 324.319 442.109 335.223] /Subtype /Link /A << /S /GoTo /D (page.58) >> >> endobj 1525 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 300.409 442.109 311.312] /Subtype /Link /A << /S /GoTo /D (page.58) >> >> endobj 1526 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 276.498 442.109 287.402] /Subtype /Link /A << /S /GoTo /D (page.57) >> >> endobj 1527 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 252.588 442.109 263.492] /Subtype /Link /A << /S /GoTo /D (page.57) >> >> endobj 1528 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 228.678 442.109 239.581] /Subtype /Link /A << /S /GoTo /D (page.58) >> >> endobj 1529 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 204.767 442.109 215.671] /Subtype /Link /A << /S /GoTo /D (page.58) >> >> endobj 1530 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 180.857 442.109 191.761] /Subtype /Link /A << /S /GoTo /D (page.58) >> >> endobj 1531 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [430.154 156.947 442.109 167.85] /Subtype /Link /A << /S /GoTo /D (page.57) >> >> endobj 1532 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [500.628 144.991 512.583 155.895] /Subtype /Link /A << /S /GoTo /D (page.86) >> >> endobj 1533 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 94.953 399.977 105.483] /Subtype /Link /A << /S /GoTo /D (page.76) >> >> endobj 1538 0 obj << /D [1536 0 R /XYZ 72 744.907 null] >> endobj 1535 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F75 1122 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1599 0 obj << /Length 1505 /Filter /FlateDecode >> stream xÚÝš[s›8€ßý+x´gj* !Ákêín:“Ùnâ4i'C@q5Åà‚H›_ lclg_l.ï\¤£#€11€ñwïbÜ{ÿÑ"†kºcüdPdPì˜ØµŒq`Ü÷?¿Lãàq€@?KCDþ(ö³)‹„'x½Ó×®Yȼ”éh‚Á·ñ§Þ_ãÞÏ”ýê÷b“jøÓÞý7`òú'˜–ë¿òVSGþ‡ÆMï¿(d&ŽjG±üÅ1yãÕÅë’Æ­Ð@MÇ–½C`‹j$JsñÊ÷CËÍ ec×¶u£”%Ïh÷Y2°í¾é¥/‘/ñlÐÿ l u’…LÁwú]e4îó—PgY°±÷¶;' ;ðÃ8U=ÕÛ`-ع ³}åCõ*£Nƒ…Ó˜5,æÕܧnX½ðfÑf踸?eâ{ì…X½Œ½Jl¯ò6£û³ Ò: @Yò08Ërö» În!Ȉùq ü§ ?ôÒTŸòHÿ¿}R<_ÄÉË’PO¤4€¸à¢E.¨Wû# ó‚‡)KSoÂÌky"M¯Y:‹£”]¨1¶3…cÐțĈ{“(N÷L­a ØÙýg}ˆ#‘ÄáEû?ºã! y²YÅ6ÀÁûÄÄ5û™±TüãEAØiPML ‡û2S_ß ¥P /ð„—Ê `¦/-ÃæoQ¤COhþ‹&ÝqJqÙn_Nuô(£É'@´ºE”ðTŽ"ò÷|¸[WMØ4jŒT'ÀCÝšOÿª‡ÊY;4±{ ¬jþ'ÎHÈpiËfàÈp3/IÙ¬v%kÒ@.Ã()R$–š—§œH£Ñò{œ…Ì¡TºÈ¸kfóY-ÍÞ:¿Zëyž ×ÅA+¯&ÓÚ &!q´[.»l­CT 7.—lÿï“JïžäB‰Zõš–E*¼H¤Õ¸žPå›âT[§Cì4Qô‰Ô+ÁxwõÐ) ×”b$bu]]ÀêàVn£Žuñ@oËqó¢Gk4´É̲…a8§AÍiÔq“Œ}ߨÀÖ«å(¨|iÙ_‘ºˆâ2tçR×…qk6hThêJìC¥n ì¥2Ä™(ÛÝXèJ쮕ϹH2nGLÏ@YX>ÐÖÔ‰7K™§^-ÖHñ–m6zyÑhkvXWÍ/^*ûý òó‚z‡9â²s¾ÿHmbÓ©Rù|8FÈt ÖŽÕ#+5u¥ lR·laÙ}>eq&ÎËAVÿ d9¦ë@ƒX¶iC¤Å‰¥žŸk¾Å»»Ñy*>p™¦" 6ÉR?nU[ԎƤÙ(Ø(fnÚ…¨—RRjÃÝ LmÔUrDº³ÚÒÜnõæ,âB›d'×™¤E'Þb’[)ßepÞ#qÍ<ÿǃš2dÑdÏ9.<ä‚´’qìÍ’ÎäÒ½²Bõ¶ÆðúÁ\s-ŽhÞm Ÿ6·àü²5О•î¼/àÿV Þ›òìËÓä@Àó.Nj¾·PšÞnÇÂ49faú@¨ËÒG­Þ¶‡vVEé5XàîjÒm¹$ÁWTó©ñnëÔx7p$œœÀ£‰LI¨.ß  Ì\© y ˆ¡«¾Ç‚ý•IõðQ’ïzÓþJ¸`~ÌÃþê‡\ƛęNã¨ú©@~ýŠÿæ¦U¸ÉVå'}Û> endobj 1534 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 708.942 122.809 717.519] /Subtype /Link /A << /S /GoTo /D (page.77) >> >> endobj 1539 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [163.815 694.929 175.77 705.833] /Subtype /Link /A << /S /GoTo /D (page.78) >> >> endobj 1540 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [159.392 682.974 171.347 693.878] /Subtype /Link /A << /S /GoTo /D (page.76) >> >> endobj 1541 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 649.166 122.809 657.743] /Subtype /Link /A << /S /GoTo /D (page.77) >> >> endobj 1542 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 623.572 160.996 634.102] /Subtype /Link /A << /S /GoTo /D (page.77) >> >> endobj 1543 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 599.661 160.996 610.192] /Subtype /Link /A << /S /GoTo /D (page.77) >> >> endobj 1544 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [248.626 587.333 260.581 598.237] /Subtype /Link /A << /S /GoTo /D (page.60) >> >> endobj 1545 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 563.796 160.996 574.326] /Subtype /Link /A << /S /GoTo /D (page.40) >> >> endobj 1546 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 539.886 160.996 550.416] /Subtype /Link /A << /S /GoTo /D (page.60) >> >> endobj 1547 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 515.975 160.996 526.506] /Subtype /Link /A << /S /GoTo /D (page.76) >> >> endobj 1548 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 492.065 160.996 502.595] /Subtype /Link /A << /S /GoTo /D (page.51) >> >> endobj 1549 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 468.155 160.996 478.685] /Subtype /Link /A << /S /GoTo /D (page.53) >> >> endobj 1550 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 444.244 160.996 454.774] /Subtype /Link /A << /S /GoTo /D (page.54) >> >> endobj 1551 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 420.334 160.996 430.864] /Subtype /Link /A << /S /GoTo /D (page.52) >> >> endobj 1552 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 396.423 160.996 406.954] /Subtype /Link /A << /S /GoTo /D (page.49) >> >> endobj 1553 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 372.513 160.996 383.043] /Subtype /Link /A << /S /GoTo /D (page.50) >> >> endobj 1554 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 348.603 160.996 359.133] /Subtype /Link /A << /S /GoTo /D (page.50) >> >> endobj 1555 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 324.692 160.996 335.223] /Subtype /Link /A << /S /GoTo /D (page.63) >> >> endobj 1556 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [152.159 300.782 164.114 311.312] /Subtype /Link /A << /S /GoTo /D (page.71) >> >> endobj 1557 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [237.737 288.453 249.692 299.357] /Subtype /Link /A << /S /GoTo /D (page.61) >> >> endobj 1558 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 266.6 122.809 275.447] /Subtype /Link /A << /S /GoTo /D (page.48) >> >> endobj 1559 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 242.69 122.809 251.537] /Subtype /Link /A << /S /GoTo /D (page.48) >> >> endobj 1560 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [110.854 218.66 122.809 227.626] /Subtype /Link /A << /S /GoTo /D (page.79) >> >> endobj 1561 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [290.06 204.767 302.015 215.671] /Subtype /Link /A << /S /GoTo /D (page.77) >> >> endobj 1562 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [287.052 192.812 299.007 203.716] /Subtype /Link /A << /S /GoTo /D (page.78) >> >> endobj 1563 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [282.628 180.857 294.584 191.761] /Subtype /Link /A << /S /GoTo /D (page.77) >> >> endobj 1564 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [288.716 168.902 300.671 179.806] /Subtype /Link /A << /S /GoTo /D (page.79) >> >> endobj 1565 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [284.292 156.947 296.247 167.85] /Subtype /Link /A << /S /GoTo /D (page.77) >> >> endobj 1566 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [270.204 144.991 282.16 155.895] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1567 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [134.365 133.41 146.321 143.94] /Subtype /Link /A << /S /GoTo /D (page.48) >> >> endobj 1568 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [149.041 109.499 160.996 120.03] /Subtype /Link /A << /S /GoTo /D (page.59) >> >> endobj 1569 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [270.952 71.372 282.907 82.276] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1570 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [397.695 707.258 409.65 717.788] /Subtype /Link /A << /S /GoTo /D (page.79) >> >> endobj 1571 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [529.041 694.929 540.996 705.833] /Subtype /Link /A << /S /GoTo /D (page.70) >> >> endobj 1572 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 673.076 361.791 681.923] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1573 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [501.346 634.379 513.301 645.283] /Subtype /Link /A << /S /GoTo /D (page.70) >> >> endobj 1574 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [502.531 622.424 514.486 633.328] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1575 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [513.918 610.469 525.873 621.373] /Subtype /Link /A << /S /GoTo /D (page.85) >> >> endobj 1576 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 586.932 399.977 597.463] /Subtype /Link /A << /S /GoTo /D (page.59) >> >> endobj 1577 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [383.877 574.977 395.832 585.507] /Subtype /Link /A << /S /GoTo /D (page.84) >> >> endobj 1578 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 525.337 399.977 535.867] /Subtype /Link /A << /S /GoTo /D (page.51) >> >> endobj 1579 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 501.426 399.977 511.957] /Subtype /Link /A << /S /GoTo /D (page.53) >> >> endobj 1580 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 477.516 399.977 488.046] /Subtype /Link /A << /S /GoTo /D (page.54) >> >> endobj 1581 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 453.606 399.977 464.136] /Subtype /Link /A << /S /GoTo /D (page.52) >> >> endobj 1582 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 429.695 399.977 440.226] /Subtype /Link /A << /S /GoTo /D (page.49) >> >> endobj 1583 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 405.785 399.977 416.315] /Subtype /Link /A << /S /GoTo /D (page.50) >> >> endobj 1584 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 381.875 399.977 392.405] /Subtype /Link /A << /S /GoTo /D (page.50) >> >> endobj 1585 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 357.964 399.977 368.495] /Subtype /Link /A << /S /GoTo /D (page.64) >> >> endobj 1586 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [349.836 310.008 361.791 318.854] /Subtype /Link /A << /S /GoTo /D (page.47) >> >> endobj 1587 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 284.414 399.977 294.944] /Subtype /Link /A << /S /GoTo /D (page.44) >> >> endobj 1588 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 260.503 399.977 271.034] /Subtype /Link /A << /S /GoTo /D (page.44) >> >> endobj 1589 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 224.638 399.977 235.168] /Subtype /Link /A << /S /GoTo /D (page.44) >> >> endobj 1590 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [388.022 188.772 399.977 199.303] /Subtype /Link /A << /S /GoTo /D (page.44) >> >> endobj 1591 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [448.973 164.488 460.928 175.392] /Subtype /Link /A << /S /GoTo /D (page.42) >> >> endobj 1592 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [448.973 140.578 460.928 151.482] /Subtype /Link /A << /S /GoTo /D (page.42) >> >> endobj 1593 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [468.18 116.668 480.136 127.572] /Subtype /Link /A << /S /GoTo /D (page.75) >> >> endobj 1594 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [468.18 92.757 480.136 103.661] /Subtype /Link /A << /S /GoTo /D (page.75) >> >> endobj 1595 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [448.973 68.847 460.928 79.751] /Subtype /Link /A << /S /GoTo /D (page.41) >> >> endobj 1600 0 obj << /D [1598 0 R /XYZ 72 744.907 null] >> endobj 1597 0 obj << /Font << /F36 131 0 R /F39 133 0 R /F75 1122 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1605 0 obj << /Length 388 /Filter /FlateDecode >> stream xÚ½“MOÜ0†ïþsL$bf^[ZTNmˆÄa‹Ð²ë®"åÖYÑþû:Î"íB‹@T½Øž™ø÷q4@8gjvú¹Ôà¸ÓBCýJ%9FZ.] õÙ×_ݰ¾Íf»ÂØìlXí:ß˱ú“9WùÖ/ƒŸâ˜_×ìSÍîÅfFD]É Xulq°Žù @^: 髤¶qoá’}cxdÔ´9–föxµmFÙô›Öš¶òánè“’6ûŽ Wí2„›¬é÷…»œ0›ðŠÉ.”Ž[EPq§Ô¬¼ç·Íxó0u¹é|Ë¢t’(‘ ´†‘q•VÃ6ú~–¬Î, ¡0“4ßÛãAñ§ÆH•ÏIe›&Œ~[ùûc^ÆÈÈ’Kd©ð²íú?C4ò=ˆ4é#Æ)NŒÓá%Fý?Õãßö§CgLèWÏÀó!Rp"1#~é× îgz©4wBÄë©èÜ‘Åhå7ˆ¼ü! endstream endobj 1604 0 obj << /Type /Page /Contents 1605 0 R /Resources 1603 0 R /MediaBox [0 0 612 792] /Parent 1607 0 R /Annots [ 1596 0 R 1601 0 R 1602 0 R ] >> endobj 1596 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [209.991 694.929 221.947 705.833] /Subtype /Link /A << /S /GoTo /D (page.41) >> >> endobj 1601 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [229.199 671.019 241.154 681.923] /Subtype /Link /A << /S /GoTo /D (page.74) >> >> endobj 1602 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[1 0 0] /Rect [229.199 647.108 241.154 658.012] /Subtype /Link /A << /S /GoTo /D (page.75) >> >> endobj 1606 0 obj << /D [1604 0 R /XYZ 72 744.907 null] >> endobj 1603 0 obj << /Font << /F36 131 0 R /F39 133 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1609 0 obj [667 667 722 722 667 611 778 722 278 500 667 556 833 722 778 667 778 722 667 611 722 667 944 667 667 611 278 278 278 469 556 222 556 556 500 556 556 278 556 556 222 222 500 222 833 556 556 556 556 333 500 278 556 500 722 500 500] endobj 1610 0 obj [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600] endobj 1611 0 obj [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600] endobj 1612 0 obj [500 500 167 333 556 278 333 333 0 333 675 0 556 389 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 214 250 333 420 500 500 833 778 333 333 333 500 675 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 675 675 675 500 920 611 611 667 722 611 611 722 722 333 444 667 556 833 667 722 611 722 611 500 556 722 611 833 611 556 556 389 278 389 422 500 333 500 500 444 500 444 278 500 500 278 278 444 278 722 500 500 500 500 389 389 278 500 444 667 444 444] endobj 1613 0 obj [600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600] endobj 1614 0 obj [556 556 167 333 667 278 333 333 0 333 570 0 667 444 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 278 250 333 555 500 500 1000 833 333 333 333 500 570 250 333 250 278 500 500 500 500 500 500 500 500 500 500 333 333 570 570 570 500 930 722 667 722 722 667 611 778 778 389 500 778 667 944 722 778 611 778 722 556 667 722 722 1000 722 722 667 333 278 333 581 500 333 500 556 444 556 444 333 500 556 278 333 556 278 833 556 500 556 556 444 389 333 556 500 722 500 500] endobj 1615 0 obj [556 556 167 333 611 278 333 333 0 333 564 0 611 444 333 278 0 0 0 0 0 0 0 0 0 0 0 0 333 180 250 333 408 500 500 833 778 333 333 333 500 564 250 333 250 278 500 500 500 500 500 500 500 500 500 500 278 278 564 564 564 444 921 722 667 667 722 611 556 722 722 333 389 722 611 889 722 722 556 722 667 556 611 722 722 944 722 722 611 333 278 333 469 500 333 444 500 444 500 444 333 500 500 278 278 500 278 778 500 500 500 500 333 389 278 500 500 722 500 500 444 480 200 480 541 0 0 0 333 500 444 1000 500 500 333 1000 556 333 889 0 0 0 0 0 0 444 444 350 500 1000] endobj 1616 0 obj [278 278 556 556 556 556 556 556 556 556 556 556 333 333 584 584 584 611 975 722 722 722 722 667 611 778 722 278 556 722 611 833 722 778 667 778 722 667 611 722 667 944 667 667 611 333 278 333 584 556 278 556 611 556 611 556 333 611 611 278 278 556 278 889 611 611 611 611 389 556] endobj 1617 0 obj [278 333 278 278 556 556 556 556 556 556 556 556 556 556 333 333 584 584 584 611 975 722 722 722 722 667 611 778 722 278 556 722 611 833 722 778 667 778 722 667 611 722 667 944 667 667 611 333 278 333 584 556 278 556 611 556 611 556 333 611 611 278 278 556 278 889 611 611 611 611 389 556 333 611 556 778 556 556 500 389 280 389 584 0 0 0 278 556 500 1000 556 556 333 1000 667 333 1000 0 0 0 0 0 0 500 500 350 556 1000] endobj 1618 0 obj << /Length1 1606 /Length2 14854 /Length3 0 /Length 15687 /Filter /FlateDecode >> stream xÚ­·eT]]“- ÁÝ%è!¸[pw îî8¸;?¸Üàîîî.ÁÝ‚ûò¼ïíî;ú»¿îí{Œ½ªjÍšU³Ö{S’)©2Š˜Ù›%íí\Y™Xx [Wgy{;9FQ{3À_#"%¥˜ÐØdo'nìähÍâ@S€•‡‡‘ fïàé²°tШ«hÒÒÓ3ü—埀‰çxþîtYبþ¾¸mìlv.!þ¯7ªK Àdˆ)*iË(Hh¤ÔR@; “± @ÉÕÄd ™휴s{'€Í¿S{;3Ð?¥93ýÅqœ€¦ ¿Û€¦@‡\  “-ÈÙùï;ä °p2¶sùÛ{ÈÎÔÆÕìíæöÿ"äàdÿ7Âö¯ï/˜’½³‹³©ÈÁð7«’¸ä¿yºX»ü“Ûô× °7ÿifoêúOIÿòý…ùëu1Ù9\€.ÿä2Ì@Î6Æžsÿspý‹†«3ÈÎâ¿0œ€ÆNf6@gç¿0±ÿéÎÕ øßª7vp°ñü×nûEý'‹3ÐÆœ ‘•íoNS—¿¹-@vˆÌÿ ŠŒ¹=€•åßv3W‡ÿð¹þÕ šf†ö/ c3{;O€Ð‘YÁÞåoJÍÿÊLÿs"ÿHü?"ðÿˆ¼ÿoâþwþ·Cüÿzžÿ;´¤«‚±íßø÷ø{ÃØäÿÜ1ÿ¿Xc[çÿ!ú¿jÿÍðÿ"ãbü· "v¥`abù·ä, òš)\L-æÆ6{ô/»ºÐÉdü«å¿Ú`deaùo>5K©µÝ?Mçø· hgöß™ÿ•ç_¼™Å¤uädEéÿûmú¯(¥¿ª»¨y:ü%ö¿ê·7ûÏÅ?¢¢öoƯìF6.n'7+€›‹Õ÷ÿí_0¬ÿµ–7vqytÿ–ÌÂú¯Âÿ×ó_+ýÿ#agjoöÏ”¨ºÛ™ý¬ÿ4üã6uurú«ç¿Îúß‚ÿcý¯=€¦ˆ«Kö¦|ÁV©i.5x9Câº}=¬PC!%õj…ùþUöÝßSÃ~ñü4z­aj˜â}oõ\6aƒ+|ÆbÑ=$Ã}‡Ë¹ÆÆ·ùsÈÝ÷ï÷±"±*Ò°½ò™ë¹µ“W`oâl?Á@Bûn@ƒ9U'ÙïÊö7l©Ä+WÙSC¢5vôcÜT\d󥚡h·ðè¬ÞÜhHÄþ£‡hþ_%;è¢Ò㡬ÁWá o™I'ê46Øò‚¯s&Êè7Unèž]½i<?˜J±82qj|\OBcÈ,N1ÒæˆJ}6oº‚4©ç™<ß‹;¤ÔÝ‚ƒ$:¢ïÅT4éëùTüºŸL†Uªöù!&»ç´†Ýt±´ô—P Z8[žT%#ïêbèôK®À†Å])!,Ž9D>»ì õjN þõC~ê…ë ÷µZÖÚKöVM^¦ÏÆÍCm…¤>‚Gpï¡p$[š¹(Þ|×ù„>üw艓æç_îTe0Çãà“]j¾S˜Âltƒý©#΢²Õ¬’†Ës@ß9dó¸¶ãÊÔüÞ{Ç-ÇÓÒ<陼ªKÖ.qÌf=ÔˆîLÑc$„ªjâ—f·äónžV¼ ÍVIMw˜Éô+‚e>1*!šè8ý#áSjŒœ%±üÚGQ¬lµÐÉù§ƒÔ»¨~yÖ øÇ[”HÛy[wklÊñêÀE6&·FžÜ”'õn5ÙU«%OùZÉÑžß›¸׺„>3¼*«ºêí`—4zâµ]~DêÊÁ)ÎlÞ²^ôu·)…Û–NêãWn\¤‡©Ó#º'Èi·UU!"¨¥_Š;‚f8*‹kÙÍg{flõdRb‡¡>Z³ I:¯ü¯Ì:gí~áM¿™a9ZÄ_ý¤b§Fæœ Ûq.’0‰ÔTKÖü}2/r K²ÝŽÕhò4(¢nó£Ôò9¶}"nîæžœ¿× œ kÞ†:Žÿ0:] X‚þ°Ëmš¸ã±VG+ðA¯Ü]NšØYpÆæ…iÃ1p¹[5Œ7èÞqöìªIn7Û½û¹|&•–èi0f —1Ìš2vßtÔëeZÁÒ?á8ÛTÚþöƒ?ƒTÞ%{þÎ*À‰¯©ÙZŽŸ‰I7$¸RÕ¥©ª‹8iUb<:ià 2ŸëŒKþëgd·?#|dììk‡Z P_Ї¿p.{ðl¡s!¢àvägŸ¹[P"Ï¡M¾Æçýر‘ ûâzÐÍ“ˆyͦÁŸ+O*#pJÛt§ÏÒKécËœÆ*Èï®+é‰b1j°1ÄÍŒ.‚¨ÚÉ"öí%÷\5žÿ²f"Ò„%Z'¦ï*1ÞgqÓ»`þÁEÎŽÑTjDqhÉ/ù7œë¬¹¦bDòyIŒ‡þ©‚õ³ f´ÛÑ" j¹`ÂÜè˜PsX”ôÃâã0W 8Ü:-imÄü™¬8)åøw$ Ή´u0;Bg˜¨•¸KwuԬ͢pУSYh™4·EÑÞxįS*ÖŸÅÌT|¾nYv~½¢éÙ”jaeÍþÐï»™/wM®*š>Òj· ˜ Ù½µ«»=7ÄÈB–³¦?å«c¤]aµ û$Wª?ñ&«Ë‹µ}@¦Y™VÁFQM¤›Ãê:ê–†2j !°øiö&û7lÊ]_ÞÀoÊ›õ€gé\Ó§&ÂߺÖI®qî-ù<ª«’ÙˆßÎx¦GhàQ<” Qì*€äøb~V rÖ–Ñ&ÝÖzÁm€°œfž™v2ÝZ¥m!Aý¤½µˆ˜ìZ=ž¦ÎC ’Šs„ì³ò3ŒÆ4 aúÙ7á'ë„Û\­{Š뎾‰í„$SH+¥Rbúg^ÃtÆ€«òÞäל˜zžóOü|;L øìÑyh¹ð²çÑ<­†=ú„ˆš…daZ$<{¸%º/ö’NmŽamÆB|ÙHyÉ‘v &º¢O“KÈIÉ|þØdöxúž@ö¶á£ÐVÕqÙÌ<ÕªPÂò hCgJÊÇQªR‚YL5‹-¯t¤~âçá!c ÚÏ…{*”B¢Å›÷LÓ.8í"[Õp<øZ$úÂô+ÙÐÈðíg•#²~䕆F-”ÅFõÒ“„5 ´>²¼¿®$ÓVÅßg^Ãa˘é»C4œRÝÙF;è& žÛÞÐ<œ™S6 è‰\ ÓpÄ Uµæ6pgúyZä¯=xµVBc «(¸n¸Š' OküO‡ÎP5bÙñ\'ª¸ú˜8¸O8|°è¬­kÜ{²¤ÞÁ!@.äSU›2E Ê"‹„­]ÙÒÍm±8‡&K&4[™Øæv4µž®¥•¼ˆü”ÌLÜli¬¼ÍÄc#t޲(^ܦÝÙbu¡Ç=ÃÑŽu^L4­›µ@ Õ-‚ÄU2e¨;káâ«ÈÓÓ¨ø ÷xš&«ZßñW—«˜ê¯Ž?–Èm†Hˆ‹Kò„)›‚~˜µT4ˆn„Ð hþ4øzmPæz8†cze,Á×m™‡$V`"À¯ÿÃMöjÒ?>BŽ?ׂ ¢û¥w„ô‚á$kËW*ó—pNf[—ÎÅû@µ©Óá5µoè'H… G2Šüȯù_á7¶&,¼<]JYç,E‹üÙaa…§Úh|;uí`köO†žñ›>ÈøµÄÜ`Œ‚áÏxµÃë8ÌMö¬™ b.§ Ø”ÒUu®õ2ÂN…ϧdõ¿.–.Ðà{×+ qhF&—»1:9\Uïljm½BµèÃI%c(• ÙödÈùWýÌK‡û;Í}qçDÙ^ª]µõm±e÷3#DÝ{ùtùU¿PŸ»AzmTqs™ÌøàÌ)êU ìŒ" ã/ûSFâ埵Ε…h­Q¤”D ÙU´U°ë¦×ˆ§íøq»{ˆæ´\`‘ù+¶§u»hØ¢!ÂJ’²È/õŠhG Ø ©­>ÁpòbÛD6Z8îŒIK¨{vmž­dp÷éä¨PF¨®øèDf_Éõí¥}§–ÌÝ v×c¥"¸—ÞEé¹>Pó—TD1 Ù–øN¶ô¼â[ò³ìkÓÑ<5ë:÷{úKºûn9õîÎ(aaHR£K,稯aö“vCç¡þê8í›qŽáX[9D¥¶žäÅœï¬vt,\/¢+>Ò°8%RôùÕ‡÷Ù"!Ø3X–b«\žÑZ’~¸Ë.›tÌ`›f@›Ý²^X0b.{ÀÛ$„BÑ¢Yzý$¾t &lÆ3Ya1RæAáX¢§cºôôìü²yñäd£tÇ[¡õÖ@Éze±J– ‚oXœXÔhÂŽ¾X€›¹¡"]ú#ƒ>Ï5¹ÂZä¦×N½6²;¸¨ê4’Iyö²øªwõ: îØ)Žj‚†¾ÝÌ»¼Û>ò?6W^ÍÌpõ4 18XSáqÁNv«*RË0”Ò;Ûì¬}å-ÝFÐ í4}ÿSÎá´ëοJɰ­‹‚V»ß&8'%ˈ—ú%§»FwäuÖÌ$Ó~kÐ;Ã@#¿IœXô8vï;±³KѺó¦ý¶’ï9¿2Ó¤2T"æV¾¨]?•g)*yïÐS<ãŒ9ðû%{$¶MðPD‹IKúmñF‚{)E~*js]&lµpÔ}p¢zºŠD±”Ž`ŠÊMž„q÷†û1–¾e„qÈñR§‹ÓÖ%Æi×v_η5³Z2×˦”ïpÝeÎ9טv›,M¿ŸP¤öË„-<,và…%zgÒï¯úwÇKZ©§Ñܪ¶g)n°<õ>þºšƒä“úÀ1Ã3¾— Ž6ÅÊ]ÍÖ9ŒÌªïÕX“Iw»1¥CgØ.Ö­;Ê´ñ[´–1&%æ-{FUX»ÚÌ+¯2l-ÄDË‚÷ÃåäTXVzH^ñëà•Ë9¤¼‚C© ­Úºw…·G‡Äª%d®\ÔXåäÜ2l˜ Qk;?çkèÉx§È¥d:.•õm½©Hd8Vó6ÖñÏ„‡½Oá<æUK`©ÇJvôÚ³cl"s"€%·Þ¬Æ•:ûƒ*gÔÞoÜZ%ƒÍ*|§78•–èN:\àœxÑðÅ‚¾Ê‰ ÅÌ^€ÔQò½mÈK¢5ˆö™Dq9+¡K,i¼ÏyïLxøÂVÂe’°hÀ/[ŸÊO-?µ2”•«ÖG°hÄÂh&î<»ã ¿5Ÿo £Î<î'æN‰µfi`c4ž^¼I WŸu¡"ÜäN”ï=T!â˜ùî½DËhh)k ¦TÞÌ:ûrÆ Åk;µE®ƒšD_5ñ/éï‹1ó‘Æ-ÅDzûë[5,BÃ]Qîy©lT(‚×w¦òHK5¹Qœ?s•)ϲ=‹M7EäòÔœïÍ‘HóúœÃÖN ø(#zI—kÎàÛC‰Nølµ z™Íœt¥eLE"à Ó[)q¦ÇÁæF³¹ÀÛ±Ö“®r+–àVKòƒ»ŠÄò~@ŽÒ”ºå/g_‡"w%æŸåÞIðÕÛFñ+Hªÿ!DC‡:H'©ÇQÒ†Îé{WÇ], s\ŒºN ø(kŸý&D ‘‹½Âúí'«¯#œÄ-oeA}ÇH — _,…n}fylT$Âêv¥ øýB|% —!¨“ß ‰–œQ­c~)¼ ­==‘üóÝ7f–¢ö/ƒû O‘ÇùäøÀ3ܬâŸ}[ƽÌ7yóbx {J¦º˜óá`jà·ª©öøþ%9xfZpÚ¡J¦ÑQîg Isº›Óï‚ |´'G˜©;xæ/¼iMrL˜F®ib³~dË×4ÆxjÏ2]¿b¥¸’3èCR¸Îs«.ŸE_Ùw¤ŸyèÍØ¤nÌdmPºp«ûa÷#8©¢ëÖ‡¯öè=ç×'àŠD¢Z©‡6Ýeé~“P_Ía¢á$wSH×ï2C. Y£“}ÞÒQù¥oDT«Pwc‹Rñ s8Ž'èÿ<…h cØVÛ0.ì#E2g’Ÿ®ýF´W–AÁósº»Õ°_|¾…0¤"ï^»7‚´t׿M#›´"®ÉU,,…û1;€\9ç _½ cħû‰¡¹ögôYÕ˜ó÷<FûÊsrzÃ[”k§¾¼n{ãuZ@¾Ö“”!E¦ ÃõÙÞ¹û"bkg:ЮƒÃ‘dg 2©­õÅVÕãòMäÝIöã’~»i;Ñ.G»ÊOÑ][D²6%Ÿõƒ_I¯=Vøš dZ'€ŽŽ>ûp¢cs‡Pfnþ)«Î9ÈyƒDˆ1ÎþÔQ :âS|ÚÌzuADÝšü­MÎAmàùç÷Þ2ßóv:‚¶$ñ´’§ú¨µ AVƒìçY2ÖQÄÆ×ŸœÇ‚X’ƈV •q3…VËÑ?ë¿K2´Àp2×Ì3l*ÐèjŸG“öêÏ÷/Þõ®l¢æÐgÅ?]|xûØ$ó üðµå›‘¹Ü`&gwñy3¾ZQ`ô2Ð)S•À˜ Ú|Ç#…5®:@ ;Uî®&q ¹VÚªY©Ö-“Ð_ÕfôrŒ¾ñD†[ò»‰Eílzz0¦üÚrÚ…¿m ¢E}ñ6Z™²@„P¡Jä M¸U±Õþ9¾p.+z¢l5ܽáP@æBu¾Òik¹P~†\ WïÑxÓ: o®“®’¨ê ì ÍsÄ Ægm©ãáù¼eõäï &ZñqyqY?Y~SÛÝhŸiÊ{¦$búÜ‘¬y×wTs•.G…ÿ(P܇©´Iš—e´s”ðÛ娫çÁ`9\M~ƒ 퇶ŸÖ9‹ÏÄ€¢¦é3Ò£âè$>EpN$X‹+-á|Ť£¯>VÌNæMX¡uârt½?Ÿ¼‡âæÂ¹SÖzï’K¬³ˆ€>Õ0\‚š7Ñ`#Ñ+%nðÖbWL]‡¬»ôÕb4C‹á‘¡4*¡Çw89’àO;FëÐé¾6' To&ï2ëXl¸ŠÌËÃK£åÙ31Ђ',ú,Û¢›5[t[w¸Våý´÷±´kÉväàyÐUÝkM9H@Ôñ:›à\2=¸Íö-Õ›å+_>³9‚‹#¶C”LöeVü“¼¦pŠ™+ûl5u½?‡Áœiûý<Š•_=Û>š¢Ép×ÔûI„ŽÔ›G̰iØ­[Bï]9ª‹*6%7sD<²ÞCË3´3Ós|$8\Gƒë©Ê‰y©ycYÅ;%]¬…“GuLµŒ­‚W+xӱƟ®:±{!~)v ¶ÒD·Þå¦TC_Œ÷šQÞ3eÒ)OÚ 4É5ŠdË*lÙ»4ÛeÔ¢ï9^zJªû‡¬òìP/ååHc“ ÑIg wªí@ìÙ¨Ké} •§rôn_º¸:U£+T‰Z·v),SX<¡ÓÐÕÛÀƒÄa»'kq³÷ífß`’Òˆ“8˜>Ðr$‡!1:KÊð3…Þú³ 6ñ«ýxÒÍý0k|p¢¯C­±«ð>öà`’‘8Û:U`kMxØÂ?¥Œ‡ÙkËx"ú} A²Ža=Çof#1-<<ÍL5»'ƒ†/¶z°šL*¤I>-ՋԺȦÀ ¼n†üTABÈ ê²¤­¹¢¼ÈàÂàtpy©J4ôg½UÓG´Q1$¾üù‚ðO!„ÿL2Ýhƒp¯,àÌJ„óÌøîC¹4ai'qxhRñg„üÐÏÅÏ ‡x.Fݰa4“³¼ÅšÏKY!ˆ ºMîÜ[/Œ²Fõ¯ °<ŸÈúÙÕ”jа•2º£âº~N„à]«>Þ€½Ÿ)Ï÷ÊN J¡¶î<ò6‡»‘5ƒHèŽI+€Ì2)ºåç š…Îïg¡\.1ܶèK¨ïd­dv.Tþ‹ A¾›O… pF„œÎòÁV $’à ËÁ± €1͵ßCû½…Þòߢ0éEÄËÚönä±ãoM°ÈÐl½KÛ à%|ï-¨ÝZC0Âë0åcš2«¥á¦¿ï"K:›¼â®+ÃÌO:RÒÍüÄ‹i¤ÀpN6þ•ìÄ¥rÕxõØA4œuf/ÇÎ}»á“ÞDù§’eíKÁ5Ïô ®µªþ—$r$Õ-8.RÉVÑÉ^cSº¸‡{ ”×÷¬}Œ4}nœfXßÕ¤ÖÜ+Â4õêQ,PÚX*fÜF¡x¿ú¥¹1µ:6߯Õv*æ¸ö³!å°ŒRwŠ”¥n©G}|°XÔ >j«¿ðK¡©û0ãÒ;ÿÑÖ~¯­ô(]r“4f~³ ŒÆ`ÎÍ*¬-w[@œƒ;e/_P*º6/%9G·¹)|Œ¬i3GÐ ¾†f™°`¹2‹Þú²¼$"ŒK9xúj¶Uã²þµyÞþÎÛ4,nJ¼$ëW|²O7‡È­PEÚ‚ÏÏ?|ƒyF>-S3¶¿Q®êLMô®Qa|c̆¸ˆ`‘ƒ¨cüTáš2÷΢peE-lä\y_vzà^ ZMåqö€eÇILÒçfn µ1‘-k†kæ.îåÑÜÑq:D͇uµ©gWáÅË8¸tj  zÐÌU¥Ûȳ¤>I^-Ûüâ£j¢¾ßƒ.ŠViCÚpNŒêÐPÅû3´G_îPx…vÐ<I‚$ß]Ght™,;~ˆ’IØ$oQ5òÜ礚ÒKˆ·‰õ\&GODëØ@2̼ö%ÄNÛ±c#}"Ó'œ-í×)jyÍ­}Ç(rµðR;¢sŶ¨•`T•M…øÌuªV(æŽ+þÝB>‹…¤±t¦ñ:1¼Î°Ó°Ð&1{ÈѩЄº"'‘?º˜*ßoSÞĤ[Á"¯±«×bÐÀcŽ+éR¶v]‰‡ÿMìEEùsSaƒ†,+¹[BR™Vè{Èþæ¸F•Ì‹®LVÞwr÷£áW¶Ï~ÿ½]â²iP"BI\nœ§Ì§¤å1Vº^=0N©5 Wh¥èK ûþÆÝìã7÷Á þ·±ËŸp U¦R²K\ä;có2´OñÔáHÜTB8 °ëÞMl)3™jeB‘"«°>‘i‡ü¢ÛM/fdc§ò׿º¹ÖÑsŒ^œzT‰y½P^kM›Ûî·«Zpó¿O¶\®°‡y¦ØŸü'¸7é­‰øTåšà-Ê ¡{'Ðd£ÉHÃÍWQÈf†–`–eŸÖ¿ 7¦–aÍ©$Lús“j2([Ÿè~egfS]…a)d^ÚÑ4-âMÖº!Ö9—j¬„R…R7XðÑd=lí92“B&ŠíÞ•K\ðõÒÄ>a`Q‡ˆ(ŽndçY’ò)çŽÛîq¹h°(¾!­¨‘Âj¨ö/ ;MpÚ v¯ž˜LËßPèÃÃZZ“©¸&>¤É³V‹]Ú%®õÌ/Õ }y[+6Â~zn¡r¦ó8ùŠÆxõ$óÞûH†‹¯ÿe”ãÛÜù-²mÚ¹ 82vƒ H ûÉ šèY®í {æs«á½]‘¦ÚÔ*þÙÙ!iÀZq¿å ÷ ÍMhQ»W÷Þºãp‰§ AòGuì«ÙhÍá²A;“:é¡~¹{šN‘q?LÌÁòH^Y'“3¾#ÿZþ²MkŠEóïñç­5­Á˜b—‡¦êZöz£ÇZûd¿ÄG¿Ž~ÅQÜæi?£QëIÃôûŽd†·¾A©°- ÿD~€@P3À×°õ(›*@ó{ƒïü7‡64ZEs£U[ÌóªµÏ·rm²ÿc ‚Ù°úü7` žwTH÷ÊÅð=>#œó·ýá‰8"½&%&"#þ„ÈÚ®fn’µµšU+¤yúè|Å­¯~‰ãäÍ-zÏÕ>Þ¡#_Ü3ÅPœ•ú‚Ó_ ô~Û Ÿj%$FÄ#õ¨>^¸«&Oã)qo#’ü—ð~Äɪè¢àPFM"¸>ÉQЫòRY¢ß?·$Äé - ãü$Dl¤SOåbÒ2|Úb΋kke³»Ê9,25SÐnìVAâØY]i<Ç“¯²|2YY°þʤŠGξÕÖ„a«Ú­´+X\Ès“¸ÿ;bz‘>ò{L§Ê½ºëŸ›@!׆¹Ð_Gd8uvh›Ÿ¥˜ÆzNÕ1W[NUq§²Gmì«Ä®íÁ»DUlmqg¾ÂÛºA* ¶0oubæ•kÚÖ5êŒó2a¬²’ª‘ÑïÎÊ_œÍÑl«}ØÊÍà×.yÈÂßµqšëøs}}´ñ{íóŽ~2la(BaҶ´q8Ù¥‰^UryN(CÁãvö|Êé¤Z¶W_¶ˆõLaý¨<ªC[œ‰ß<»oyˇß/Ð¥-ÊÜØá¦œ*v›2Ø+ÖH@: §1¬v?ˆ9¥‹ƒ0Ò£!*R—* Ÿ×Îà€Ä™ŸÌ—Yz“k í=ƒE–Èãßì­Õ/ˆÓοyÎÝ_œb7?@’uŠ¥s‰HZ=ø©:½CølØÎd”e@O-ìÖqŒçG­ÏO¤Öm¿Á1´–'î4Š|‰Z ð¦nŸ©D0ô ”ôéH«ÌR£SK •“¹âMê‚NÃͤQ’¦¯ìIÏ„¤âðfWÈÖ"ûdXJËÆþt1E ™€?rõP Ùv‡üõEØÍ~¿l,nk áL SañA à ŒÀ±îBëzb–LÝ^Ù$Ñw[Q£024¹MØþý+Že:‰v& íìÉ@]ö»rh»"äxTxL¶ðG&\œ 1 ãÛMV‹v»è_·ì¿y¤ ÷wŒàëü~mÆ[j9˜lá¾lƈìĬòÔ¢ïá¯[ơş·#p£±9UAíè9¤õÈaò’ºîW²œ%åÙX¬FÉ\Î Í>•xÞ3Ê"T!9nkR²Ù~çüŒ5CÜ&s1ðc›ÖQ¤¤í½ÆjJcÎ)ËË,FÒNÈö/à ù™Í²·¢^XIÛãI€}ˆ ‹2¯ÁQAïÂCèô6ÿòͦD÷3ZŸÒ§Ô#̈Îg¸É\Þú’ìåÖ˜ðYô£×qˆ>e¿Ì48RÖ­´ÜêÓ –Ž6¼_»GG\0Dê&:³¨l»?<y¾AóÄìÙË- 3®Ç±j$,uÑ®s^Ü8«õ|7*0úA–A^ ]Ø‚¸å+|Gûl›ÍËõa˜“xñEì·Ç[‘4/Ä`†ØŸ³Kq»ïxøŒT¯áYµs ¬H¡ˆª ™,‚hÒê¸Ê¾D®ìÚbÍzöÏ·71¿Œ6 úŒ5¾N2;UÔÊž®“FbEib…jgæ,jú²?‰P¥G“Áï0‰zkN|Ûº¬Wô?ܦSX¶‘±Xö3(è¿§  Æêúža’f#Ïï ß~*î"GÎÏhUÞä/+í”9IÄó¬'Œi [.úb%®ë@©-ø÷Ï®'ˆäK’ûa¼F¸KSogù¡©³äÙÅ$Úƒ ŸÑ@”Ó$…!°ÙƒuÙç5¿iŸ¾f£´ÆÊ$¬F»£›^0ÔîýPk¡àÊ_©q9-‡€õXHŠ<]uZïö®«ÕÀàݱ.Fw5ræ8eM‡7SK²ˆ‰­Ï6¿õ&„(°ù• áçÊzSk¾ßŒ®:¶a²lFh m×làíÇdß­‰~Ç!ùáÕïjO´ÄMÖèm—íHª5öW¿bÒWݘØó6ãßõ%&ýÒ©ŸÄQr†Åµ`BèMœ Äóóß}ƒK$“d&Î N`ü)b§Î²v&V=Ãýˆ“E:„…pO' ‰Ò«pÍ„ô(ƒ·<;~ ^ö/Álœ 8n— kìãî@o³”š¶uBV“ÜçËÉD)øBê±sÌö*7ªMyK%»é ¨½xeü|lZ!c€§óöj¤'kcÀ¢*SÏ6.íŒ2¯¡ Çðªv€çki&•¶C_—lš;¿®EÛ`L|º’=¦œ5 úoSÔe<‰-ø’‡wÚ[!çºÀ¢ò:d#{@š x—9¢¡.±šx;£a³u6Ö[VkîiüYãfv»UË\(ˆÍ»ÄE”¸vS+3nrf¾Ì – yb]{©ÃÂgÒµçúÓó Ñâ£ëÙw&Ï9¼ æˆ*ƒ®}áRÉwš; ´¥·kZ®úQû„DžÜ)Àr+¸W™Õ¨|Êš¹BÆD¹çœQìY³TÑW´€ê5¢üiÙEÞã¾üb`*,ñ;›óKšXÀØÆõtžEýÖ:e‘Ö ‹Ñ1q×V¢6‡e¶ƒà^"i€Âì­Cw£fŠRÝ+¥èW¸o̓ímĹAüçfO0OÇ#Sþ«ý„™¬X\ÈjœtuÚ7O–:€`$(`»ºÞ·tLÔðÇkº•‡xŒïßK÷UGo€.ñÙ„HšJ"Æ/ŠÂ.p¡êFí8Ž{c½Ÿ{%ʬ}4Å骷cBáz…W,†fxþ ;Åî¸f7cMIé‘p‹Î$£’T°˜\¨ð5&'·ÐµÍN ¥Ë@tÿ™e8D’ª\À4ýãþS1 €_Á}Û ŸÌA'Mí åÙM|ÅTŠS•kLg#uk\xŠN»ÙlÕµèfoûǼêòjш–óÄKt"JqùŸ/¬[Øl¢,Ê:Ä%¢ò)LÉuD”oŠ4¯j`:çb[ŒÓÔ)ê0½¾twWXp^ƒÉÕ Ô«m%I(¥PP'È´tAÂÖ-lä¤5¦ãé` u2S>\K/ÓQ¬S—²µU÷'&¥s÷è‚*å—ÆÓóŽàÒÏld·©#²S‰oU»Ôu^{îC ÚÏ­§ÀK"wHœ¢Y §8ÁuŸ)£M”ù{¯,øïáqe©ÉS2 {/ªp˜v¶p|ð×Íïz;ÓNÖ°ýËÓ÷ÅQ7ËwAß°¼x©jÞh’§“ñFeß4,P9*:Xí:€P~«[žMeÁã9 ^P^YƒDà̸¸0I·GÁGúºí.Š{´„' [.½°Ù&±£;–®I÷™ÍvØ&FÈ¢b­‹m/ƒì203›-‚ÒVÿõò|n“oë·<§<4 ß§<… ~p—;÷sˆ>ÛáA²äíÑ(Ї+¶ƒ”³ÄD]2ájsSÑ~­Ù¢–› PáøçÉu¶dípZb/@y7Â;æ!Çí'œ×Ú~o“zƒä*j[}W´B‰ÁÉ>#üÉ…Ÿu!£vHlE[_•ËÃ)Òçzw#\2vÔ$>*,g+i¼Üî¦ÙQþ‡ˆ„57宵Ð÷¹v ÑJnæ¯î)·ïßåaž‹6¾}PIF$ŽD *ãÜEOÙª^À›ôvb„Ä2w>Â~òî,w$­¸Ò°W[˜Ÿ î(NŒ½×a†%øµîùN„½Þ”Ô¯À/z#ÐߤØÝ£–Z#D JÌq¸‹¨eòa…Ä`ðÌ`“ÉAót2œw€ö¶ät|¨P´ô->íKg\ªô³¤e[Á^äÀ)»ŠÑÊ©SvÉpð IW²sÀ’“cZNñ%`Ûó6І>7`ý ñ½ïû.  ¶<”1ÐÓ^¸‡ª¸4oñ—„|úÍë?àd†&· ò Í`Ç”êP.–_G0›ÂKa6Ô¾Q :¾™Õ¹*!xŒüÇ0A;WÛYlº‹ÏZý)Ƶ—!];åwü EÆ™à8𫾋Cê¡ÞƒÚ’|ðs?’ýÙ½ã`Wg $”LûòN™xðò› ŠzUo²Ü¶’Q|“f’Ȥ‹+µälS·èÎW)€'ÞðnÅÌáeèl`klÕØ1ù]ÕB³¥‘½ÿvW“òÅQã6ÌÈ^[IB̾ nK‚CdI„¡wéØljW"¼sZùSÏwÓl:’ÜëD¶ ]™«Ï<"Êa‰Ò!¼wä/¢‚}wÙ -"kx¨Œ§½žÐôáfEƽ¦þž¬ˆ]…ö&_{ù÷&_»u÷0åäÅöWÆt ÉÃ&_¿µ:8Âè ! ®õÁ|ÂTˆÓªm™—¼þÄ´×þE½‰ßV*:xSÕ¦9®K%âQäå±3žÛG‡òçÍÄLöN6ö.6ÆŠ} 3-–|k2%ª‚’FMn­ÀGèÔjÕ³KØ“ÏïYòÑ8~Vþc&F ¨ÊF¢ üB+ø‰5®ýr‚MòWûøÛË«0w&,5¨«J)pÁ0º’âϱ%¢„÷ù𢀗xÛ½ŸpQ†õ çtÂ'VNGýIVÊß*§Ò³î2’æÐñ¿äÅ“gýš3ç°ËùÀµùÊݤfkè¾ÄÛÆÿiè–úeÈT£ƒž}Mqû¬Å—›ŸÄ ŠÓÅ#ì\Mœ¥’ª £˜ÔyÛb¤<ŽÞ儌ݤ/šÈ²Öß"Aº(£E †Sæ$Š?LùÇøå×jD™6§fò_JíÄÂVŸ)m>)r6f¢ß&1¡ ; aºûoµÍ™‡7Öùž£1tàŸ”ÞwÉQyó›Ã(ÿ ¢,„ˆ6Ñ%6û!Ì{Z/Q'ìd¯Å4ºõ3 I¶:¹šûZ©6ƒ+Šw:r5c<:ØX j/ª¦€2œÆCJ˜Ì%óÊ4DgVŒ¤„3Š Gœ( 8¸éÝ¡ÐÉ›×îÌza4×H†Ùªœç‹÷yn„S&ù‘Ø=˜!·ý믢vC.i5í$%E™„eF U×ÒŽ€ U÷¾³ëÈ'ÛáêBºàœ“áAe¡BS‰àk¦ÕcÛãC2VEUÞØjÓÓkŒ˜7^kè[Búv¦^*ãÚÅŠ£"¥ç ?Óp¤A:„S¶õ²lÜ™Êb¶—¢_8Ÿ¾­H3øÏªMŽ ÛIUœÀQØÆ`iw[˜ª›Ñï[° ª°©×½LŒé:Î'X9n~Ïñè™ö–úóÂC±•RÕwÎñ¿CÏý Wð8 F—õU?{É §íÀæ1™Œg=ðu'Y ‚”wý:NÙR©Ž¶BOr7fš¨”Ù!Œ$Ô(.ù˜-dÓ€ô-ˆ‰-HúêŸõúâ:›é±– @ùòÒ{λ"®ûù8Ê œ‡KÍ9I°©`´õÉËÎÝâŸIÌ(ø©Ø>›°; *¼mÍç§7Ø[›‚ËTKö§yµÏ¤>š.ªøhteÖÈ<ÓXºKéÔyaA•‹&i¢sùMléXu?.X1Ê#øn·ó ’$¢=JlÜhãc¿(~SÛ?VžwS-æ}ä_ûÚˆéw»}ï@•ÛÈŠÛJ ´l_™èÚIiÃMÒ¤bh˜|üB›Š›ÓÜÄåªüÆbg­ˆN8kêgÂp¼ÔªîT0ËG·þ»E³ ¼>Âs-“çOó¬ÜÝ—¾ø»d¥4–ÝêRª¦˜×ê½æm²—ñÛ]z‹qy žÖ–/Qr0yU}%bý.ÀÐÛh‡hÜW'™~ƒùç½ý5³Éæ«|ª,Þ7‘oùdNhÝÑÚnœ¬á|HÉ5½îÕs–{&ÃAð>°‹Dú iZ¨ Ÿm×À°ÝêX–לÄÅÒÌÅ  ÅsÃlÑ[.q¹Â"n3çx{ïKFÅýʉ.É/¹· ÆüÛÿ·²Ïâ° K ÇiDŽÁã 7®øÂøp/zÄó•ݶÑýD8 ŽO Iu}WL÷lô÷ïÀ¤¥’¼Ÿ±#àîÞQ35íƒ )éµ`Ð|<5.]†ýÑyá-­„ua'*q×QÄu;æÔ¢‡PUœ÷g­ì„2ÁbÕsnÙˆút¿jíÑwÿ·‹-‚•êm}©’–¾Õ`A´Üjªû“bÞ„ìÀz¬ŒüT<7H)ë‘JÞ:hO:LÌ×&k¹“ÙäðlÝâ2³ÈAÙí ó/)iD[{ŒÈü1ë[ü%\ëc)Azto”ÑŽ”Êæ]^W©(ºïͽ#uÔqI[úº%»Z> endobj 1620 0 obj << /Length1 1612 /Length2 18038 /Length3 0 /Length 18882 /Filter /FlateDecode >> stream xÚ¬¶ctåßÒ.»“ŽÝY±muìtlkÅ6:¶mÛNǶmÛ¶sû¿÷9ç=cßs¿œû~XcüfUͧžª§æ\“”PN‘VÀÈÆ(jcíHËHÇÀ1³2prøec-M«4qü5²Â‘’ ÙõÍl¬…õ\U @h`b0rrr‘„llÝìÍLLÊ ª”ÔÔ4ÿeù'`àö?=w:˜™XÈþ~8-ml­€ÖŽ!þ¯7*GS ÀØÌ’•S—PˆÉ(Ä€Ö@{}K€œ“¥™!@ÚÌhí¤ÛØ,ÿ½ÚX™ýSšÝ_,€>ÀÁhhöwÐÕhû‹` ´·2spøû 0s˜Øë[;þí£ ÀÌÚÐÒÉèíÆ6ÿ"dkoó7Âê¯ï/˜œƒ£ƒ¡½™­#àoV9aÑót4Õwü'·ƒÙ_7ÀÆøo¤‘¡Ó?%ýË÷æ¯×QßÌÚàtuü'—`dæ`k©ïö7÷_0[{³Ñpr0³6ù/4{ ‰¾½‘%ÐÁá/Ì_ìºó_uþ·êõmm-ÝþµÛæ_Qÿ‹ƒ™£ÐÒ˜Ž‘éoNCÇ¿¹M̬áèÿ kc#ÿíFN¶ÿÓç ´ÿWƒ(þ™Ê¿$ôl¬-ÝF@c8zÇ¿)ÿw*Óý÷‰üß ñ‹Àÿ-òþÿ÷?5úßñÿßóüŸÐ¢N––2úVàß àï cüsÇXêÛÿ¿Âõ­Ì,Ýþþ3Pøo’ÿ8Žú›!`mòW:†ÍDÍ\Frfކ¦c}Ë¿ú—]ÙÚhoif ü«è¿š  ed`øŸ’©™¡…õ?­gý· hmôŸäÿŠô/êô’Ò*ÂÔÿy§þ+Jî¯öŽJn¶‰ýR~Ùý¯Å?‚‚6®Ú¿'–‰™Àö7!#£çÿ!Û¿`ÿkýKßÑÞÌ ù·dÆþ?~ÿµÒþkC£fEÑQßÚèïxý/Ã?nC'{û¿ªþëÄÿ-ø®ÿ5è@ +ÐneÑÆ;À<%=Õ±3{p\X³·›|0ж¸^© ϧʦË;%d›³\ï½:®a’ë³ÅmáÔöc_’ê`¸Ã’¼+ x™‹çILÙ“‡¼AÖÆN}àG¯Sü-õL5Âãj^z BƒAå`g\^A§è ²Ùæê‰Ò‡Ø9ÏäÑÑË0¹.½ý{JMþéYüñÓ#yÿÈÐà@× dÏ>.uV4,)·3Ä7²p»,\> årPJ¨Ô?¸7­WŸª:9¤$8$aVÒ¯r&¹«Ö­Òï ©­|<ñ¿ìÅ›÷+Ô0®g'Ãyâ'9g²¿ÖœHE0yþàMø÷ÞN[øO¥C×ý|»¦èuñ¨±ª¢zíAÌžš’o/®si‚Ú'e  uª@j¶u u×Z”2¾—d†XG'ö?Ϋð¶uÏXTŸIekÛפcÉÑ6Ÿ ŸüaË×o¸î›ôÿs­!3&N\x&ïÖæuîÜ‹K[ê óÈ®¦ _™rOßP¬ÜÞ$=§ô”0ÜYØ‚ÔbŠ»)BâÅBCŒˆa0—1¬ùuÚw”æºi’R~âë~äÔA7=—ô¹¤§Â,Ë%±°R+ o#+»k|Rc®Žâ*M"Œ¬.´{]AœÌáñåÆˆÐ j{E‚Óª·f¥ÝÍyž#ò¶€´xÓµxDwG6‹äiOþs¬‡·#‹p2#ÄB½œ­6È÷&Ÿî@Yg:t< ¨?Ë¿=ú‘ßâÆñ,'ï•·ünmü]\IŸU¥ùû+Á#r‹Q" „íÓçuàæ özDÀ¸ª)/!j&š}X>9ñÎŒ.Õó5 ÑTõšS[á½ýú4í/©a[ÒŽfqÎñ”±§€o§uñ¾øû×ìâÌ»ý{çhýâh^¹h’ÉHËŒFâU5uåµÏN‰Z_PlÚ×z¡’¿Ò…pÄNãhAÈWW°òA û0]1˜!Ë%˜âª æœ:"Ùî6¿S¹ 8ËWIC:ÜÐB¸ùØ)c%Ývm® YÿðÕ°Jðû†Po}l±-·ÇŽ‹;XÕxõ'dO9‹Î. ¯8^M8åØÌb‰–ìÍp`dÒ œUîtÇœ¢½äI<FÅ•!Ø>ܶdòi›Ó!}´”-.§±®Ô=ëdâ³uÍv½JH¸É¤8•pHØÚ!ým ß+bŒH9ØO¯r! ™ÐSŒ|ªÛ19®ù-ECªÖ£êˆ4`Ëg ÷ማ9/ÝCØPù±“y7”¤£C… ¾Ä+áV0cíóûÙ(*ÊŠ°?eÖ±²gÅý;+ÃU‹Ù†²rw$˜••e9\LQ(Þ`.çd×FCÕµb^ñD¥aΚ•†eœ'/ÌOz›·ÓT´¶ •Ê[¦ÒÞ(€{ý é‡¯»Ç’ú¶s6þôßf(¤¬ÙUÙˆu,¹0lå,Æ\høEé¢ó8­âÛÍØ¶Ó7À+€Ñ.°d°™œÇm-ÕßíKËÌ%R,ÊLó 0dîžd¬‘+^Å'ÅÆñä0qeƒ\ž1¸Ñظ¡vŽjk¬(_ààÿ!R=ÇM>ÒÿCmŸh ±£1÷ê=†éÄ›ÉòLWKâñ°6žÎ+M˜Ò˜¯IïuÕÎ[D·Q\!-$çj4ØEƒ©¾C9È–ÆÄ€˜á¡Q^‹8Q£¥€@çCŒ#ˆöHy*ˆp|Gõù¶ë“'Øg{ºg·ˆï2ïëH8ÎRÕhú¼ˆØ“ðcËcO'š$1ÒÕãÛņÈë ‘Üì!³g¢»åÕ'É™¢rcSü>”Ö1{âµ4î·µ´Ã¨QmC®}üTÔŠnѽc- PB´Çq"ÖÉÕ ÝÞæ1N”ò&ÛabŃ•_z^۳ȖSK[Kæd/æÞSS ßa±–îMÏ2ÛÊÍfü6ø_?—ÏEuý¢ L°à—…öz©Æ¡Ñ]q›Ò’‘Ѭ*ÎJ”uÁç|¹·‘°¦ÜÓ‚M-ÐbábÙ¿gpÀæ·®Ÿ 9I뺡6ø¾Ô õfBEîï(p¾ÀDÔ~ºÅ${ ÷qv„+2_Wìx¥Ë¡Øz‹˜ƒËÄM˜1fÚ'ÕTJEÂ*i>Õ§¥—G^ëñwª=äóçX0sPR§c׫|w´÷˜ÎY„Ìë),ya  õñm1köqI)Ð]\Hxù˜»G†ÐÉèÿC— “üý'ˆxOu AÏbÈüp³Á%««r¸òÔ27Ÿ2’n}L— Ë>WÀ”ÿÔ#hiÖõb`²gµ,'jçg“ ?eÕ©l¥R‚«žz¨s‡®Xý )B$‹# z>yØì%`ZE ºl ˜6±•댻H¾| ,Ö_˜PÈLÜ<¤óRxdÖoëKÈ~CaŒêÖ4[è,ÜŸ‘³ïÚ”n€â$ñq¨ón)û„Â¥©Ê»!†5Au6Ž=°>=ûuÑP¾ûót‚|õË“~ïs51It“/œ}ç°0ÿ •}©’—%ø«ÀBiŽAC)(bYùr¯‚'ºÍÔvè—±e•:ÇN‡P-jœ_'ê閕ဌßÒ$o¬‡PÓÇr ÕšVà‹›UÎ}èk.…ÁõGgìÐL£"5q-«i³Z\ …x·j™—1ouT·—iØq§‘oÅ/–2Õ¤þLÀqN÷ ¡÷KTBCš…sŠÁ ^‚¡Ó—P'^Å +- Šf´SoëÄI$úö‰éâº~ÕŸþ`óRóï+ôE~Uû Fœ?9é·Ù²9”™0ú§|IÒî6rP¤kêîê UìïšÒÅ7ê~ømq§yn«½ØwxÀ.û×¾3Ê’£ïã óÕV#ïNÔìÚtå5îJ¤Æ ê\Â×¼/;B¸¹h¯~Ùp¬Fí˜KÐ]ò:tÛM†0ä†í •.¥±Í4ãôá^LX–‰ÁVàjö /)"9#êÓæ‹çðž. -E>‘iÆ—· ’ ©Zܳ]8Ü€LŠƒk¿(GêZ2{ Õ¡ë•-5=Í•¡ õé#;)Ù¸Ê*p~‡!rÊh¡,};¹ÓÇ,ÇQ-^qÝç!È=ÌSæü Þ¶<7Ë%WÓOnÌ‘—º]èâ/IÁÒh6ö¦¸ Žq,o 5Q´óñÛ*—èÙ¦èÜúaÇtµô>An68kúºš”¿¼:Ø·8=úïè²Yž„1¼5(a FKt%ãQ=™Ÿ>§å8P‡‡R,N®wŽr<½šBÎÕš×N:`;üP`ŒæG-ÚJ}ž™ÙáMºs J‰º@Î*î{¶Ê 6ï©@BÃ1þŠ^šD”<Æoÿ•µg ?¿f ÂÄ%`¬¡Uß)®C¶Ÿ¦ã ‡QoúÎÊdQ„"üe6üª@í¼“Ó˜zÆÏòÊIþ‹.ŸY‚|ýÞŸ}`K¹âK¿v %'›¥kI枟m-¸Ý.}]ï`á ýûÕíÊ{„ †;©Ð$ØyEЧŠn7Ÿíg‘±FxÃÄ‚}G C4Mƒ¨ðB€žõ•†Wp1(>/LO–³­KÛêÞí¾¦e®îñ„˜hŒkL/fþÉ~BbÃ)u–„QÂw&–_âyl/Ð Ë0‹}DH½ë¼ý:d3q{( ÷£¶&~‹nDoÛ %m©3Ü*QŒhÊ5èoA3ñóVU‘ /+|£­0¨rF®ÚuªÐ÷Òá7ÆÜ¸ÌU]ü$Œä?­Eñ”º)Ð[-ƒâªàÒ=¸†ᥭ%Ó¿ß´Ò$íÞèªÖ–Î&{£zýJFŒ‚UÄ 1˜ŽóÕŒ?¯/MS׿Ù;O»º@Ká!ÂZƒ„‘ öd„‚Zòóš-¿‰”^VÌ ‚„êóéµkH‹Ê/Òæu1ø÷¿’ñŸ÷÷êó‘t9ûz܈sºÊL#Îê@WH2'TÆ0›¶$3~UÕƒ`«žë4 î <ØÃ„[z¾O¤kቼ»¶·m‘ã*ŠôK åXìë5úye‡˜@wöBr¬Ì邳[gÒ«®gÝ7Ÿ¸m‰6[Ù%Å'Á߯÷:Kv_ÌÅÓç´¡æVÀ‚Ìôä2‘l^p»J â¦û~·¸¨ØˆÑ×ÿµ©>[Í#L$Á4«ƒØt2˜³ŸÒc²@Y•@h#ì?°B,R¶dU­·°{fÞÓ°vãÕ?©H|¹srõæ h@pŸ\ì-Ï¢çZ·q–dÈ‘ ¾ižÁ~´JÌS¬05öS°‹:N0y]˜=Ì`ˆgZoö`2Õ !¸ÿzð¡v¯]d8£MA õ:nXGúú gíÅëÁÖ(hš“DîHùµPbìl l¶×¾G˶µiÎàÉR`iJ—únÛœ8œS ï×u[+ÙTRiÖ©ˆ.ïÙÔõ«I;«x.®^?‡¸fì’qÓ…Æ×yVmä™ÕÜ.ÕõJ.2ìHJih \Rí#Â=ß×활òÈ­a˜’º{% „™C1l¼ÓY®Ú‡ÍCH"]›vª\„) à©™D2S9ô›ºõà>ƒDÇŸ ™èÞÅÁÎ.>N³þðHbÁôEŠhÅÖžsÝ{R<57ï§¥”— [wGÀ÷¹g©r)÷$¬%Üs8ÞÚ"ì`ïþPߘ!º~z÷^F/y¾’ú%VÞ´”ƒ£ELWt¼¡®µŒP‹rD_Pã>£Îõ%³†ƒJwõ“ü&D n¢æø ²:Eòƒ´Ùº'ëâ©ìZ]+q¬hG0²é´6bB†ÇI˜óŠ ªcmH3GŒ’Ü6äV¼!B½{øgšÕfX+]rÁlgÎ ¢kÕÕúY†Û‚Ý'Kº_üÞil|³´Cuª_ƒØ? áå×'¢Ÿ™œ:$ž‹ËIpèk$G,õ]‘ÊT&st­wǽ±p^˜°xXf·ØoìvQÜIå®_XLv’Iõ×êw•— hWJ«SÙžÌ*7dÑ÷ñýS§Ù\*ÿ‘–'?ˆ32¦G˜>?­ßy{3þPŽ\Ý{¤Ã/+4A‘àZŒˆ¤ "5pQdîÎåDKDŸÊã+ȶj"ä¼³l4ؼc`ÔÂ_šðo%,áì•ÇÀ* Å=çç]º¹oÏ›+«ÝÉ÷þЦ°Ä®'˜Ù«õY[šáúµ”9¸êänw‘°”‰+vûµ ± ¦ÓÍŒ³yÄݸ±1-Èœè!'M%Š|+ärQFd#űx–1¦Õ œ­­¼XðüX¦oÁÏ÷ó☰7—ûâô—þ1¸¹ÉDÓm`ê~»3‘ü· ò;Üzœ©œù¡=Ẻäx¯ <úí,¸—‚é#ú è>JH× kÙŸYf¿¬T$÷™­ˆ°g¹­Lh…›á&\Å?£Äœ_¾žЉ¦¶ýïÛ §eî t H±”v»õH¸ƒ³8'gÇ)Î_ßš°" ï_a̫ވ‰c–ˆ3ˆe/$O,U‘ÁT ðM%·zmɉ-!šˆå´©6*Äý0ÿXžæ³‚,Jxptön½È¶i=ó‹né`-qcv)äS²¸ßù®J„ˆÞõ imŒ;yÎ* Y†M•'yU[\íÿõ]ï„WÌØ5Ecý`‘¨é#„–šòîV4ë+HüPx¦?듎îóÁƒåØN^‡} šíù3KŽü#™ÂÙ“´½æÜ@ ‘‡ü„B[|왂xM?A¯0Ÿ«çåe— b£HGÞ-É‹üヨŽÜdEœf¹†*sXÉ}K¯ƒeB].ØêÃòF%{µ¢ýšÝKF1Ïc Ôî•]æz87„©ÇÐ* º‘µ*s8Uf˜YäDÄä:8xûæGQf?vÿoo9ˆ_‘ÕÏ"ãH–ÃZ„ÐÏdrßK¦’—ò(þqÅq` :a;á)e4á ¨käýÜŕŸU ­*HUi¤áòÔ)¶( ˆh&ˆEzíbõ†AÑàãZ?Ù>V)Ùƒúš3¿†ñ— œ£Ê§3²'Vh.åã! ëÇ&p¿ßoQâ-õCñ"¹#ï2‰ªÃAóö+[¾–ÝàhŸ.ãnÈVë)ó%=@Äüv #ê Ç=g°ÆBÍfΩl†< ma½{F•LoMú䮫® *…Ç_8h>X¸XÊŒ+™Ÿ!â¦?œêëB«ÁFEL¬Ð8Vö™ºµò»Ñ®êŸ_ÇR­¦%Ìì É”R [ô6Ÿ“ÞûMÇg¦ + ÂÇð‚´ã=¡M ±öiND§¢$ß7áj¨-s³ ôóô2½ µJŸã|¿žh¯j«— 9œHoyUµ›‡ !SÚŠê ‹Vl²‰å¨È†wd"×$/ßìŽïR¸ÁñÀ§·.ã`R¹Áûèž1Ú±ÉHÂX±Â×YòŽo}NšDÜÙÚær8¾ œ>Y·Ÿ°ødÇoÅ£®£lOë9ëL¥·´ºÂ×¶‰÷æy=±ÈöNñèlM4å©'ÊÖ\»©Ô7ª¥@­>ŒUeCž¤Y„+'.G}™,ã ¼ˆ„VóŠåqÃ|éÀçuIÓÁÕVYë÷ )7ˆù àjÇÚ'*®!ôȉÑ6j òdâöâ”ö^ÿŒ¢C\ Öƒ™ñ;øÁ±iV¹'¯ ·¢îäŠÉÉ‚Ømošá¿àI¤ ¦h)re|Q® ~Ö3ðz8†pŠ[À‰SJ1ܨ$ÎZÝÄýmÂNb=Ò<)EcåKW9y2£S ÜØo™G2t";#¬åÌêRâË`+ª³UèõÝ©þ6Ckæÿš™ÈQÏw ?ÕåÙ<78£©ÏcçvOÖOOi¡ÊËy·ía¼#vRúE‰ïxÚÙÂbÙí±.䤭ÄL–®ßÖ¾Û.ÿžËÁ±ßñ7¦Æue¶#0‘‡zÀ¡ýqÿÏå7ú‚ vÈê7.È.Ñ»ß VF÷Ä‘ J«»£:ï:"ä:¹8çßu·èV¤1ÇÒÓ%(ÖÏà§{¯ªs¸QÕ~rMãfý‘¨[ õ½ÂŽ 8¨› ~3olÉnsT A4?.¸Lh³(KÔÎúj;™šËµz ý%‰i‹ôK³t&´=êÜ6¦¡ 4’Uú%%;väzvhÑ!M†ßÑ’ƒµû§’C²k)ÛÈ1£Èƒ9TS>•KÛsæaÜ¥ˆê[¨¨f¬${6æÐð&@ä¸DÏ++é-?ʆê.[°®@bí»S“Ø¥Q4ž!hÿ㲂BîÔÆqï$Úç†%Áw¬tÔVÎOµLGÆYšÊ²¾îAžwî׆5ü^Ž‘yàñÖ}‰u÷“òDš3„ös—µ"4e Ûl=G¶Ä~·h«¸—I5Œ’+ðŽŠ!†U4£·üwöNÀ4ÿ>ô[ÝÊn¼ªi­šƒpAúçÓ/~>ÝÕ$wÙvý¬Ê7«?j‡8Ö/úl_)¢D)9¨ÏÓõ¯<=_ÜÇJþdUçQYÍÇ–Ã¥P´©Ë÷ýÆï ¾çz´ Ž+(_×µpšß?oÄëÝvzu•ºjOn‚bÑ›¾CM55=hÛ!ÛZ’eŠFömH[P‘‰ÙøÈ®î*ëÜ% IÊíJV­_N|i›9‡Æc( ÏEËìž+¹>ºøzzwãÛy.#¼òÞ-]뫪ãnu- Üžµ¢”Y8iˆÌ Jú; ›€Ûäü)…k×ôZ–BØ-ºÛ 3S™§¼¡Ô¬ž=exç¢^N¹¶?Òçf\/~Tùé“—6¢I©$Q{èÃÏ4H0¾{üP‡‘R‹ÛX!:Þ.põÿa®—»¹îFÎv™Øó9ko`¸ö‘ÄZk‰øî³#TÀ†€â·Š:Ö´“\$X¿jÖfß]øtç’NõÒk‚vo)´ä9,«síë®aÿ„6ݸì´6s>|¬ñbž«‡©¹í>ž+‡CÄnáŸóióÏM°c|dŒíFÇ`äÚ›k#…ÿ¥ÕªyjË¡ŽŸµægñÓµ·|%ÃDÚTOa®§=-  qQ£Û^=æŒôIõ5K†?¨òÑSL©ìx  †Í{Ô__ö‹d©,í#ôò)qf[5+þš¦2•Ì¢@¥)¢ÚÇÜÔÚeò«Î˜£am”É]³ÞÊ›;Ì€„ ¶G\"ˆívÖö'Fq•Us*oó¡?ö¤%)OzPÓ‘Má•Ìì«§ÞHÀø¤Dúǵ°tŽý×Àêã 2:ߌɗ;Ã÷ñLX¢;éU6ÅÃLӧЂþFê¿/>9Ö a'}8(^«KPSw{Ìà×ב«Ì›hñ?ïcò°N´T1ü÷Þ…Íß“>,uzQÊYjTè>Ù8Ùe°ys¡UDb&ÚØ0¯ö×Ròü o¨‹¡‡î¯ŽÇ#SæˆWªõŽ)ö•’W‹„Ö(÷JŸ_è_Ù_üeAÈãNc‘ÔÒa©à´Z|l ñsê|&ò]'й†û¨{4%߃” áõgDŠª:Âö~½~‘¤Kþì0ØèЬÅgPÕÜ–[á^ƒ6Ù×êË]:œ ¨‚~–Ù}0æ«.Æ4ŒB˜d>ŠÖ‹Sv¯ç?§ÝtÕ@ÏVÛòó-u“¬×'ËMÍqƒô¬¨0yÕP¸5çf ×Y–íôØc2†Ãmo@:jÌZíQà‰>¢‚¿Ú W‹0ª™·øtdW¢|`ˆ»|Ãst[ܦä‰ÙLÅ׉êi(ti?V¡ÆÀþZ‘ÿƒ«§H 2äe!\Ù Ì©Ê k ‹@nÞÛW$}ÙÉÚsDsUñúšF­îuK S›ˆ˜ŠÉ%¦*ˆJL±H— fuÎvEy‘jáßL7HÝÁ|’urå ö€V—ŠÛà1!z•«˜£rp7û~&ÅÿlwýC¹ž1æœÞÙzXkÙ(î²ÎÓ„êt#%b+DÚï¸ji¿"xÑ6ìYÁÄdwHÁ0`7ëhœ[qqC]hƒ‘ìȾ£ñ’ËrקЇ›½/Ê«‰N’ãa öµÚïöB¦ª*e“ûÝŠ¿À*Á2<7Å Ž8û|~£Sb¯çPêâ(ùñ;;A"rŽ »óG®¯è.俦2šÃ±ÚXD“ ¬òPGýð£Ex‡B×Y´D’JSÃ9zG²¤‰>+­žŽñnmÕ Û½µUjöüÁ*ußâ6߉ª:®«u0ÌUÛû€/Y ^Ž“;$[,–0Å•Õ8 UvÇü‚GKyÞ-¬¤¸%qìWç¡qö]ÑìóþfîD#á÷çEa4|¾ŽŠƒéàÏ("4SPl˜Î Ή¶¶Í—áà ;þvTiags뱨¾É,ÿûOÓòÃ-ES˜Ž ûçGÂÁÐé|¯ß'ãþ=î< ª½…MYÒ3b†¹EÕ†>V tù¨tÍ¢ØWô\“œU.O,[\þ^Þùc]é;Ëâ.ÒÇ;]^5ôth²²k¢6ÎüÕo=»I§j$Õ“’½]C´»˜ÀZð$x¬n(ÛšÀ»¶z¬ýß^J¹¨´ ÊŠ¹Òê TX‚zo¡ ¥—YÖ"†t„ÓÝÙév²³#0ë=?Js ™|ŠnŒod£¥Þy§: ކ1ãìBhqŇ®õ_r<†óÏA^íkºyŸãºâôD\ÓÆ¼õ±óÈ‚Ìô¡ôtš9#—DO¹¹{§‚ySÈršc ÚŒ®è x9ùQJ!èÖw±ÇDÖXjªÁµ)! Û/. &¡8ýäfçEÜlSÙ¯G>Ò9UKi"Ì!V’ù¾eŠYœgq=£uö10AÑ«QFPýÒKzv¥£,ö±hïÔßuÜ£FoN¸J¥¬P Øç¢š÷ñ)l¾ùÐjœ©z£®ò ›Ù|c‚f0¾Tv# BRE~&r Ú‹'އ>M4Û»|q”µ·–Qï¡Ö_N|\dã=”¼…ÊÇ‚ãivÂÖ \ ÒЩü ;üQ¶]¼ Ì“mµáhÖŽFŪ¹ÑïÈ2# Esˆ›aÏ=އïhwWÄÈÇ¡ã¯NØkµÙ"ñIø'¾_ùPŸEù* Å‹l=ïkñ66DôkF}sB6îvQ'~§UÎ<D Âî!߯¯àEAŠàüfµüÊ@0fœàóúøë€-¡;H¾¯•å`ÆE!»¨qÀ-Á¶bÈa1ê:©?¢Lq&CßÙcS„-š>¢$Ä)øäÓ·©þ¹šÒÏê¨Pç O ;}¦–|»Õ3iZF¿–[Ì×–ÆÎ6 6¼¦Ð•¬—–I…³/m)¸VÝÛÈ¥RI´¹¨r@Ç©®Ê}ÔÑ¡Š'„{iI§×MƃíÛLUHm ûMöº… HO€tyêµ UGkˆõZæ~‡4¥÷““ §öIÕLº¾ò‚ {ºË¡HRÃVC¥ô½˜Aìw9÷/ËÅTµ‹‘@Äû¤!J` hrj«µx×"¡f\ßd;O·3I8üÌeVXí;F_ÑWCï:~×9ù1 Ì÷{/\H,x @?¼°& Íí~£—xSs­{t&VÌ&qúŒf¨|¢“Ü`”Qº0Í~…mÇ“ÂCÖBž¹ãgvÞL½¦âI9<%zÔDÍä»úÕÚ/ÅíÕT/’Šü>5K Ÿõô§É÷9’=¥^ ÿóæÉºpoœ3‚`ÆÉaez¾‡é‰Ég‹õÌ\ΆÆÊ56"鶦GÈSƒÛÁ’—«ê¦N`™§³HhµÆuÏèO6¶Øwbn!³¥ûGL)o¹# Tç>klg)ÏEß ¤$Lntf ûp¶Â®¨='¬Ì–uWCž˜nHâàFÇÓ¦%ë‚oQFìO–”ô1â/vR"kçÒw&hŒØTžt9cbw\\õNž4‹øßDÔŽvòìCï\2@ý Оa•¡Ç¸½W‰¢bN~äG'–Àp8ȘáöXʱr“¤(…\Aa/Yf±ÆpN˜ñv¢DÁà=›i:‹`â þ´Ôf ­U˜Äþø£LAùu·Ó6Ÿ«”jÌÕ³DD ÞPzî€Æ“Is¯:¸Å7ò"=8¶%§œAûµPZõ‰sèЫ6-‹‚üäW‡Ôñó¹DäËþ]î9hð¢ñ§55!÷NìÙ.AVÈïÓ1B›°GĦÏ)gÎ ×…ŸÞ¨Ô^X²ÁZë´0ßLºó~hN m×Á”!6K<¾¥që„~·a)$"gNGvÄ)H#•"xφ…°f½¨Š´W‚q'¸¬ôV×ì’Ÿ«¯ÞAck;Ž¥ÕbT¬L Æ Âà¢ì+& `"óög¸ …d¸Ë̬@t÷Ä"„€üö¯6”ÏÒˆ°6øó%ѼмY«ÔXøXÓË-n¼»K[Ô×QH­ãC#¿nžòÔ·$C ‚†Ó£V÷©´’ˆ¢:®;òÊw~¥m r™Y„‡×2®Éú˜[ÿhñ:‚_È‚a{|»Qiö5Ìe&“æDÂç'9NªÜ ¤“N†Â‰4T¸ ÇŸ S-‡ŨµŽ)ò²±þJ§p°ÔEx8¢7ôwöHíƒ'¸ ¢³vƒz3)³ÉŠ³Ôºd%üøz.X~¤¸L9ßäÝxU<ç-fyS†Ö:xäÙ3vnsƒöå_¯Jóš;28F™«{1ªºÝŽEÄÐ+m9ÃCLýÉŽ}®Ä¹C»‘ŠxEû-åüt@”OCK†êVDvñ#zØÔÙCÅê(¬vœWìy©Ãƒ¤¬µò¼ÿxA€p³ÅµÉ#˜¢5„*# Ü’‡ð$ôy®ÐòDYªd yÏ9ÇôU 9½Uœûf¶Öj¸Ã  aEjY¨`¯Á·§4à(Bš¬øä?Vpµ!œw’¼fšq¼k]ù¢Âß m&Ý(r,>'„¡ qÅx«$æä¸Òõ§Ý6ùŠe²úÛÞ‘läí“.;¿êïó Ô¨æ=OüIL$º ûÙ ‰+‰@ }W±n ‘b§·˜Ÿ‰¶C"7:û®¬¢ëb3ô&…ÑÑ#oÿܸw¬°’ôï_âYmëÑ©#ÅÖ³ü­‡L‰šæ4› ê—]5­:èÀ!Wyq¥ÐMM8¸28÷2îˆL§ä ̇Ý%M-"¯K($jŽ}˜¥Š–š[©’Â:TÞIÅL ¥½XûÆJlvNÏþC’ë\¡e´Xšê4(ÒÒÙ)ÄÞˆñÇfÉPåOYЬï¢1¾#õ¦^Ñd¬ViqHã"[˜³W“i·l6Œ[EíŽî(.,vFë/fåLß\žàgíŸ_îhù9àƒÊvYr©‘ÄÕA¯y´í¼Üžî, üœw]d8éG‰ßp+wÅs+dßEbížÒóÞº²¾ŒïÿZŠgðŽù©³pjI¯|²VÏ^-wQhÿR2x5m\¥mIž>SBš ëüF@š\‰ÇÛø+QgÝ ™Ùc5 ÊÈÎ5öü†H`÷¬œÏ âc)é—\×2v@=“¸5j|ñkd>MIY>öw¤x?Óz»ž­ª„ à]Ô›0ßUøvý°øÅàŸ•7/Z°4Šï}“J-_Ë—ñôIv¾£H7?ƒòxµè:0CU’*¬ µ;ër‰%WSå¡À’^O`y·FvÕø/¢òް8ÁF®ÁµZ ¥Êó¼¹ñ‰ƒæ\³wHoŒâ£¼”O%æ’ðl#Éa¯N’Ì_GÝÓãרý½®·r!#C Ëf’!ÐÂ"AÐÔ3¢Š@ªîK"y¨&Ž ÌG¾®•k‚G2ì_í7ö2ú\{»*+ùÙ0µ­y)úñ z±Ùÿ(‡¾—ÇÉ"“Y?nâ%Îú¥FÑ>Ò`ÎHðFeþƒaïð]3TôF—9Òsæ§-wü•K£·¬(õFµï=NiŽì®îŠ&ùØ7,bZIK5Ó´Ò¯Ó‹ï›ç~Dp–(GG z†Çè_†ÈÎ0Pr½¾ý÷æùºu˜ÆYPøx™þP˜аV6ÏI·#1òêåÚÄ`úýáT×¢aŒBDu:œç{G`T$+(Qƒ/åíiãç*žÓ[-¢ [ëµþ°‚ƒZíX¿ñ©žEiE ­€MßêìP—iÊ—´Á‹šÎ6é]bS€!ž2Ïeîšä!ŽÊÞ¿?¡4ÆT÷·ý¨cè^ìèÜB„„ ­¢d’)ö™? èdÏ0*¸¬™€˜ ,*”…RÁâõ–¦@)­‰U÷K°ài Pèøú&Ë µ£ïÛ!†+Ä,ZB¤@( ´ÃEò—Z±Èßê¡ ²±¸ü 2aú*¢òuPåÕÙì’Ç™Ø{·Úp¯C`+ƒlš–_PE"²<ØÀñ”_ÏiþmHÔcûá»Â¹ù ÑSäñ÷î¼DžÒ‘k±Ø¹ rÏÀ¹KWµeËUm@höЦ üûzä»t¶Óñ 2gÁUþ|;®‘Bq¶f7…[ Ýh^¦K§hX÷îá=Ê*¶Óó ”Þ/y¡™åzƒ¨iC±ì^ú’o% ÍuÉ7{Iû†³«d6g±†¤øòåIQÔ­zz5±—|¤ö=ð¹MlVŽŽòÓ›ª€([/ ­´Âßkh»:ßS³š|{(áòY?mPI1ÆéŠŠê$"‰w˜-ÿð_•³©«Kà¢Áh÷A™aH“ I%”{0]3ÚÏèÇ©¿Cµ=`RT™”s…ßô×#žë€2)Ϲ6:tôüú9¸ÀÿÝ2…¨+†5EJoîæÓtx…ÅdÄA}‘•Y‚†¿¨2”â r´Ñ‚mb­$ýÔi±¡ºkä¥,eAÞJž‰æÐÒm>èÊ–%–~n¥Øˆ_4Íës‡³ŠQÆGËüû¶2î’ÅCw·Â‹qgyi>M,ÉŠ-QȆÁÖýˆ/>œX»hÂ\µ=î)4 Áç/\›ý]@±ªÆÅå²–¤ +-]¼ÌX½•Èf„F{ïµ³Ûúú†ÂÖ‹ÚíÃgï¿â:þ†'†À•ªSðA1öýµ˜”+ÒÓÙZ?hªà›’cyþ¥ßûƒ9àŽ{Ô |cJ¨±÷|CÌD‡ˆ\$»®qáòÅUï]›¿Œ 8 cI:~&6ƒ¥Ç̰iÝŒ°ä¡ã£Õ„/IÓ§œŠ.ÈïÀ®:²¸ÉÝL »Ø OÆéséăÖAgQŒæmùÂ\Ì ||¹ä Š÷̰‚¶Ê/âZ;Kð¶`ÓÚ ³¼ÉÎH,rð)Ž^U¦_‹Í' @éŠîQhþPÕÂqo\(¶?1ºPè˜];û «Ó’ñ7²Gž>h=é1P•mXUp:cAû÷äÝŒ,ÊJFZ$žÆMz„.?^-šÆ,}gSÚ‘Ë¿\×'òÛv¼È¿J‚u$Äx"‰ÖT î´ÒÆ"ÌRVUéM™ ¶ YÑ·5ǥ®$ÉoäL–t³ ‡‰y]]|}(´ØÁÖô•ÝäyöƒÖíÆòÛçFm|B‘×,?o&ô|3ä Ë?fmœ+âd?Ê4óÉ¥',ݤL•K|ÕV‡k §Pì¬j®ÒÁ³©/ Õ°ôÆòRœ\瘂A©žÍ†sÜ›LNÓȰ-H {+©½bÏ©ëè+£É?àþ‡1>ÓjEÚâªÚ:øch{!Åo'"ðú]FH§+:–+rYì!Ë«æÄ$³ B¶í2,¤\±Fh±˜âE±PxßˈO-O~ˆhæ¬`ù1´eF ØÈ¾Á)ÒkŒyŽ…š¶›jhb…(ïÖoX~£¢'ïÚåUíìµ$zWBþÍNþÕ· §ÜÊýuÂ$1Ê8 *R¿=dñ¼ ™»ì=zülG‡{>Ô—» ÊHmrµ?:2àj¤æ \2é¦}›BÞVƒõçŒt–‰¸M=}vÿTØ„dÅøã“ÝUÕ3yûZÃÞ©9¯Ë!ÉßZ¿çôÉÑ1½¯vAÐÂVo¤Cy¾“rRͯEÖ)‹äÀA*±Õ—P${|œá£iêÙTʶҳîêKÕ[äšó›„µUH Y9[/ð·‘jÌ2YB£ûµ¿>öœmäBó„0yœ‡³í¢ñ÷`±y£ —3a-Ø’êÈ‚¶¼4¥ñº>LH¸t‡ÝIaÔ•üË˜ŠÆdèŠÄIëë²øÖÌΔשü½¾Dȱ!‹ØµpQi&Áõ˘´M4>Éþ[fåfn9aŸ;ðúË~ £èXL¡¤áEj¨6o?Õå¾UݳZ"!Ù«'HÝh®áä<ñm9²·ßÊ 9™µËià`ÜÇû×à ¡õi] øï@$¿Ò3—ºiû%Úþ7S³õ½Jò_yÔï‹9¶ X}!Jöª}WèM°ìÆreôF¨L ‹bE-è Aj•º bÛqgeÓa‹õÞ"…÷!¿Ú4•\ >z/ܾcÀrM¢¾æ² ブjyCOë‰Eqö‰L£¸ês=A(40U^pÉ„fu´Ë!h×£ ‘>A¿xñ7‘å+e>t˜úiÑn§‰‰óÌ”Teh2Ÿ”ê!ï‹5g˜öÛ´Åi==Š9K…ðÓØ˜¾É[²äu|‡§nÕw~gIÿ°7ƒ«ûÝFek¯Ž­ó¸…¨JSéYp9Ão›@ F!}\3OŽ6¤ ?ÂŽ?·ô›š_°¡±”;6óec0ȳ¡ËMå²æÒ êÂ6Þ´‘“úåÃõ3¦Sr$OàÑ*”ÎÚó7i3âg›Ê‘¢?Š_@?S¿†so$UMß½‰-%­W·µŠÀDÜž4ß¿xüÂ’啌VßÄX«˜0Y¥“—h †[Z²wåU_fät?${xp+°¡J‚‰ÿ Ô,lSˆŒSŽ©¦ß+•s”Ô@íDô4,$È©†’ãë†åª|=tVGY ®5´½[EE¯Ö³Vø·!nƒZÓËóËéô2ÿv;¢®L:H³›pB,’ñHa‹HÓ2|DÎÕ¸ìÕkÆq×ý@£Öæâ‡`gú¹#¿†k£÷cÛñ!ü”¤÷[Jšw@ÖÕ×Eȶ²¡¾&W`üåøž®ãtë¾¢”ö!/3ÍæÚ°WÆIq6~ÊHèX,ØKžöS1Ž •k¬¦Ë/p]¬OÊÙì‚D:”ÚžYqVÃ}vËË1F-áÌZg¿‰¢Gœ[:<>up)ÕÝë­@à HHÐõkVTNqÒ%ÆêVsrê÷Zô2ï~>Æ„šð…$Å9Ó¢li`pà…h@;•‚ŒÏÝúñ].S—Õ§¯ÓÚ貘àNx"#ÖFZ Ä]6Wxêz¨ß͈ÝpežzQpóKvÕÁ.ô±»y2×Ã&D:Лñ÷cMÑíÖ3àcâ£|›R·*CR&Ñ@Atb“<8]€v§-yw- ïÙÈ¢¯Þ2å ¸Ú4Hœ²\O5ó–f˜H¢y|†¤°òõ±ç†Í+=B¬“çËË'µ£ WÕ¦ð®gµPÍ‚ž„÷ Š®3Pv“m;µÿ>§ò.g댢 MuX.Ì*ùd£ø £æCcâzb𼞉ÓÇëb„2³‹êM JNÆœ’Ìþ~ÔŠ÷½ \óUP¸C:çœ~w'NÀ›3óte–:UÔr †`vºÁÿtÿÂ9£ö*_ô¡Z"êö»~Ÿÿ½ª_4f¯HÌñÁåIézê¬6ŸPSÊ?¬Ös; åŒ-w-Ø[WÕï µ‹3àžÐap?!û‡ø–$7:îJ]u¯ ¼à½ðô!ôfÉÉlÆI÷ÜaPsQQ²G¨í½DHI iíŽ:ë+!¼Ð \ üÔ?=r µ€ ƒ:iA­ H 8Ó  Îh©`î¦4h‹ÄZJm¾Ñ&s!OpM•õ/nG MëXa‡N¡ŒñTæJúîo^\Õ¥¤)]Ë"È‹Þ8¥oªæ8k™8Ð÷¬§º6Ì 5“7§ƒî“l¾¼rëa{2Ù¦û/«ÊÃ{pÚÝÎ’JòXúèç-‡‰cÚ{¨ûß©9L"œsü2Ž<]Yö°oX}Ñ\êֿˈÔÀc«Ï}8œá@¿¨‡BïÊP?‚­XŽÝ‚ár èÑti4Â6M'ùjP¡‰rg \uÉlÇÒ¿RæµË¶K.DPe"(¶EÑë?4«_e ^4L]ZlÄv\f¹66Îü…–CHEÙk[uˆVµI] T9´™‰ÿèÐ=PÖ½Î;x†åÊ B¦(y„ô¡–ë÷ -§³sº²óß—;59™±ã›™á{¾1Û…ÊÁiâúÇà—ÀÌôý?ºE÷Ñ_é¥ÓÛYÖ3ó`䯙¤ÁJ²z4â õ¬ªcÅÿdYË‹Janc™z‘DƒèJÆ*¦h•+<Ï] }€^Þ÷˜n§µ=íj˜ŠÖ3[A^¾ÙϳÈòiÄÚOÓR&Jú¤‰y<"#(K2Ù¯ß Bà9”é=T„$F²N¸$¡ò¿´L¬¼tL°Ct’¼ŠìR•ˆù“‡ÂþïJÉ: ±[µ³6SS®ï*ªç¡§ûY@&œ³ÄL?É•ÏrZúpÏtÒ±ºøèí{¦¬`jQyŠPT©dä]˜î'°˜xÀ$ñRöûÔûR3aV†½ÌßÔÞ¼B|“žµdRÓQÛ|>±q[ñ|o=´<ׯŠs z º/íÆiþb(a…V Ù WÊ/J"ÑÔÀÔhâ,®ƒ$·,Ø|ø2×wÏ–‹T¨¢Ë½r{2^²TÏØá­0!CìM± ·ƒ®ñd …‹Õ•sá«öV&™^¤rO ƒâ&v„¤jcdø¤Où8°7R®Ï–"Ýä£F]bZèÎ---6zë˜ÃŠï¾ Ê*b…òýS\X°Å5M)ʉ¦Õ9ÄóÔ0âx—Ø[„¦ uOоõ^Ø¡ Ùð/"‚¥×?²~`B Ž’¸ÇÙÇÏxâ°Ÿ”ÓêQümtgDß&‘ùgj/ˆ(mì†;ñrè%®»ú¨–‘Éh½Xž½Ê;…»‹—ÿ¯Ëïÿ]ÖTqúãgI6‹œÍm…«/׫‚¦)'|6¡¼áÞœº*äCˆTô%~š°ƒÃ´çšÙû¸tÈ}ÐNædMbªÍKÛq­ñ¸Z(LðÖ]{ïÒæ(íš¿– Ü xâVk1þ«Þ5[àÐ¥RÙ'¬©ÇdùZàkͧް€òXª×é™%Ø…Ó¡u¤áŸÍZ¿µÛ ³–p ¿ôL׸.dFñ"¦UÔ+hª` ñ×ÚŠ9µ [rCÇiwúWBÓ)‚—ȨC8 ð‹’U±O~"àæÑnïE”,ê4‘P©¦ƒ°ãÄÙBô¡'}mB¿inJY‰ 9š¦•÷72úoI.ÑtÒ¢vÕÈn¦¥ÏÔh•cH>ÏêW]³Œ¹F ê Y*ÙJ8sšœzOV¡U Iq¤—3B¾ä&¥ù³2’•ú·=7D髌ÿÃv« ®ä²ßÞ³ÀÀfMî¶f@ù¤¯‹¹©pŠ·âæ“VòhÁk?À®Wpo­•ì A€Ò“]PÙŠ©Üš"%¸w«V*H¶e¸°­Ôį™àXˆž¯àa©òÖ{y’PÊ)(?F‡•=¬HZ½¶Vn‰P$ `gê¨ýa‰3³âˆÙCÂ8Ö· KúTÑl4êVÖ%\B—Í"Ìúðκh H„˜ŸýÝŠÇ/õµDlw‘ÿ‰ÒžÍì7®ÿ:nWIMáïYh/>}(Z2û ^õP&yß߆4.[v­¿k"Ââ(¯oëneÎfe ¸YBж&óÛ~ôö؃ŽJYIE¶Ójv~®VÏŸÑ a=ÕÖÈ‘¼dl ÉÿüNð­µ¢tޏ²¸éz¯†Óéè²-M xè2Ñ‹Ñ!L]$>†©®ZðÚ„ÆÐ롦’ë ß°¹VHðpä cƉwš´]³í_€ÔÉÛ;Üj/ÀðJÊîaX…LPÊUõ¤ç—fïÏ:5Èîâ³aô®‘”Õ™zˆÜºƒ¶lP €tjh//¬|½ÄÕ4~”~þ7Òq‡#Ý«(Ìë€Tú†?Þ€R,ÝÛƒÞˆª\>ˆ;\Ìn§= @/<¯Ç!EjöË€°Ê©«9Ù)tÐzÊ¥ ã$z)pH¹LŠ&Uâ›°ÚÂØål?wžhL²ó/¸ÿI›'_(Xœ²Ç‰Êeã®Ûpùíè®ÅÌ^«0 þQ&‹f˜Y|Rp¢î£àQ—8ÀSDĶÔeƒK¼Ð2ð£t8Áw;šÒ´+-2v`ã‚"蛥½ÝyÝ Vëd²7C£Ì잨—À‘C¿ë—e_›ÿ+ž|ØlÊtíuEHœ¼(=ÊÆÖ ÐTÇõ2`;Û ê&×Ãæ28;Ä ˆnªt¢;µÏÚ’zi:.Ïìgˆƒ¦ÃkodˆÌ0(ØÑô‘Xn% Ed‡~€r“€4zY–ÖC!k€¿äe¹qsƒÝ,éêæ¿¿k> µ%e®ÓÉ8Õ1²b Aà"??ñ^+…·y¢ié3,Ásßô0€húrä~9@Psî!^vÝžô–ÉöHÀ˜Þÿv4$ÖÍ~ÇX8"×ê0ù›¥ä#‹P<ön®ÎBW8©rÁªÓ}Ù¥{ùv›p{gö8Žó-’ª!^x .¥Å@Ɔ@Ñ» œ¼íŸÃfp¤ÌMH¿Ö[hýÒðïî ^^†*À ×1LõÏún5wìûB‹í„8P¤ù)0}9Ìû傇óbÕ„õIP­›Ðåf²Âwã Ê84Œçâ§Ùol|ô±a£&h†!’•ÚáÑ»mnch òtÉE‚¶Þ?šð“¤ÙšExÈšËÓ¶—p6±òÆ\Â7ÛCiòUèoÀЦä‚:“-@PþºÆ¼—ø,¥fvTAêܻâx Ôß"¹—ð"íÙðÀÎëjÍà˜ïÈÜ:éçw-ʼŸylñ €; XA>¨u¥¹_ÿ‚Ðqpàfàá»ëuêÝÌ{Óï y57o¤Š_þUnG4ô!е²A »:ôÉ‚ïÕ¿˜sà ʵ®¿È®òÔƒrQ»C5+°,þ§úÅÿyëdBV[MãÇŽâ4<šëÖuy9—·Õ„¦hPÊ\èž3p^ˆ|£@þu¦‹ÄÞúƒñ[Ÿ·‹†~ýŒ…Þwf X}cä}Á" Qáâ2Y£›ìToòo_>šœ¢=Šgd endstream endobj 1621 0 obj << /Type /FontDescriptor /FontName /JLVIDA+NimbusMonL-Regu /Flags 4 /FontBBox [-12 -237 650 811] /Ascent 625 /CapHeight 557 /Descent -147 /ItalicAngle 0 /StemV 41 /XHeight 426 /CharSet (/A/B/C/D/E/F/G/H/I/K/L/M/N/O/P/R/S/T/U/V/W/X/Y/Z/a/asterisk/at/b/backslash/bar/braceleft/braceright/bracketleft/bracketright/c/colon/comma/d/e/eight/equal/exclam/f/five/four/g/greater/h/hyphen/i/j/k/l/less/m/n/nine/numbersign/o/one/p/parenleft/parenright/percent/period/plus/q/quotedbl/quoteleft/quoteright/r/s/semicolon/seven/six/slash/t/three/two/u/underscore/v/w/x/y/z/zero) /FontFile 1620 0 R >> endobj 1622 0 obj << /Length1 1630 /Length2 15996 /Length3 0 /Length 16851 /Filter /FlateDecode >> stream xÚ¬¸c˜dÝ–-œ¶íŒ´mÛ¶mÛ¶mg¥YiÛv%+mÛ¨üê=§ûö}ú»¿îíÏ^s¬9&ÆÜkï2"E:!Sc3q{W:&zFn€¼•±›‹œƒ½,²™…›‚±­à/ÀCF&âlfäjå`/jäjÆ Ð03ˆš™˜™L\\\0dG/g+ KW¥š² íYþÙ0öúO䯧‹•…=€üï…»™­ƒ£™½ë_ŠÿkG33€«¥ÀÜÊÖ ¢ ¨%%/ ”WH˜Ù›9ÙÝþ–bµ21³w1£˜;8lÿ½˜8Ø›ZýSš ý_.!€ÀÅÑÌÄꯛ™§‰™ã?-ÀÑÌÙÎÊÅåï5ÀÊ`áldïú·®+{[7Óøk7wøWBŽÎwØýÅþ’):¸¸º˜8[9ºþFUÿwž®–F®ÿÄv±ú Ìÿî4u0qû§¤aiþ¢®FVö.W3O×b›L­\m¼þÆþKæèlõ¯4Ü\¬ì-þ+Z€³™…‘³©­™‹Ë_š¿Üÿtç¿êüoÕ9:ÚzýËÛá_»þWV®.f¶æô0LÌcš¸þmaeÃðϰHÙ›;˜ÿm7usüOÌÝÌù_ ¢ügf¨þ&adê`oë053‡awpý@ù§2ýÿœÈÿÿü?"ïÿ›¸ÿ]£ÿí&þ½Ÿÿ;µ¸›­­¼‘Ýßø÷!ø{Ê8dÿœ3¶F΀Î'7³ÿŸ›‘•­×ÿÁñ¿oÔ0ûw²ÿÁ÷ßa)W£¿M²·ø+ 3=ã¿ÍV.âVžf¦ŠV®&–s#Û¿=û—]ÍÞÔÌÙÖÊÞ쯶ÿjë_'FÆÿ†©ZZ™ØØÿ#Û¿!3{Óÿžþ_¹þ•<ƒ²²º¤Š8Íÿé„ý×NÅ¿“àªêåhø0r¦ÿkñ°°ƒ'À‡Ž @ÇÌÂàà`p21ùý"þ‹†é¿ÖrF®ÎVžFzFF&Àßïÿüü×Jï¿ÑˆÙ›8˜þ39*®Fö¦‡íþMÜœÿjü¯ûÿoÑÿ¹þר›™yš™À¬¯8˜ð„Zgæd¹ÖcŒN‹ê ö3ކ9–7©–þtè ÈŒÜåª6ü¬ £ožåþÓáµ|îøu(M}4ÞaKÑ—nvýÏ„j y‹¼‹ƒæ(˜A¿>ëB#Öçæ—ì˜6;£úÑÞ´’²~Ù'þl‹3ÔÍ U ‰{Q é³#‚¿IFcz7R3J}ñùyÊéË3ÅðÄØèHßøÀ!.M~4;®€¶ZµOÍHÃaü@r»Šùµø[Z(ÆLP毪B%;5Õ{vÿî|ß\в´å¨2ý§ËR»‰žãì£6ÄæZe~6’x`ÅóÜ'«„H[gÝsbfŸ‰ul{‹’ vUæÉÒíÊšæò@ù 9ï'-Õ¥þvñX4 ã å¤zlªž@AE ‚5 K’®W#]u¿UîÚÁjèób ÂË<Î ÈlÒ z¦:4Hì¼°O@þÖ“³û £Â,ô°½‚J+ˆ™-µør|毦|A[¼À_GžÔ0àuò{ÖêÄÁCQ³Æíê"4øÃ4êû<áóŽï[ô«.„xÓdGÞËÌwž5¿±#2טS6Û½§%ûü‰A·\x¤à Íup(¥K3óaSä|>¤W_‰>“³•ê‹-Ÿß2šÿgD \µ»¾”ve1¶›å㜽… 1ÇqÁoZ¡Ð¦¥ÎìkùË]¶.‡w•Øyµz·ØqÔÝ+I±ñt| s>u$kÿ1£)ÕvǶ‰þ[4sƒ¸CY¸òøLöʆ>ÅqAB.ˆþ@F›0‹étˆßãôò¨ycÐv¿ &ßÈ PêŸÛ”:Ô¸—+÷ô?åGé_‚¶¢™ ¡a|½( ±œPŠrú[H푽Díx-‰°TQ«â1=½µù8y°ÍZ 5A­‹Gb²Ùán¾÷¼<ê5ú®x©tcJna-!hº÷š?Üm‡jEá[F½]«Ž®¡ŒÆƒÞ;ÀÄo޶U[BÒ Ymm´‡#«6ǵ…YÖRX°¥ìÙ¯f!ÑŸs m­bŒxüÞ´è´n28_$IíçvþU5:L ¥?W8ØÓGí±·|>à ™ÐÅγé}Ùd\þëÆè\¶š¤ ä#l¡ê ³=$ì£2¨ƒ»VS¤ÿ»='Š™Hœ2÷a 1^Ù¡Ç-Ãׇ u@f¨/RÈcbRn’¡mì¢7èçúgq(ìø;?Èz‹"ÝJLšÂê!mƒG‚—0†’\J¥ðÚàÚnšõRßårËìT÷¢œŸ:Dæ0ƒ‰ èáö‡#2Mt¶²f*[@={ '.Ñ|× jn°ln[|N©³‡?GIÇœ{7¬ = =Ná/oõr‚+è3š—U?ç}qT¸òÖe¥36·^Û@éM¦!¦ÉˆO²z;<iiDX¿Ò¯zÎ%|RD®>¯²³{òKuk4û¤d7z×WZK§ñó´œyjcRا½(­0.bÇ)CGÝ£Òtÿ¯øÆËðRø›8¨â,î©Ó5¡µõ;ªXµQÑ7¶CÌu›^ÁÌ.‰(Ñà\q*ž2ö•ÖL‘,§váè?:XÚ/7G3³uœýÝù‚P)MÜn©¢ÅU[ã#±O{D¯Ómˆøk°F _+›±‡¥ö|rÌp°¹uຘÔ:"ÄîÕ&ý+ß`Äzn' åŸ +¨\ñTý+qÔ t`¸Á¬ßÖM­#ñZ» 3ú–I]zU¦æ+ñø­q”õ|«¤ 8lW+VˆvMÚ©±e†ß!üâ,µÍÙdmðTè®eÐe Tºß¤P· šÖ¥5Rn”£¿©É©¬¶Äœ–ƒ ›ñ$[¶6Ö ¸…à!‡ýbšÒØáÍÛ± áÁÌSVþø550³ajÛûŒ1ÒXG29´ÕQjÀ’pw× ÿà© ™©:„Œë}Ù¾ÿ5ÇFØaI§áϸ¼ù½nФõº¥wåï[KòÐ (Þ’­Ý ÀÃ6÷;Ê$=bLSçÎÜý;aNs.Ü]|è$ER#:N‡ªJ"Ú6|¡O˜ˆ´R=,~#¡ü§‡éY»™Ï|˜ ã‰"wLõaaB ô¤g(Ñžf ¦%q@h Ó®KœçíFw;)&aì|êPœÆˆN˯4¼>¾ø'”-é¬9·‘I{³,âê,Îecʤ„icx¥ —9“kŠnS’w 婱òE–-çÇ2^)y'oyB {R´X]•…:•dЙ®.mµ:–M 4S¾t*Žç¬U$a›˜³;î.Ä\[på«’ÃV.î‰úsçÔîÈ:Rã+6´oc¦‡ÑþqÅëpŒc—?KðªãO2=jšx”š¸ä$í;ø¹-zöSÔO ‡1w.ׄ;îÝcõb\KX&\©ŽM%ºßø¾ñ ò¼cxF[Bhœ0/k2Gj ”Ó^l ãt%k¢¢¼ >ÂØ¨äMkd·ÒYdÒa…ãŽO¤—µ˜”— úÁAÅŽâÜk ~³ÍÛuôu—w”eà“©= p#MµÐ<8åÁq3Hîõä³x>m¬ ¤ á/U, ½úÐYñõGÇù1-›ÁÉžLq¦ >±&*^…NÖW²ëš ßÞÚK¾èâ3:ÞV;RáXnç‘ôq϶Áò‘ê¨Í¼¬À ±^ž»yÜHf¯Aã“Þ¨©Ý*žÓ­ÊÏ7ØÁh%A;†tE£ÿ˜ÇcÁðÀϼ›èz÷£ØWËŸu‘£4|óޗİ·c}þL z:Ó‚6 è€\0õKTs&5Wø"[ïF7’*ïoé…-àŠ¨ÇåÛöãæ&ݪJ¥ƒè½§àˆÂ¾ypäTIÌ&ÔíB«€ÕäÔ»O&‰2ž1õ»W¬Ç¥z­€?S"d“Éå7lI>ùtZO›¹,g"æ•ã\éTK"† üÙø²Eø!…cÞÓãåiðMÈ›³Ú7D<²¾%¨›±lÎU¥}WÙ¬ “÷êëéðÍ»l?Eœ äøF|à¶ùã m´Á/pàO3ôüÁêX@{âæ»ß{|ú¹¬˜Cëc°7z†d²¸£§ê](å ŽeÌêpê•®±õ¼³°Ýf3°òò¸Ö{¬ñ*_ ½²¼øÛYIúE¬h^6Þ¬òÒy®‰ÛiB5zW{’èlœçÚ$”f•°aú:ɶµ$öƒÞö‹Y ¨~¼£ý„GÔfçADÔC¡.Íê(e ¯×‚ ”^èg Û¨×êWEɆPhά` YµÁ4[òÌ®ÉnR²1ùd<)9 x ›H¹ïouqXÔm'n“Ì/r;ìKÌR~ *ý%(wO˶ùÜè6eN= ù3¬'ló .‘7±Ì¥,~³ubÐÍ£`€uå=žâ ®P ŒãkçÀGÀ„%ü§úÔòEàç¯Ö0bþ«>÷BBÖWáGê*õ#‡`‹h‘òŸš71ùu‰Ä(#,Çx‡WTÛtJ«%cà ÐXbZ¼™SÌ‹¦ùFšrSþ¸d 윤¿†=&¦9Í(è@°»‹§v«M²R¡XrÍz÷]Œ;”®$]#`gÏØo ž ×À?z¿ÈÞL4¿Ñ©7ÁOdRòi¸û¼ÿH=%Ë«69˜B ² ™©àÁuÿàO 8K¹WTÒE8’ƒÅ_Z ´"K¤Ú‚Ä)xãwlQkn$P©ã€¦R2Þ‚ãðq”™ÀAg$3ªÌjbƒ8 <‘ÌI]lÎ-!sƒ|ä–I'j2ê ÜÃöËb£‘ðmÐ¥­TåÉxmž9J߯­â@©w“¿3S"BzîRñA^Î@,>Ÿ‰/ó¦Œþ˜œ/ˆa,1~ÿ™<År´¾ÒÕ~ÄÎJ€žµ£icйðÔÛøÃsÛ!Ë3øZ(.¬0Š<Ë’åqÏ|ÝkH€½ô —Êÿëdítyö—$A.]Mcq¶¶‰7å*IìLË_~}(ÀO«_rUãøÁ5;vâMÓú•‘E©²âCÜ­,¿a^Õ§­è^ÚªŠr>¹‹ÀНä®d •Çò^DUTϸO@Î"Ô'D—‰à¹ÃT»–;,F¡ò’à‹èãÕS ¸¾{¤Ÿ†ò+TôR*à'£{¤‹¹å» Zkí¥¾ð±™QÍïGlö¡}HƒN‰åQ9®)¥ëÕå¡=Ó:iªUP‡š3â ƒ•§d«!G ô£—<µ¤Ïâ˜'ÜßDƒtSs[ôÏ^'ÃH¼G#¤ Љ6sÄfÝÖ © gfG,¹ÌÕab‡0…Å?Áwe&Tü] G,í°ÎTŸN7„ç„ ²â7kÓ;c¼C ÙÁžk>/­éšØá]¡, ‡HP°qëu>uq(™É$­;!Ôn2H y6eÛÙ#ö³pÔ!ðᩬh> óQ{ iñ ÷ßýi¸³ÏoÊD0Ýc+¹{Õ7ˆÔ~ŠZÝMÝà€èš±¶˜Q868S§Òå;§uú±P Âó˜ÂçÓSJ"Qü£T|o¥|£UG“â`ýMÇäå¡k¦´¹ÇÜ•…Ešì¹Ý.*çqì÷Ò}Ô0~XĪb¸˜¹vƒ©~„)~@‹ ˆºukaóPªØp†7}ŽÖ˜gWGOP·ü2TÒûJñIÓŸ‚?ç³íÈ›± S©×Ù y± <ŽË·د[#Ç>íbûûüõâ7 FÆ®Èü¿,iuBŸñt£Â“y~Gl´J*–/ñ@'ç5ÇÒ>^¨ÆqÈGí;Êñ[‘W3r2X+41ZØÞ ký<Óìsz¯#º(‚¨Ó-¿«?© ©ƒø¼Íç8[ã$ ”„40¤1‰é©Á»'y«jǤ €Kâ™ÉËàÀÚ»Ñ]ûøU*—‘ì7>€þ+±Q­×Úí—Ax\®ä¡Ee.*öVâ..[ÛøHƒ ï”äÓ0¼Æ1ùµg\oÌæº´Øt?-±ñÝ‘V oŒ|ýël$$P¢WÕ†åGþ5)Ü&ïê‚ÎF¯4«3†’TáUDøò•ƒâ¤v9"ùÀ Á#¾ «æE—‘´zD¦aÉÀM$C¯i…[<áÏl ‰²Ih.¢clü‚ÏZ kˆK~±ÖÎ볚ö2 ¥›ÒE‘Æw4Þïˆ\i ÄŠòæ×Ew€ê•¿u+ô÷M–‹“9"ϸç§Ú?„H3¼W…ª)ÜðIÃH{¼<+²°äC£ÈÕíçS‘_CûXñ®ùÉùãq{æê£e0åÑoïÅ1ÁÕ?º6#… ­ªŒïóY¥.K¡R¶)Ò4öBK)!ryA„Ü@‘½™q4$2AÖLÊgÎ77K6hù!œæ=¼ˆD- |WXÀmÆüÒ*1ž„“4tµ‚—l¡Uðq¾'1W….˜o‡uHV8Eóðù ý+fŽðhW)»“Ú+ ~‡–{÷×öZ°k²Œ¼iÒóü¶ƒ¶óv»\:ìÂS…‡ÂDrÔÖ®ÏÃ~´JsY–WùyVÇž–+QOb4ä–ÀŠ_mS¥S…·É%ŽÏú)qŸßÇi9Ì¢–&nn¤¨‹Î2̌ȣ»ZN «ò åFÈN¿¯[µš‰.…1!8¶ b›¦3ŸLÍl‚/éd1%ͳv•‡jScgµâUUÌöAá‡5 ½$¬ú Z`ùrÞÞ»Ö C!K®¶wxÝk^ÝøB<Åx°?Ÿ$h2^à¤K7xè•Q޲ÀH±`ö›²‚¦›3WÀƤf§ª¡åM†-Å›®3£ø…ó®` Žÿ>ûûᨩ¥µ‡5ìÂÛS“¡ƒƒÕÖäüw‰Yh¦âÑívÿ­¤ ¢/ƒ.p9œ±»:üö¥Þ$™,F¢‚w;f³Gr°{ŽÜM+ìJŸÌW,Ǿ®tØEIVThÑoŠËäø3ç,“ —U^»Õ2w98£¤`଩bc­'«HZ`Ôm±å󕿢2¡ú:ЋrL½àsWskÆC YΜ«%JøHÊ îšÚ+yÇ^õ-(ýËŽ|têg·ï‰1êž~¥æÀ!5e‡&æ„‹ê}¸[”BEüŒÒãT­ìM¤s‘ɧ\„d<ÕÙoWÓ¼Û â *'À3×(çáWú!„@#*+ ^ÍA»ûùlQ¯8b³1-X4­{¯qëz> õý•ZŠ]Jym[ ,Ý»\FTŒæZû…ÿ  ‡$¤=€D~ú#O5Ò×ëAS6KÐp)ˆ`¼€[Ï`x«G£¬Ê I£µ›2¥¥3rô…_Dð}߃)k*_…²gP_-–½3¢"EVÿŽéíÒ†£è”(”Žü “û1MXcÔ‚—ŸÁeãVr''Ç[‚ß|ñ»-J Ék¥"­DXÈ%AŽQ÷ÔÀ##wÑT$ù3{/.ÿXÏÌe/c( ¡÷y2fH[ÝÜ;Ëm rsEÆÍ%Í_þ5@I×Ià9²ä‰NgÂ*IEÆ.ÞlçÇšÄâ°°iXulÍþ龜KÚ² !ØòD¯ýFÁá„«Qðd¢ÔYËŒœ–à‰¦ [ÙQHD«‡ušòÝV 9åΟ>ðÝëAxsXÄÚÐ×êÌÙkM¶Æ²®.XFÌtÖÖj¼’ê%ª$`AÇ$ çIëf}ÞÊgíh°æ»fWå[fŠZã¸ùé`ônR ßúÙ{RÀüòd÷©±–+u&Fêw—õ(ÕÊ‚N|ÓŽÇÙ:mܧôTo·SÅÀ\wd2¡f+z€w¾ñÁ5Æ'—)y¸M§ dd¡ZnÙ¯Gµ?ÿT{çÔy‚Þ{i–®PrU%c²^ó3xyyý¸__û‡“^3Š‚ ¯»MIuÁ_b'¥”µàI[N­pkyÆC…Å;¯$é›ÉÞi¡1¥l¿_hÝ/“)|áK»ßΕ€Ò?_¦Xhp¹ÎæÄ)âš÷Ï8KÕ¦ç´BÕý}ÔJs}F‚H}³zUð“9åDß¶ˆƒ'Åpöψr‹7Ïà)âîsWk7¤¶Ç8¨øóRbêx Ò•9i¬r[[ùOà*ÊÕ™ü?«Ù¯2tÉRé¾÷y1½Ô `л…DíPÏ xHbW¤rÉŸm4¡BÙX2ÐëÃ÷;'ŸÀ§¹ ÐĤšÇ!uœ0f~´Á®j0R/ ¥§m6ѼÄÇ—ÒÄuÒcä&;¢5A i-õù=¤8Ôо­8Þ}8„ÀrÎÞ]²éMEzj6rðá™2þ<…¡!«&[2‹^i‹:s´Á0Öz =ÌDëž2îQïÕN¼-3ByC‹êOE ãDÿŒKšgY€ê˜~JÞ&C•¼0²;ØãY/Ê3å¾Å‚?ô`ðï#S<Ú¨\ÙÌ|Yðí.ºy«²¹[ßäßåF™ùò™.ÅúJ0áË×/pN,Q?œq–[b„+q…·h…W¢:žüÞ7üŒ;e™åÙù4 Uòù+/t¸eÚLi ¶äîêvô‘¦ W)¢ôŒ¡‰,(°r¹Õ}ªùéÇ“®“€zI=žvØ‘ƒITbê7îT…°7®Õbè«Xu²µÓNÅìïƒuáíò eš˜5œd÷s"hˆMÃßfJÓª¡µ$ˆÝOqœýÕ:§[)jIñòwNß½æ?~ºG˜SÿûEÏ(.çz`¹§˜ò² ÿ³xöR÷¦^‹à9*ŠTªò¥³vàpm^=šyY7pY Õ&ñ“wV–önM{{Þ~WöÏçû GEªÃæo^‚V*šñà¬doàÐûÜ1:B•ͶÖÀ;çhûgÍxVK4˜†c3£í/üÓ >FI’§\Äs‰÷æk`þÀ_»Ék (ˆ–ˆØ/ew¢ò൲®yœ®PÞè1y¶`Ä™»*°sÌ6®—@³<O´I`£`ØÊ?æžO?VDÊÛix×f—ËâjJHêþ‚öÕ/@s-è 7"%0±JFTp_¯±øŸ’V%nŸî<64º^ïÂ(<Í“n¿ïÛã×·ŒÊ–Æå»5ÅæÚ›ì¸Ñrv\Æ&¤±;Ò.LÂAŠ"‡è}Š€•PœpÜ¥¬JÔ7V7¼ºõ¦ÿúÕ|NfÙâˆ×”³VAˆ¾ßc%hÙ‰¡¹ìÁàΕ¨Ê³PÙû˜Ç/þ³ÔIÚØvá§tÇ3¢öÄk1ላՕ±ÑÆØ¿Y§ßh0QúA¼.Pa\`æ×«Ry‹ VáQZ KÕÀËèÏVðV«*¾j‹KŸù{Ú)o%8J¿ü‰¶TäððÏ5¡ËT8BeªÜªsí™ãP_»Û¼,ÑZâ|bä¥@¸Òôö‚ʦQ~"qËJò@ójÞ‘KûKZsˆÂ—½< mâß.A-Ÿ–G~— ¶d‚œˆb÷\Â@/+½—sÑbiЩ£6lü¹2‚?ÝÀŸê©\÷eRN!ñ‹°$ ûhÍ1®—ïj“.Æy}¯ô¸¸ƒ¬‚“…ùËõ}Õ]¨4jØöûÝV DF«Ú”×Òg«D@ÇTgÚ%/UgL7în~Á¼d<ÐM˜©'h놔 @½™;d¤\Xt]K6P]™Ì3ž>eޝ7tx‘コ+èfªøq›ÓYÍæ~VtKœHù0Àg^£O+¢-Oªú…—Áw«‹Y6Ž1yâ˜;9±õ‰%b“Xi½g¬ò¥jÍjŒaôNƒ3K-t<»LœÎŒ(ô´?æƒ2]ýÓ|Dž¨cä×YªA©èN¶ˆºuÿ“ÉR.ü„#ßuykï°—Ü—ÑF2×õõ›ïº)9¨¸¤Ùò®c&"„)þ‚칈qÓºÞ1¾„TT:NÇjÀÁ Q"²µihÐÃNn›Üxê&Ë«ûfiYpª¯ ²Ú ²V3ñuvÅ©µ0,!ó]-Á—B¦äcöMÐöÀ¥òt£ñSAÈfNÅOuN9/¶vÉy±1Ä*3hË1Žæ‚‹¦‰Ka ) ò?þp.$€Ä`HܱË#%ðCo_j ßÐ#Œ Ö•Ô—Çšíû«tÖp;õóž¸'äÕua‹YEè–Hî¤ywdÖ šÙ¥¬à\¥7'Çñ|±™ê¨Ä’Ú°°TËŠT­Uëp˜Å΄Ôeú»›Ïuî‰ô—ÌQ~¶óQmY+²$¹Õꦙ<ä©k›¢4Òwå™;$û*,í&C&ò|v/¾@’½ÿ®ìKý}gB ñð@£ªze7F5­è¨,‡%•ì{’<ó§ªù¬\Û[¯¶ÝhS£^ãŠV+G˜{—ôžI€%³9™E¶”áÞÞû……}ßc”!.™[°z’ŠÝ9öý͹›Êññ`t``4¤´‘ ·¤ý@¯Ë Ö’;‹WobrÁ¾·¾Ìÿ2·2ÏZnǼ*» =zÑ—›JkºY©¤gŸ„œ\èA=1ý–da’íÚøîy¾û¼Êzå¸cÕÈËÇZØý_Cîä bA(ÎñÓˆÔ.ø§ò¬Ñž>5—)‘Þò‘ÓÄ Sð= ø@v”1èQf^÷ƒƒyæA…× Îû)G¤ØÍþ˜ïŒ»VYu7ÃÊ—2Zב " ÝtLØKZ„iVm$–ÑœmØ×ä—FV„Ú0¶˜Ÿ•ä>Hr'ë(#}“G§²:¤?cƘ=ÀÓ±…ðë¢A"sÞƒEåõ÷\Øš—î4óÁ+^‰¯K™ ÙŸI(»^ùFÓtήâAš#/ok×ùýenõ&Q—æ8„½.{ÐNé@¢…|܉tšÂ^Êalù츪a·å£ÁâA¬3íodŒˆbä\^+=<½FÂ’çŸûÖ¹)°Yv»ódR” ÙRõÉ@˜p€• ®ÛxÞ¼ÚÌAkóñwƒöp:î¦æÓÂ$TÐh=‘œçêèæ‚@7d!W&¢Õ«wýò^JW ç K¯¤í 7Ý4(š³­ÞqèâåCq'ÚöMìp\àïÖ¤Yî˜=ast*ÓqŒÛÎ*qÔ”¡PHa3nr¯2/ßkqç°·½œ–¡‹w³@“Sbë¥÷»¡UÌH¨"³yFö­bå;f£êý"O6¢â[!2ô¼EGÙ¡0^b¨¤¬ÇìvVé8¡®"“2Û6EoÀ¤/Ô.ÍvÉH§PkÐîæ²ìL#û¢jbÛ65Úr©P6$X<·¦Ê=óýãyÑï÷x›qA…£ç‹ßçºrÑÍJŸxtºö¶ž5W™"Àc²ØTa!Ä rB4^§½:Ùé©*ê’ŠÂbÌÝ]»DÃ7N£ˆ/“/`M„ª§mªAĪÍܨZáBÛñ8X*\N§jÛÎÞû 8×_èbÙ›ïáN_…ŸRúŠbf…kñÅÖF].µgŸ•tè²S5¨l%— 1ÂâN³q[$B¡_×4:‘'س»ÇJý ÉJP£ð¼=@»Çê˜Ø÷¥;°müPtÚcs î®çÅFãüB§Ôq`œ[êÌ6íÎj¥çR²…ßTÇjŽ·$’ŠR&–Fв­]! ™Ë}P¼Ì]Ô „ Jž¸Ò¾qtÇ!{7-ºwþ!Çç;’ìòÞc™©‹Y„rßAɯOãhë{VÌÐ2Ê^‘tYm‹Œ€äÓN) E;š­UƒYç½I+¯äçUe» ¨úš‡=0”à3¥vªÎø¡j”Th¥ÅA€¹OÈ'‘C¯WË‚Ú$µ]lÖ +b#ô|ß[íLç̤û:ØõÈ$dÉÌ\ÔﯜáWÒï`ñœ¢‹l{{EžÏã-lE_tm|xeI¤+jb\°–\¬†J|»ìOn&¿þ¡d—ú{Vƒ‹+§‚ŒžqûàF%}ùîÖ¦"`óàÈ„µãÅš_wü CCži»’9LGo­`&é»°õÒø’¿Ïæ`ÃyKVnN2Ù>Š,;´AÀAïb­D:ü kÝ—K÷ ‚ÃOÙ{¸þ_@±ó½ŸËd—ÞÄÛÚÀ7ãfiØS™’¨GQmG\;«ìÌŽÕÉ ççÖ]Œ Ã4µu6O’rg2l¯.â±/P°o óÞ*ìþ ,;í(u” »KÒf úsÜ$¸¥êV‹¿X«Ô+ ˜†Ks7)Þw˜}o¨ºqÑJ–`Æ4W‚ÕØSN[ò¿¨úIæw|ÑQ3nÓõ࡯Ñî€ ‘|C¶Ï°ídy=•#Iã 'v".Ìì—8ÇG ª¡–‰¹çHŠU§Z&È–RÆIžV¼.È4ô·ph¬Q]9^ BÍmAlÔ—ûA1¤Â·H C…±äµôÍðiuƒÈ#Y[›T‡ijÃ|Ì2µús]ù®§%\ù•vñÔ/£qg{“q®ˆã×!—-L@ÉËWþþi$ž@ÿ gªýî$*ö¹ˆý1Ú30<)ËOíO&mîèÐó,ä·)Uâc}ºãmA]¤Òp§Ú>‰oíğ犟tå†läè;¦QÊâkG@$³8ûF•Ãäs£Ó‡$q@qY¨%(Í9ˆb¥p_eBƒU±ú5ÓVʹȱÇËÕ²u —æ #”%¥lC ÜŽY²Âh.é§"¶q¼.mðµ&.ÆÔ§KYnN_Ü%V£ÿ ìÇ9=½í»  ³I7q؇®å_"MUUÌcA­\¼B $}Ò²>Ô]Éžßýj®/@a„±)©£¤°Ä#m }ä"‘EÍÆy;KVÀÀY}@ãÞ`í™ce- ÷ºÂ²öúøÙ—Kè{ö³LBú¤Yû¨Àud‹ÞÕo‡NJö­>Ly/$wŠMœ”J0‡Ï¹N‹¯÷ÆFþˆT"C([–Yı#ÏÇÄôhNÈæ ye”U{•œw)ää…®”T.~¹¶?'j)l+³Bz9`lÈT0rÐr£ˆ (/׋tæUëeým¶ÙƒèršJ(˜Iœ­Û ¿„!)8èÁ¡ßµÊv YñGx¹]Þ“niã¥È#½¹ŠžÉïšo¸=Ö-6F“̳¥kô[ÊzO{µf_p`éûžgžc&­ºXÏ M/;ʃÎDM.¦mTª–]‡&o²¹:Ü[ž\TÀyHo?¤|¤ ìzY>T¼‹Ç ¼*TÏÔ{ѪÆ™¤{•ü¾$&s J‚üÏeb Ó=‡/sH<½ù}쥘ƒ‰¤îÏI–B‚°·Ô•¦ºò÷Îaö>¿Žî~«Lÿ ƒ`z-c2ÀXŽç†F{#yÑmÑ‘ÌB£0.¾,ôk æbhÀå~EA "+Û½6WŸ2ŒTÉÎmžvbîïøFêÛ9¹ç®yRÀJJt³p&éüæFŠ¥ò +~äoÆs3Ž;Ë9.p¶¼"Ý<Úÿün[ÒŽ ïŒº›üÜ7zý¥œ—rÏ~ËkI;ï5gy*J·;€Ž£ð»Â™lÁmÌ3šæÍ*=Nº88+°êù1^o÷!màâ ƒ5VII-ìG¢—)Ó«„fS¸öÈAö,¬`% 5ÉœvT"¥¥»º‹¬è‚ê2­£cR^+nIþô¤š{Ž·VŠ—Î\äA2’]Sðmüy Î97»Æßw¾Tt®$pÊ´ÁvÐ4—Å—D\ä’·=‘™ŸRcjÚ¼3©¼þ—?|Q±³X,‘ïÙ)t_«ÉY_»Öqë9v'Ù>ßréI7Haò¾ëU”·ÄyÒJ|6È“$Ïã'ãêCslq½×­Å\v]¡úBÀF¡HCÈê-f^7bÆæƒ{‹H´®ß()Ÿs*葚·06(!«z[ÊBs®w—J5EœÿY$M|Ó"bá× ¢DàLÚQÒ/[ˆ»­›‡U(XXßp°ûÓãÞ#5„²¤Ô†“°™©€K}@Ïÿ:š¤wI§2Z!W™p‹A†Cí§XYêûžtäÉç­ó躾Ÿ”Œš¡œÎnÂßB½=U—¯Ÿ»È¯T3Í×TV²}ª­=OA¾Ï_w^ÉEø“ä)csô¨ÍH|ÁæÕ]¼]!Y­bþä¼™ô‡…¼9ß-_‚Í]ø¥)õÙÚ¦ûÐ÷ !÷Ï𽨖.–Âït%9KJxï(ÑÞp´} ø4D)"1¼$ƒåp3îKfÑlþ}[ö'"èoåòsV¢Ñ:³C¢pÂäOQ r¬«vˆqGŽÒa zßéq÷RO–AõŸ…®^¡‰2ûJV¼-Ù‹±K´+Z{ë1žà«‹hE¿9¢=À£{&½ 1'Aà[ùíó•YJ8É q·ò+.}½…ÞûaÛKnl;ÁÎåîœ ‹Ž|î\Œ7¸©~è(\qˆr&l¤èç|¸äeJO‘q1Ù¯ÉV$We&Áó.\γ<ñü¾ â?zëï2@ÅxÄ?ÀÓpvìuQžö"l #ÙÙšÉ2U!À@N‰“Z‹§0TÑ9²CS·ŽW‡Kß)à _ÿ‚´§Ž½­môt:An»í_Ðë蜨®fü)ë¦?/Œ \Q¾âﻺ1èHÎVcWçêÀÙàa¡Ô“¤G™ônÛÀX¼±”Hye4l¢sƒÒËËò»Ã‰PDI!.Fä]g-bÛŒ!À%ÂA0–[ÈÜMbåáû[A{Å]ˆ3;T.±Ûeõ‹F]ÞÐÚ0aûT okÿD0¥3°ÈC%ój$Ëo§ä¸^“8ôL ø§•¾#iðÛP^¾êê@ unÄ©œg ±ãþ@ÞÀQ*eÁ‰ø vu$„ Èqû!Ÿ˜?âwŽ+úi×8ý/hÖ õlo*E 23u´çq¤p4 À]ëâìÉÏP x~â’i:*‰ {`ä} etù»L%Ìî­~ßt¢áO±<ñªWJD@“ä%ûkA×-èµV&§E̸ò«Êš´•Þ$X ;¾õº†•(ìà "—·a<=û#¢º¡¡zöƒìñ oö0¼EF±ßèßáËq`XèP³ JèL¤Rê2=¤u¬XÄK˜rå`m‰e1ìï9æÉ¾Ï.kVâxTH9”ýs W\<®ÖÍ ^¤¼?<$Â3õÌ"­µB¼ÚÑÇ0˜ºûÔ¯TräU¦þÇåvú÷©0Á5$†ÐÛ]-¬@ •†Š,?Ä»Ãè¬÷sqs>žÐêãŠt…¤Ç673ÙBÝþ “Cæd^_@NƒVÂýeÆžŒÐ€p+£—š‡Ô U Êæyv••Û_•O‚‚ 3h•¤æ¾§~ÿÖ'nôÍfÛHЋBá'$º ×,ÎK±†Þî¡=) +YêUo(G1³ ¢Ô„öS2j-.”~D„ß¹ U%dZ?ä¢Ñ¬æP~€A¾r‡¦$/ûT]P(yÇ[Ö)WN7žòc äs>oBSÝ}”)êÜzCàöZâñªkÁ T‡Rk Üα•*1äù/ù/AunÆø€òÖþ0Ãíú[WiÌy©þÀÊñYzéEæ<ª)À¾ £¦¨ëù´UÄ´8$Ã7k¨bEæpåàåOïÝã»™ìÁ·Þj@¯§?¶Æ€³OY «±Ï% ·=|Îh±ÃaXP¯\ú GWò'M/ºñGå¾DÏÜ¡ªuÀ'ùÝS RQ>áù$0>®S+#¤µg~-"ÍO#…*]!q®e ª#v‡’"X’[›W@2!çYp—Æ»8•EUÐe²®pGû|Í1h¹!—š„_£PöÝ!å•ã‡À` îÔY&a¢ã…S­?¸éã` „ÇäùÓ¹"õˆv6ºëçÀ@“Jg±bÝ ™:Êt›¢Û€6ƒ1uC ‰=• kx”ۼ΢ÚêXŸxjÁEω2­ |s)¯Ëôõ‹¾!7âiÜ™©SO“g Ñ©¾Ì?˃¸¿?ŒÕQ\¼ðp&¸¬D±µXt hníé$ÝÐ>>˜2×ÃKúÛ,ßpY(d‚aÇ®5‘“Xöâò–¬4WÔbL$z-­}€à¥ßUêï'F®K%ÌŸÁn1B–CÈEǸoP˜uqsrÝG¤;žs¤'û·}n`Aú‚ Õ~eÜ‘'ÅÝ¥¹§eÙì…týß?ä™ÊN`;‚·iƒØ“b6¼~ UjdG°³àÒõ(Óà¯H4yú¦„Èni$­ùïn\D9–¹™¡æ9§~Y‹p­ÝÎgmV;/eÙìϽÕz3Mxͧ×lŒézÞ„#TBžd ÉÙ­o—¤2Ôç«4m÷…ˆÓ¸2PgCV…3Bq¯ó5H ‰@\¬‘ö§sÉ¡ïv›Èlûó.øLw‰{sÌmØ´ÁÜEš[Ö„¿òô\c¬°²ªÖï'c&z[Äÿfj‘˜÷õ3 M4K&û3¿ºfGt(™û`5„¨u)ÑMÞFó­*"¾`›äÀ‡”µD¹÷Qœ,íxéë+µn®ýŒ¦:YÃÀ^Äò2w+gáHÛ-Ù¹§ƒü5g·Ïä5×çê)¬÷ˆÄ« îE¶^P÷::¾ÜL7œí„óQÿÅ3ï<½ƒÏâ ê­'R¢6•Ѩ˪)‚°þÐ<ƒgœ±4­DܦÉñCSÝ’°!Õ²´'Ul3ÆÒgˆƒBuWg¦´Ö¥SÀmš:‚Xø(y£6s<7åK]·…˜MH0FÄà †4 „”^¿Í´Þ—‰@·ôÕ¦¡T"Lþ"–¦°;˵ú½]Ç6•ä/vÚ²6çÓÊ °=_x¾Óð¢} wsR‘óDöê8×Ö]6¤à‰«T9°4£‚ê]U èGRiV_=ÇÒ‘¦u³sƒ«<ÌŒiÐߘØ÷û¼ôY½!öT-OªATúáÏ˦Ëü5¥lSü̼ïÌïÅX±`æA„ZRgAŸîÖG²hñTËuú³Þ2R<yÕƒgžúSŸhîÉOŠ®÷3Ýa–ÊMº=ˆÿlJŸc 2ÞBà‹¸ÊœV²¬n¡–ü䊾¤ºÎOUÊdÏùP{‚ˆ#íPö¸à<,àûm£ÄÇ1Ïß+ãzÓVËõ6â\,—••äÞf½hag ôõ+¡¨$ÂÎÿ”2»díVߣöpÜñÚí¤mÁ]#Épå@jÞ¤$#èõBèMŠ,˜CcÎÌðÒ«KŠ6Ô{Š£×= +EÔ£Ï8϶t¤xZZ‹Dï—Ÿ©R§8ÆNͼC8…ëVkЪt[²* Wº<躾«†‘i#E ê|³ÞZ'Á\¿’†‰¯rp³M¨E-þ~Îúœé¤÷í[í}`ÖÈð#Ûážó¸·QlþìN÷ w!eVy¤xtˆ‰˜Î³Yá¶i„ú~üã™›Í$Ý<BÅkiLI‰Ošê˜)­¿ú!;ËJèRÉ“¸â±{K¦±n‰™â¥j¬‚jÎc±/‰UŽäºnih/%õþ Þ§NÄÉdÏçK¯—°&7 '½ÕŸ¨ÒÓ—óÕð¾«Gµ{\pqÔµlªÔîô,ý\®û¼OFµî «¥§3¯’öñ½É8‡«ï5ý˜e“Ò³®aØcŒj˜¹õ‰ßÝæ+ø@8-T§¶^’_™ÕU´R â§á53 4ðÞCæ×4¡+$’1ÚEöPé¡e (++þ$ƒÌZБp@¾³¿#{ˆåùN8K…Ü3º?]ˆ>F (OáNæº-±Q—W(èÙþW?ÞöIÁŸ RÎö2†?çy6ŠþØ$fMleCuÞ,é<Ë¥ò—wÈ¥ŒB6ìŽuƒ:—BY™{ìþr’dÔlwUÕÝܶIjîo0$ï‡enœ+ÿŽ¥ke5ŽM‘6$ÛXjž¹HåÃá|Ž„—&Ä¢s¬Š,ôlIømbñÁz¿ñÓÌë€w’;°yîÑ—y«ÒM,99ðÑʵŸ§ [x“·¨Y®x8^\9´‹ÂüRÊùîW¼$&~6™BFŠÒýiçªû*%gãÏ›cöÀnÿ¨é{%Ý­d»+£ ¿Útf XÊ23½wÙB±ˆo:±`{‹ÐæNÁ­ZT¡—ñ!^)æç+]Bóôõ¬“©02áÒüFô=6T І-DD®I礹ÇãÎS­0£€oC~éiÞ‚!hÜOЬDô å{ìtåûŸvN_„ДÛ`ÃYHU·ø$±öÛö¡­ÃF¾Ëþ².ÙÚ߈DOS¥uNš°d¼MS8[Êu%7{Õà{*ЮZØ“YÔräNjÊØ(Õ0¿ˆöMûáðóûUú8d4´“[ì™Âœ¦R  –^9À}±.ñ\¢&´€ÜÞTE€ƒÝF ¿®+ÆjÌkmªûœ~—ãf¬Ðûf©ÚÕó£Zòèr÷Ι:"x¹g5Qnòü[­LK¨Ïýòx&âæTÆM,(#áXWqŽ í®z'Kº¯ó¿ñPu˜|ưšD‚q)V~Â,·%™3GµèA»?ÀxèW˜ž¼½KØ ×N[•­åÏ¡›éé4NgG äÞ“Š¹Œš4`}¦ö5.gŸûì”+c¡ÃòyT­&l;+1Ë[Oûõ@I«oà•GG¯œTi àÁ¤í!"*_UwÙæY#­.LrÔÑÖ®Þ·º6q£Œ×ºqØ{Í£-#¢ói}óÛ¾kè+¤­^üÖ’P¯¢üù…!ªšøÁ„d¶ó\ØÐ·xDn2²Zª6p—9¬©p¡…›ecð6¼–øy?ç“F4ãMPcz"òÅÀÕô3ñò w!>ðŒŠ_t(ÿêø…9¼J¿©B–{¬¤>Ó}QKAœD×Cß(¤L‰·±„TªA} mæþ„c”i‡ |õ !ºa/âû0cåó8§ðtz']D4L­Š/Ïyþ¦¬¦X¼c¹!?Û³ÖŒ:iËÔ„Éxk ‡)¸­°Û}\ pÄu1<à3Þò’ýo‡qÞ0;õlæ4EÀÝd2)~<Àãô[ån€3pA ø~þ`SÉk]ð7êѤعZ€NvHVV¶-´áÆXèæÀJVeÉzéÅ‚ 3Ùç¬Vf ‘ÒHêˆ|¶ÙÂOí–:N×ñ¿z'‹ïÃízý´€-vof™ ¤¯¦†jäÙ(â†ä‘˜›¾àÇ#V"JËc¨w‘¼¯Ö1iæ&ãÀ=Ù¾¬µþ0uP ‡Š›4S¨Niîššõ y–+Q þã’ûº¢w7b×OG³åÖùÍ3Ê”Fûs¨ù[DSÙ”fZ¢Îñÿ Ë4ÿé”Ãê •¸(›vÄ·V WËYýÌ^ÚË0ÛEhb´˜KÁÏÙvçVÞkly6Ÿ®dBSMpp&lCȰÙë‚ï€X~Ïp3Àò&gÅÑMmvà¡í’k„‰w ±â†o¹"%SY‚m’.Çæ[wìxT+€…l†“SzÓv®pH;­P­$©Îaº³<¡pßOÛ§ õ$˜‚Ï QÀžd>¢‡PÌ×ÅzxÒòeXVRÊc<|ÁèYX®Dg»`5çQ?íâ¶ÄêK). endstream endobj 1623 0 obj << /Type /FontDescriptor /FontName /RRVHSF+NimbusMonL-ReguObli /Flags 4 /FontBBox [-61 -237 774 811] /Ascent 625 /CapHeight 557 /Descent -147 /ItalicAngle -12 /StemV 43 /XHeight 426 /CharSet (/A/B/C/D/E/F/G/H/I/L/M/N/O/P/Q/R/S/T/U/W/Y/a/asterisk/at/b/braceright/c/colon/comma/d/e/eight/equal/exclam/f/five/four/g/greater/h/hyphen/i/j/k/l/less/m/n/numbersign/o/one/p/parenleft/parenright/percent/period/plus/q/question/quotedbl/quoteright/r/s/seven/six/slash/t/three/two/u/underscore/v/w/x/y/z/zero) /FontFile 1622 0 R >> endobj 1624 0 obj << /Length1 1608 /Length2 11017 /Length3 0 /Length 11842 /Filter /FlateDecode >> stream xÚ­wUT]Û–-îîظ;ww‚Cð;lÜ=×àîî‚»www'<ιUu«ÝW_U÷cµ¶†Ì>¤1ÛZ”¤ÊªŒ"&öƦ’övÎŒ¬L,¼EK[cª‘<£¨½ àCɉ@I)ædjälio'nälÊ Ð45ˆ›llVJ€˜½ƒ‡“¥¹…3€F]E“–žžáŸš¿\Æÿiù8 ²4·P}¼¸šÚØ;ØšÚ9@ü¯ªššœ-Lf–6¦1%emE)”¢:@ÊÔÎÔÉÈ ìblc È[Mí@¦´3{'€Í?ÐÞÎÄò¯Ò@LX" €ä` ´ü8fê4uøËÄp0u²µ>Þ– €¹“‘óGœí–v@“¿øÐ›Ùÿƒ“ý‡‡í‡íLÙä :Y:8>¢*‹Kþ#Og #ç¿bƒ,?Ì{³O{ Ë_%ýmû€ù°:YÚΦîÎÅ26˜X‚lŒ<>b€98Yþ† ÈÒÎüŸ0œLÍœLlLA ˜쿺óÏ:ÿ­z#¿OÛÿíõ_9X:ƒLm̘XÙ>b?b›[Ú!0ÿ5(2vföV–èM\þÓæjêôwƒhþšÚ$ŒLìíl<&¦fÌŠöÎ!4ÿ;–™þ}$ÿ(þ·üo¡÷ÿFî¿rôß–øÿºÏÿ -ébc£hdû1ÿ¸`7  øëŽùÿ|l-m<þïuÔ4ýG†ÿˆŒ³ÑGDìÌ?¨`abù‡Ò$iénj¢lé ´˜Ù|ôèo½º‰©“¥é—·ÀÈÊÉù/65 K µÝ_MÿÌó·ÉÔÎä_3ÿ çï¼™%%ÔÔEÕéÿõ6ýÛKùƒug5‡Äþ£{“ÿþµwx1²r±ÙY¸>–…ÀÃÁãó?ÄûˆõŸ²‚‘³“¥;àëGÑ,¬—þÏ?%½‘°Ú›ü5'ªÎFv&£õ_Š¿Ì@'§FÿÞö’ÿSþ{ÈMMÝMKóö@¾`«”ôTçÜì1ñ¯=]¬!ÅõjyþUö~)a›<冯Õ!L ¼Z=æŽÞveéö†ºpl¨;˜žçùÓv硯QµsÑï2ë#§žhFz]ÌÊo@é|fÑØÛû¢¢_ô ói¢Ý îâÖŸÜ5Ï‹âÞŘ\ƒý­ £&ÿø„*áðឺoxp ¿ó º{—>+ž’Ï ™*Âß1‹PHG½Ü+ʸˆ5¡¯*±+´[†}mñ §qƒ s__\ö:©5mr ô’~Ñ_9ƒÈ-ìrWê÷áø¶\˜h[&ÜyIN2K¥`#Ú½5òî‘‘=÷ß^ó”. %¸k½+“eÞ®áH9·Îà(¹ÞÉ‚i¯1šdêõÏgzžjŽM–k¨†—ÂÒø2v[ç¢.?J€x´n˜NÈ÷ 6óõø³;yô»ÈµÐ)²ª‘Œù¹Ô×¹nüäòeÄ;7ì;Q8—uæb‘h_mpmûÍkÑüñէ†?Ï(à¹|”cÖ9?2ÊÒi¢¦¥°l§J¸”„„]Ý‚ c˜ýÑ…f8¢W00Ëo8ª’ êH‡e]•‰Wûäs£>bº6Íê‡ ÝƒL’@K8F)ßÖ&ª Iχ ,²Œõ¾U?|^@¯~Äš2û(SÇS™éq«Ì áXý‚DëÇÌáõ’éhboãæÚb Ÿ=~L É.d‰×rEi졬kÍr­µþ»j’!ôl<¬'^¦—ä˜ôÜ\Ù®æÌ7oŠŽ¯pN‰“”ž ¾½¥LU‹»–"."ƒ*ðHÙÄÙ†rš¿H}‹È÷ꋆc=]ÌÉ‹ŸçýŽHsŸ”E[Á§°â^Æï4Á‘ƒQB·ÞÕÔ$²“ˆ 9ÙÙ-ÞªTñ–é¹™6`?~bv{µõ/`êþd¾XJÄeƒ9´#.¿±®@Gì£ÔÒ]Ÿd×zÁ1ã˜Le4çß„§æ ™Æ~5¢ºõ«üj;h‚F4˵ÌOöSì d–R°ÞQ ekw—N$¯oEÝ´Òcžš`;:f]O¾Nؤ[b]üLÚŠØçz#ž„žpï­°ØÐkª_ÌØCGÇÈ¥™p(ʆP§åGÁI!«æÉMÈ®¦vÈŒŸžo@ÀÈÛ”²óèÃ=C·6{ÅÍæö:\qî×Árÿ½ä«ëÔŸÖTø¶© ,)‡¢1ÿ. vãæŽ¤ÞO¼c™Ê=ßþH—?=J™H \˜_Kˆït±rCtàܪmØOÿAÛ¼Yb ¯ 'ó¶:=ØhÜtn!'ws5%d¾9>äòå‘ɧvMÏCš5þª”ç,ç[.¿Ø(þ…D#áèé¶Ÿò)¦êH£`œñ”ÊšÒÚqºW»úãÍlÙ±í÷‚d…rÐý¿3¼^)¥é¨ó<[áÔl8Ý=׎%Å€X9Åý-ñràd<Þó:ۉ龒…yTé~/D…?}ñŧk:¹:{­–— SŸ1…Ü (.¾„áO©qè]‰ƒ>M³Ó£úé÷Y݆&]Ñ †ž;f¤—¥à{„ð4kJ€;64Q\û!û¹ ûGDÙ;æ¿ú¨¹0_ªNÞøáRÈçázXhqc_’‹"˜¡zŒuRVð Š¦Š¶Þa“ËãÙÆSI{»n\èQáh:v Lørîf²6Ø`ì ÷‚’(>¹âwéü¾Ë·÷m0z±áýðà…u´¿äÎÅi-ÇúÜ‚| tÃA“÷H·bí5ÊwÙÎjó AÝÎz陸§ÎÓk”)˜í‹Éλa.D"Úo,”(5ðídÊZ@«Š«Ø¢!ßM;aë’¡*ZÉ“s""Å‹ü^H'ã¬>Y&f2)³sJ§½t.yÃn TJÖ ôÂÏžòiKH_àv¤0µ` ev(õÖNJr”6½‹eJ‚:]9æk/mð©8„¥bܼ€ fÓ¾ gãp$÷žÑFê!À(WÒt8ŽŠ|vÁ§‘Ø™"䣩þFÂá#+‰Ô••(œ(Ær˜0íƒ –;yrßJåh++À;ÆŠ&«êq"÷Y}9oÒRé(K:®ÐÃ2à\‚è`E£¯èBc»›¬Þò!°k@o.sKµÖysê²I\-ל5tnyúT»CæÅ7ú˜ô Ÿ£<© “˵fbñ~¤5= ¦Ó^ëÐ’ìöÆÎ:ê—üâæuïX_-wÔ×¥K³ª¸ROåßbÆÓ>.ŽÆëcªýÝÄáΩîQo¡Uï|ÍeϨìa³Ò4‰¬Ø*Æ"‘'É•c´¤| Ÿr_Í3‹DÕ¨«Øi †Os>U3"Âî}Ñû¼YÆ[ÛÔ²}»Ø ^§PBB‡óüÆ/>°N§£¹#ù&Ÿ#A |¿oͰ}59 ­ª]˜ÂOM‰ÅàePØ`i1d$ørÈŒ¿P‡©S"IÕ¹óÎF`}Ê Æ»äëš:™/wK’AϽ!•8ª³c“tK/Nʸïùc$Êå[jègs^H"Åþ«BUª»%…u8kñˤ³M˜˜¦Öi —X‘ñÅ‘ÑÅwíê*ÖÒ®¥Â®`䥔Š[ûù+:CÞ¼Á3KdËñU[9×Ië ]¯“'¬ëogã:;’'ykg,w9«‚ 5ÂEç~áõ}òFàÞ.‘è=Ûø•ðR¿Ä”>¾ÁDŠNÍp+]{ë„]Á)5¨€çIEÖ^Z"^€™ª`»Iª„Dd)ö§D(õ&x_ºàÇFT1FO¦+›™C3æU¦œÎ2û¬ýüd+}ÈI¢Í ¨Šcdÿ>ô¸ ¿ŽäáU›µ1þ:ñÁhãìrw¥ ÓÇ÷dzùu|ñs@èÂMÜx\SX×Ïw3@Öuìsc ­sª :ú~ÎØ5H¨vXWödx\¥¡9­¸wÏIUÖTeßùÜsï)ˆj¯E¢ˆï–áùtr¤»Ÿ©æö¨ûížç(hâøÈØúŠ”hÂ'C54ÛgdÅ…¨øDòÇ›» 3a&.ñf"Xty¹_¶5•=ññÑÃð¢â ëG¢ñ)Ù¤lÍÆE½½<~è†É™ †ˆï0Å$#Ö¿ïÎufSu÷IæsÇaç›ÈŒÌ…‘·f8W]n y†E^&zŒÈeÔ€j7i œãEfê&¨ Aúï樶¤ó jk`› jU¸(D„tüL´"DÚÆÙOÃÀl!VfØìG1E-ÿæùUê•€ç?ÒZø…Õ¨•ˆÊ‡5Ê5þj9«xg…ªÀßÁ¨ÉèÌòñ6ròú¼:Ä›°ÃtY{îR„.N~Gq­/ ÁÙð|òAZuŽm·ä”Å#x:F3¼¾,8k@ã{p¢éÈ3ÛŒo?ö¡WµÌ!L_cJˆQá6g$CŠYÔŸË+— Ö­›(ÆR9‹vgšŠvœ¤÷Qµà­ÐT¾A4=õ³f‰h÷†n,l~ÇÌAÿI’«D²IîW*ú`®)Ên9e™hN˜ž;ÍóÿºP'½ª Ûê‡Îÿ@ :܈ŽÝÈ®ž®ñ’ÉØ{瘣O³Ú8Õ~ŧöõцüUö5Y_#û(p¶.ó¶G:bÑÕçÓˆžôH˜!«¯ËÞžê kqŽgøàA=L*MÂgÌhÒ¥À0…˜þÂYéúþ¾4›ÝÏŸŸ2~,ŸûËc:X½åÎyXà•HqZ¯€AM#«P/ÛÝêƒÃß=”æŠç>ÛeÖôé¬ü‘O™Ô¨Å³þJz+¸V½\…]àŒ5UKàj‘O‘<-üY³T–ÕéΤ4Î1M¯"cªæÜgïΉ~{ƒ+mÁ5Gëø9t‰ùËÆ+3xÏ0»Ï“H¿·zÖuÁY~ÀÝü.Ï—ÎÊÏt[…ú§«dpĦlˆ“c¼GÔ‰ž]øEŠ ¦žV%r'®Š1] ²UoÒÐ"‡~‹Ï|Íh.¹–Q¢Šø:Iˆ¾¿úCH|¨y×P&è¶me‚óyÅ<Í3Xšò¿ÐʹŒT‹8(˜,#ªvúGzÖêÙ©QÓNâós³îÝ%c)„¤L•*Óc¤Õˆÿ¬WWÑØ·—IQÎTíÇ[›Sûö.ÕÚÓy–L©Ì‹ü^'{¤${ßrYûxü9¿ø#8¸Š ÿÁê領³%7XR7ÇÑĘd¢+š˜ q§×œôx‹ô8d oBQ' hÇç¥q§¹W0ÃJ^‡æzÒ4 ¿›ÞR!3+­\–îLnuŠÏúž xR¨“!uô„8­oØlöFíܸ‹r~ô>£‹Ã?¶–ŒÐwŠ“$¿Æ½¢‰vò,,+ öÔÙîǑޙɗ|yH½°‚¹_å5ɵ‹[$_úG¡Q—‘ÎÊ'.Ú‹{ÊŒ1<ç^HAîÉgˬnV{¥^„FŒœ:©£ë_)ã4‰ÎUÚ2¼8´gaÝ…ÉöÜ•)‚]ËAžƒ6£“ý·ø6ឥ”LamTWÆ,æf½‘ëx~Kã¾OÚ]û%ûÒø*D>þ²/,¹ijÃZZ”ÓÓ¼½£íìÐdáÀ.7øgõ+€ &fò/!ÅêcsÄ¢™ì„ÏipðoÊjCÜQ%¤h/²wŒ7Þ¾p‚».Ï,‡Jx<Μæ#z%#’Áã#=³×è¢ } a™×~ÕÊq²×5ðÇ®ÑmˆÀ-ã·‚1‘ÒåÍ8nw‰%\_D NÛt0öùI‚ž^ÔÙ¬ÐÕ£·W‰÷ àX]U˜=Ù*ñ31ëm¸Žìg#I-®ãé3±ÁÓG¤a:G÷‹¦ß„,#XE“éåaµ}B^ EdKÃ5®—%G¸¿¬˜#ÝNm™^~7ßK—~S½y<èÕó½“ÌÎSW¿Ð·¥ª„G3‚»œoñzošx4RSÇwì8[¸—UŰžIîÌÒ­Rr K錻ç³ÀÙ+•–›SWI5Ü€Ü M¾–YÉ`~SÑ-Üÿ!’9³Epûäy !:—3ÆJ’ÒÁOŒ÷k°À1Cp“Œdõ¾iÉ¢l./ɘw=©'­*´û¤T3Öº;.„?޳ø8‘]¯ò•þ­ÝWy}8ÙDºovüŠQ+;ä~É—Öï •-kpÆgmÔbÍxöí[¥` v–˜ûž.– ¥tþ½‘ªS•}]GÙË–ƒìñ±Ñ–”ázKI<™n$×èëNõ‚ƒPYž¾ÜQ6‹ ¶Œ¾ü$kÊ‹ËÆ[ 7d˜ð™C%ÉU/DúVW²6½˜" xÉÑ,Ç„›„’9÷Ü’™ò "~Äsh„©šå LN@έ5 ÜÍÁ± ×ÐfJóýâè+"<þ¾ùO£çþÅú¦ëŠõi“yä!©%ª=ëÅ»´Æ÷Â*›µªmêW‰GÖ*u.·dͰÙëÖfª21tô€wß@h½¸hg¾ÂÜâù´½‹­Bj  EŒ³Á‹R–Æ(ûABKöw“­2×õ-µ~|Rº”Æûz¦WÎ8u×+¿u`‘æ 6X#‘ùÒï‰z@:žTŸ§‘ïÙ4¦äøËŸ¯é—Y"«²%FRAúœ…Ñy¯8ÊÃõÕÊ;HÁHÚÆÞàyW9ªRýû®"˜+¶a2¥…ØzÖ:X=}«à„P,fgVH–Q{? Ä»¬E›„g¡:H³~¾Î(N»Òß^Yã E?ÿØÍÄv>ý“…­ßÎÚ‚mjÇ]%6^Ÿôé0M¦'’¨Ã8ØÐCZ8 ¿ŽÚÀsôbîRnŽS/Oïöq=$ˆ›6ôº¡JH3*lx™®dÅŠbIb磋h•·h” ¦¡šNPÏ]3’_Xf U”ðÚÈj­³÷NÙGó¥{ŸŸRpÛ‡§Sîâ=á«´6J ,U—9Š+"WÞÖTC™×ð@¥ôy.{õEº£“p€ÉÇŽ?ÌÊ>àKg]`¼WH³£0Ä󌹸”0¨]+¬ê§FW,„í áóä…U® éEÇ {“/¾ÓàxKkòTÄÅ»e¼ªŸ+gØ JwÚ”–¤"áX±TñzQ7x¶u-±§4SòyZ§'±yZÍ›lŸšÄÏ[ŒìµBξêÛí×Ë‹Eæ;iÞX˜œ¢\£ëBÁŽ 4v~º Ê”[Ÿ÷ÌØgôp ø\·x±ÕDGº`Ý›VV|k¹?ó>¿Š„ˆˆö˜7ŠœÎYœ±¯àþKý¼}ˆj»ï.¼vÏØhÖM{‰1žµØÑÃõ¥FÑŠÓ­È9μ·†Ù“¯_T¹‰á"¤K'ûš´Â4‰¬“ìK7… ­LZ¬gG™ 2=§x4Pˆ5S÷!a͆ë½7ÝØ¾­xŒP Ïra=⨳}ÙšM,И” !û]‡DàM¡a㉠-¨”xDøsè<¶b÷Á¸åö ,Ž$M`ê÷j ¸á_3ù¨q‘YïTЧ›œíöϪ)ÅÞAD *0)êÀ¯bůã #CùD+xð³ÊÜ+©_½ÖâG$hA=ÎMKÜZ¶ ‡8Û‚骋‘B÷‘™T`W*ëÃ1ß«Ér10[–‹¤œNɬ¤‡m׿ÏñÝ2’Â?/i•®Ñx¨:üÛ¯¼ÈéŒpÝ^Yã–IIEßPù–ɵ–®!\…3ÐpÂQRô¢ó_¤}A Qü$oªc ó½¹17;ÀxoQBΓD7\hÀ'ûsú™áÀi¡n«ý`®§ «è¿˜d7•ç–¦Fƒ@ì¶ÊZª—X¶Qó~]mÊ Ú:bÅÞ}b?Ê–H =„Оrý~ed=Ñ”E;r™TŠênAÂáÌ-ÃIÚûLÙ¯£ÁÒæï†Í8®JÔµ›Ô=y AMj «åa\'–X‚ŠÂ÷À °(#—¹zmò™ÛC1ѽ<Ë[²ÈÎ1DŸ£@çØªí£½•æ$I ´çûpÒ|IgN‰tåþ*kýOüÜgL¿|½š‰×Ú>ƒô¼:rÛѦ4µ†6„³AÎÊ…Š™Qm€òˆž“ì‘ ÑÌôGO7<˜W þ™‹`Ê‚×÷rŦ¼­·yP<é–Ñ‹z¯ƒ±°òQ×Ùò.ÜQ‰&T‚…âm¾¸¤ÇÌ…ßÍqœ£E8þ“æî^SqÂÔOê5ËÁVU‡®zG̺•`ž]#Áê?ÄUpð疥‚ÒÜ#G,<Øb\ä)DßMQc"¤bæ$¤+Б§–ej;ZhÜRÎåy%{ÏCð8õi…°P=›ñ ¥¤ióG…´»Y »Öz×[ÌX'¯ª=vâXð~ÏÖÝÓ©ÛTÚ£Še¿Ó¡-±ԩƒhžÿ4YýøªR±*öù“ KÚ –.TgL’Ò°ÃhïÄk¾ñÓYF+%-ï ÜdIþ¶TJ…¶`¼ŸìeªîÌ'Ô¨Eíåt÷Hú\©–U&4>SSÛ/T“pRŽv ¦ì«T[GDïò‰ìEÐyýºÜ5Îèþ½§‰êGà‡çÀ-_›ŽÕ‡*¡Ž6Pž2•×V>˱­&?žÜîëY!–¢Ì} /ÚÅYò›Â¢ù®ÂÂæÈ½Ànt]ØH¸\ëÛʨºÅÙJ8aT¸ìÐG[5‰ yè]ôŠ÷ÓÃëãwY³Õè†óèã pÜYùÓDŸq: ÔÛ´O3 ¼C#z÷¶«güæÖHêïÝ›çÉß´Ù‘ÅèÊ*À ´4'×™’qÚ•v)úʬ'¯¬¦„Yp–YzÒpêñ­Yí`zäEûd`Ô Íô$ D-æ22&èZeõë ¦B—:*%[«ÈžNÞÓÍÊÍæ›º^)uê`€Ž”‘Iõã'dÙ0’µŸ‚QˆU6SÛ…CïÍôté>Z?ߤ‰R¯z@w(ÓXŽc¡žÍ"7Û ×Wh¼A´<~±”ýù¶*q›äT0›ÆÚ²/˜t”0#¿]j•%, ¿6ö„Øúæuáuñhd áLÐEŒ±kš…½Ìœ)0sù/–»Ð.§‹qrFDúFE$´øä•Òf&*v‡(} wSÐ ‹Ù ,ÿÖ‚Öeò¤Õÿu%Ýù-ßm¡Ê䇼.Ì]”>È;N'ä¥21·nw=>C"Ñð¼ÀÖ+me¯ ¤±LÞ ‹Û>kŽDÿò×È-§ £ï4ùÅOMÈÏâ4?`Gâ\#„IJ~È塸¸8jR®ã±Vû^ ÒcëIˆúÒÑd3ï4ä§­Uƒ‹„s“·‡É8ü1pE|"ÛÒy™¬q¢ÛYд«-[Eê½]¢ŒéÉØ3âz6{%†®M«°?Í?óq‡2Ëbx–¯ÖÀX$açøíÐ8"ljtñ]¨~†a|%zXí瑾;âòH_Ù›n5•Lˆ`ˆÓA5Ó;:TÄ¡4.ìr½ä¬ñŒ#_­–üÔ9æt¸m㳿íÄ£EtÉò3’,Š V2LsRú“8Är`®öʃ¶ß‘de4þ1 ôÀ€ó÷•YÆž\@ˆå£ú;)¼ëÎ\¹[mh*9Ñ®^®9ºôŸWKÓØeETÿ-T›»Lßß í¥!”0ɼá·a¡2ÁÓf¹8¨Ä>mçB$gHà±ñŠð½}„FÈ¿Rÿ­›HP{üÙrŒÙ\ó¾*Mᇩ–r&Ÿ€ jÊ©ò[Ì›>ù䶉¿ÐÎüý{Òf]Ë/Ü8¡7eÁOrß»0×?ó1 lÏÕØäg(œˆÌ€EⲬDgÔ„˜§ÞõC_8³Ð ?)1|{i²À͈TZ“ßÞ>Á¶š=€|ÆX¿-½wå²wt®}HÙÝ•Bpx”Áañ] ©¥il²e¶Šf+*cŬ"ÈwIKÅlFIÚ¡/ò]Ú7qÍç–;¬ãýÉü<W:ûÉg¿á¬#ß7™Ô ãœ$°ToV¤úr&ðànªp?Jb´Oe×*ΑyÕîW»!š_¡„‹ŸÈœœŠyE^”‰È„>¾-Aï™ÍöœcÉÔ¾î+´hd›•Ó“ª¤R’@«Îºv¢´ìµˆ¹¶åqŒôÜ,†¡Áª–`$'V'æÝü´òQC2u¹ˆÌçÓt°tA™s©ûcׯ´fE˸’ðº 86ªß”ÐJVk\ùrR@ê.S™R¶ó8Ål·€W ŸjàkžF<Îý~o>v[­“ÆùÖ”öнÜõ[‘sJ*bKáÈÄ)TW ¬OwQ(É@,sN/Ë @‚n‚Ϩâ6ñ'¡ì—uŸ*pª.ç:c °ÅŸ6í-u@¬±ª–âÉÛ¾ÐIÔ“(Lû£\!¶f3L¸1 ,®kÿ^9Ïê@$=$QŠ>úˆœîÊHt<* ëVÅ ÀÐ?êcòxNû^Q:çLwá8×dÑ4¹ªöŒyëi|Š©iBÞ‚ÅØQFÙpÿ©gA½÷÷Ù"Œ3ùj[ôŸ·Žý%®õOåZ¡)†´Y™T£ˆèð-Pµj[ÒÇ×–Ôý|M55ý+ïuVÝ™¬dݾô…Ö«cÐÙ1ÂBãìé„çà¾ÕííÓ¨þk4™BÅ`Wus\í+$ïéwÕÃ!­0ý2ý •RËb¦ *p;Ôe»3¼ßxç$Ã*fzq؆é:?u6t–œŠ®±!ÄõG©’l‹xc^]u‰ äþYp¤úöÚUÌ*Ri%hÍé[d>3I_å Ó±½Ñh¯Àú¼5N‰Ç» fѤ܀ö5çO`Ç‚ëLãÕ+¥[Ù‰¡i¥œ¦¯®FÅÏ’aë ë¿8%u¥DräâS‹Žþ¤ø(õóàùÿ@ñ±k:Ï1/Â_R¿ÿÎ;z0ÉV¢ýIé í`Òí"ëûÍ!µSh]½ÄRƒm&ÜyÜ#•xM|ý“D¦v™‹=‚FAOP{ÓqÇY;(ïGý{*…æ\iÕ÷‡4ÒoGùÊnW‹2=Èþ"bûzjÅØ÷‘üå^òѽFÈf>w¢8C#Ç>Cb¦0^Š:~ì^hœ &,ŠÌíûþrý]n†gk·< ƒÁ ˆ—oƒCUC‘eT>Ôg¥b–8¡˜IÓç¬ÉÕ–ù…&»tÙic<¥Ì‰@âwc¿ÏÉ„» /’ð¿«èÑRó«^4€Š\ÑåCn’EG±½R^(Ôê^•;*&;ŸmN+,ˆý¦[QîRw8ĨPMòª.îôHîó~b©ÂŠÑ\öZ6Ú§ ½ùªÔÝ-p®q[°ÇõeÛÇïÆ×«”GA-£·Æ[<Âv¥§&öÐ X[kèªäI”¾ßwòF W -9<}Zõv/äb påó¬»òH%ôxaT¹Gµ‡üþIÆ©\õè“8Çæeãð¤Ñx´À–dUW¦ÑRln IÂÏe³Ž—”ë龄‰çÌÝFй7,¤Nýy)I k.i=ðXÌë˜jR´Rªs…<… \'‚áRIö|µ>Aâká~vƒ3gGè—\Ò~¶Ç²Õr”mã’‰ð¤ÆÛdi*ù.-’<4Æb ‹OÝíÆ{8ß™ó‹ã_¯.kƒŽ‹œA5½1Mä•^ѵ>ªèÞÚ[r–³1átøia‡¨§m‡áWùîŸG ¬Ô\¿2hU^½ƒ7]ªŽ¨e4SjRwÈæ@{“t9¬’9›æ|Þþsv¸+í‘XŠ+¿¾9XÔÁ°ÆPç“ϛۋØ1º«¾Ø6ÌuÓÜä÷Ô^Ühš)?¸NÁY[g#·ÏïƒÕèL˜ºÕ¹RR¢è®‘NÄtãü.e‡lÚº˜C;€€Qñx´Ÿ—/µ"w=xÒ¬á å†ÑH®)Km‡euÔvÏpH¶N—þ>ÃÄ}4š *ÒÍÀ>œ .p_¯oSâ7#ú¬!+‹Q}' ìÕ­Ÿ¾¹·=²Æ*Òt«†Š±,R¶‚7;ÈâþLJ¨±2w.}îUk8®Eff:“Ço²ðÁ<:½Xµ÷Ë÷ªö%çna¶>Ó|{Wy×®¦qYuD÷Õ«Åwj¿}qÅÒ(&Ó»8 ¬ž]¨ÜÉ?ÓrDˆ¯ž&Ü+I‘—$à:-œ¦ŽñwòOwî©Ã¥ïKdšw#‰ŠÐ®5N›ë,Ôg¹ ùzúÚØÿb6!ÆM™!æåïŒލh¿®aÅ“qu7D7¶3õ¤YŸÐÕ/óËDKnýPÝ ¨-»‡ó¬#PUPK"f]_žâºéä6ÇÆÖî`^2¶ZÅ?<Ñëj¦ ŸéGw¿m\ߌƒâÿ8–UÊùǧ•…¡óï½R¤0=B1}˜Lš Õ.•)s)à$dnGÍMŒj€NNì[Š8'Â_ƒØ?ø> endobj 1626 0 obj << /Length1 1625 /Length2 4983 /Length3 0 /Length 5803 /Filter /FlateDecode >> stream xÚ­Tw8Üë¶Ö;!j$„(QFï¢×ˆ.‘à †13ÆÑ£5zɨQ¢w½D/!’$z !¢E¹#ûî³Ï³ïùëÜýÇÌóûÖ»Ö»ÞU¾ÛØLD Œ°‡h#àh ¨¸Àêfïéa‚Ѝ#``=4ÀÒT||( EÀ5AhˆÀ hB ¼¼<@ôFAœÑ S+Aaá›Y.\öÞ"øH¨Àÿð‚ÀH7§ø¯Í Úp„ ;Æ÷ôŒt:F‚Âaìiƒ: ¡¸Dàˆ@`8zQš‡(žKÍx !P|ãA^@7HÊ êáÿ@=N(ï€Â`žà x»#â· $ ÷pÃcx2c„ÚÃE¢ø¬ÆšÚèD;ƒÐ¹= x€pÄ{‚ž%ýÆð4x ‚Â=h}‘ËC=07>7ž ‰‚þ–áé…;ý¥à&q¡À0ˆ‡žÏ}Ñ¿êü[õ $æý;ñÛë_ hÌQ” (Ï逯çv‚©Ä.–EîˆÅÿ°ƒ=‘b^Ôï \ìŒ ^Œ€Ã¼`ˆ#•˜O øï¦,úÏ ùñ?2àd¼ÿ¿áþ}Fÿv‰ÿ¿÷ùïÔÚž0˜È ¿<2ü+ã0\¼3€‹‡êðB@nP˜÷ú»£ä¡\Çþ Vƒ;á'"”ÿà õІb `c(ÚÁà‚á›õÛnCP0(‚êï~⃀À¿aæÎPWøE÷eäC8øïÚñsú­\LMÿŽ¥ž®ðzZ{ãWmî„þWšÕmø_‡ uuà+”•ˆHŠË€@qY€¼´¤ÿÈù›ø×ù6‚b÷ÅEÅÅüÿŸ¿¿N6£Ñ‚; ÀKc†ÁÁø=û—ávðD¡ðãý}õñeÿyþ½ñâ@5óá ê’ž•®fÍéѼßÝ $î C×™?Ï\‰èL\/³;© ­S8{éýnyº¢/´:Ðɻё ù–wÍŸW°+Ÿaž¿EVx5X̶˜6cÓ*Æw{Êð3‰µŒ¸åê∉©mÑ ÇX‹$Šbû@ð1¯Wþc¦ëûHº‡´ÚxæVúz‚ËÕ›üIëû7zûû^wìv­\ÎŽ§äSô"¡å~ìž}UÕÚ¢Ì7¦É²ãÕmë¯{í¶£Ú}!«k ±yADÜ6‡œŒ£Pø•Ôo˜S,ϹÂ9‰NIÓé¥y1§ :æ{Ò¶ØíÁ°º.3{æGTo³°Û'ÜD qâ0ÌiÌqïAZP ÷åøð¸ž96ÝÚ×Ê>£ó±Õ¶ µï_*&˵e¥äÈÐòHOˆ  }$è.6?ªã¦ÆãyÕ<,W£GH¹Ì°Ï/ÔË_Aôk¼UqY_ÕÕjÊv顤Ä9!… +3[Ê×-ެàkÙMÉ ´ò\£€ýCì!€ä:¦É`öx»ðzø¹Õ8ë¢_Ì2ë!‰U”qÊbáu…¾…z¿ê›ë6yšïÛ]2Ÿ¨ûÊrbÒ½_Å}öÙäœ0:-›2iêàì,bsº‘Ä‹{ƒòRœ;„4„Nãi¯ÖÙ®†=O×-ÈFuar—”´†>ll‚ͲXºîÑs‘=ö×°Ó­»:üáÒ¤E•å:²e:d¼ôbït ¼ Tš|$;—ªézzWîe*ßÈê¡À>ë 2uÂfœÂM_)¢0z°)ñƒ½Œ«§Ë»Äm"sç\·ª­˜äW«TŸ™Òq~šþhDhðÄ’~Ç‹üìe1›LÍìzþU§½(ÓòÔDºX"HAï¹ÇV.Ò¦egô<_¼•'=[°½:/Ígê@ÈG,aÑ«•LX d ©©½óÅPš³¬U&Îr-”Ròá™-Jþe”´[|=ÐT/š ‡GÃhdßQê“åI½šmð‹P«=Îõy¿ÊGÃ#ØEÅш󘞛·!O·É3£_C&Çš–›Sï)£h;Gk]'è_äŠÉt"ŠkíåÆ-¤¨¯·Ô_ßu_…ä7³(½R¯áOÀn3_)æÑ<²W[æ|jJŸpgoBÎ$ö˜‰ªÌ0-^Jª$Ó^gÿ¢KY°ì3s»g*Èký,ãÒgåô¦½P»ÿhCØÁÝMk=½õ–ðŠlÛîƒwžò>Ûë‚™Jš bóóXã/”ëE£•9͇?ˆó *9 vÅ»ó ÂM&ÁÞ ÞoŸ´ùîG¬}îöïRŒS_1þ˜.ö£Ü~ §Ñ/‘ªppä( DÄ4¶Ä"øÄL¿¹¤ãæj¾ûŽàîÁâゞÃ`JJ/í«Ï²˜?I®ùò¡Ä>Šê”ÎÕ‰^ª@´ëõüwc”´i͉)ž×^ä=i÷Ó1þU1Yg_U$ȧY±˜¡‰è(?ùŽãÝ݇^lªŸ”¦’)ÅŠ¨_ %g«£±°8¿Ìk–¹±6‹©çëB·(»­’°»¢mh²E®Õ®¥7dV-d[ 7ˆ½«jà ÕŒ¸»&Iž”Äù"nÒL〉ª®¯¡N¾çÝŠM ¡ŽØC&Œ=OxÚf "S^D"ÄFÃr“gK3åoÓQ0F½±WN.pë70TéΚ§•a=/(w¦³¨lTÌowÝ)ÿz‰£VLnsKë6\–ÔMKFé~¨ÝŸÓƒµ0³PHIÇŠ Ùb­hzÞ3áØöXdøÑUÚxPZ#[tÌs§µ†M’aw`8…[¹Dz³‰=rbú°ª£ìõ¥Y@÷´Õcß_Úä&úóÂl”®ùÚ.º‰ÈIý½\©q±â™óäÛ#8±Šçðv}'?Ã/î‘7ØÕ?ÑL'“­HœZžípWÍL1Á YI¾©y?µýf”p—ÀüÝpê=4Í©ç~¡B!m$«v;E¸dôêõ•Ïá–iÄ:´Àö¿‘dõžöàòqЧ‡zïΘ}ᑺ:¥çWvü>hÇòŒ7:Ê.Ρÿ{hÞKéÄŒ@-O—yuz˜+éP!ðrz<·W?í‰ìWÌì‡lÆ{P¶á,»¿2ßÂh"ñy„ä¯èËš#H…Ö-kæ*•«’×9îÎ8#ASfÒ|Rº´ÝL'nh¿+i)Ê" Lôß鿬Tû]β ¸ª´žü­å­¶ 'K®¡Užäàê>3ß¼ªp)‘/^VþQ@ñÓBâÑ%ǶŒ1A/â„Á~—¦Swú‘Íý=GU¡örªkÒÉnrü´¼Ã¿Ãùã·ò)º^Š©…æ®XDIv éÝ6Z2 éÛŒ¼"ʾZØð5Âé>î“Ú²)gパeBÕç×$g³T. ›œéo†šÜ Lâö?Ä*¨žÏLs´hDÄ©ü¼á\ú)þZâçKº¹Ú¬qK¸;’ôž<¶g‰Dõé2\oSvž¨GEÑaªº¼›ü‚kwI”ö²e[¿vKN9˜ÃÎRh4ó²ýËÞñ@%f3ý´JT¯^5äÜ~Pu²¯_ŸüAòµ|õèÖ}ªùx9¾”a¯¬8¦² i-ik}TïóUà*HRùn ÷ý—ðmw]þ«†k- ÔQ ÷oGÂUŸÚÍKæ±etGư¬R|N7s¢¶îùbÎãò[cc¸ë^°&rÅÐt0ÄO¡Ÿ û´Í˜ÄkçxÜJ7z²OÚèÛ]dv€á:ÆÈ2™sÆ`wª{²­†šhf¹*c”?ÈEjhŠ˜kí¦6/Ss´e<*ÜëwÙÏb{ ŽYíÓõ§ˆ—"ÏÔÒãVõàÙ&)ãa4üfwɲ g£µÖ «ùqDõžàh}n7çÕKÆFeOf“=Vý-YJuÁ[%=PûV« oÈ蓦µ&? !33¯˜¬5\è·4+!Êâm·ëgŸK‡çù9µ¹©g¹iŠ?h’'ƒØHdZïsâÂs«i}ßÖ<µ1®(V’´\s5„œÐ²mñë;>ø¨bb9ØM¾7v±{ ÀbòOU‹¯ l0{ ”·s•+Íä’ü¨ØÛýÄ´˜ð“öFç‹"6Ž'Jænhý¿ú­ôø{ÊÖª¹¦ËtJjÌd~¶.žîú;Äl/ÜGqöϨYÔ Ø%†}Ü$d^y>Ю̨$£wKø9ï² ;??bʱÅbý‰Ÿ¶M+Ý&§F”G44¹€b¤¸íOÂ$V›±æNÃóv ;Ò–ÝŒ+‚R¡tcj¼¯‚¬mI[ E̬©\t’Y UjÊŠÊOž$).´Ò“*Ÿ;ósôDùúš‹I¶[•øh×((=—¹>xé$÷Ç*Dȳ»59«œ­¹ÿ°Hìɳ5y.¸gpàƒñòÎâš¶,­^Z½~÷æÃ7SÂ4‚âtvü'ï·m¿š#ó\°4€Ö¦Êf*2kЄ™Nôo¶x¿lR«rÅÚÐ¥­ Åß’ñ™¡ª Ê×ïÇ~d&e"ÑUE)M2ºéÔRÞEj§¨Éæ.,3×Í2§Ó.ñï.nÒ>%¼úF7’Y)YÉñè›tE­XîìUÙv°A#²µj˜9AÅjC]’âü »³\C‚„ýxCÃ#°to/;Ç©ÌÔ|äsÄ|#WY¦Œn7ÿ@^Þêã2ê%ÃuL×1:FQñ™) èi§ÙlÝQ¡•ÑÀåB(_•%‰†‘&>½¡€"}ND(¼º|ëJڵɀŒ#TÓ(o;lpFlܱ.\ÉHŽ2Ùsá Áä <úé¯Kµ4d?U¡$±š¿q´ºp8ØíbgÆthžçÒCÈQ2énøÈåÛp’ª'/‘ì Ùµt#`ÄĬC|«B.×Ò€§ºëô) ¬µbœ^êÓý/qN£¤~ì›ÙÚ²Ÿ6 *^˜†X¸wŸiXßh·;FlլѺ4p×4˜õí7ýtY‹Y›‹è&Ò„ú"}K eð_"íÖ ý¢Ä½;¬%¯nE`l¢–v*¸vPã™ÑÅWd$Eö(޳k„~.LP²HÏHÏgn³¼“æPÉSnêÛÏë9|g+ôìrМ턗Ù烮¥`—ä¾#[ìØ}öºA¯1£kÁrmBñõµ€³‰ÒI͸ÝÇ1 b>á¹VË/_‘Rû¾þÏ3\ë%âítö7ëÆN t(‡:µF®Þœ99ø€~èkõóʼÞu5–ƒWÕO2uK¢n v«ë±GíhM†¾Ž¹ì«mTØ,žLÖµiœQ'óüà3’!#D*ѽ"9÷uHÿð%èÛ^X®.Ф‰ì`ì ò93¶58‹ÍÚuÎJÈzÒ\¯mJ±T¹Zfæ—ù«×š’å{ÔüË«M‘Æ7:²[…¡Ãõòl ƒËBæ»Xä¿Óh¨Ä½UëÓ\ŸN¬W˶ %¶>¸Œèf?©ˆÒ§—T,ÃØ³¿Õp|®¸ý=¢4_EeEÚ˜C|í v;KbW•ÙÿòOj”Å— ,nÚ1阉….¼ó@YiÔ¿Þ•@? 4=ÖŒk|ÓX✠°©35>:ÓÊS˜¤‹“ÎñÚ{†3°uªÊ¢è”,ýYêM»Â SOúOË^†‡}x|X.½é[¡Ï#y+øÆn•Òn’D–FId+$÷õSð‘жóyÊꑟ¯iÖÕçêÖ×ð¨á’ƒ#¿pÌñsÉu÷,¬½tø¢2nE“ Õ¾Ms‰!Œfј› Ò™K‰žÂ8|ërŒ‰qæË³9°ýxõÌi÷>òhV˽Ý€^á+#Ù:éÑ<’ߤ W_\ž¬ó×Ñ µ?#ŠMòÆÜX]eb·:cøwÓ¸ endstream endobj 1627 0 obj << /Type /FontDescriptor /FontName /AJOVIH+NimbusSanL-BoldItal /Flags 4 /FontBBox [-177 -309 1107 953] /Ascent 722 /CapHeight 722 /Descent -217 /ItalicAngle -12 /StemV 145 /XHeight 532 /CharSet (/R/a/e/l/one/period/s/zero) /FontFile 1626 0 R >> endobj 1628 0 obj << /Length1 1166 /Length2 5652 /Length3 0 /Length 6415 /Filter /FlateDecode >> stream xÚuSu\”Ûº–î†Fj†.é”î®`ˆ˜ZB$¤ )AéT:$éFB¤›‹{ß}ö=ûœûûþøÖzžw½ñ¬gq± *AŽPu-(,’èÁ<}PÆ`¸Ž ÔÅpŠƒ‰¹¸L`hèÐ7„  FÃpU0ú†7qõè‚‘@$’’¾YƒDÿ D eH˜'"`EC‘0ø ¥Špòñ„ÂÑÆ>^^0(ÄŠBø  (€óMgÿY ‚ð @Â\\Ñ^S#ó{üü#ÂÒÒÒÇ€¿€*s¸o¾P„×ïJ7)4 p(ò¦iÈïXg°†þ=.€×ö’½œÁÐLå,‡¢÷nUƒCTž¿ ˆk¦ CBn† þS7w8Âô°3 ùc$ˆÐóö>PýßàˆøoÌŠˆƒD@Ò Ôõwrþ.iàýƒþ ƒáà /„Àì‚Ü¡7?â Ø @#} ÁAÿ—ø÷±°0sB¡.7×ðwöêüç^ŒFÂüÖ !Húýýke{s¡Ü#àïp=°'Ô3Ò¶ÐRáÿçìÿŠRVFܤ–ŠH‰ß8å&£´¸è?3þK‹¿tø5Ãþ·OÐß)Àé?ǹÑñ¯‘|¡HÔ7¼Øøàßóë!Ð0'(€÷oëØ€ÄA7®¹ù ÿWKýÿ_õÏê>¨Âû§€=PÀoE<ÀÈÿ{Â<þËšCÿtÿÿ“çìsR‚»xüK&Jæ…ÀÐN®Úå/•!¼C¨ûý’‚ÂâÂÿàL\aNîp( usPP8ä%ÕàN î0F߸Œ„ü øM;ù ‘7òüqA7gÿÚ;Ãn„Bý¡NÄ3“'Ù·Úˆæ“j%&?A̰h—„éѵ«q¼ÖŠv§Þ÷&±üC©ç9ªƒ®ò•Ö8º8XQILjñ’~îÒÊ-BÔ4‘ÔØš1Œ[Bu÷õdHw y?ÿu1&?½“ž·¡У¹ÃVvdÉ%¯2Dm…  e>±Z[÷ÖÇgb)+¬ Ò_Ÿ¼'M¾­¯éb(ƒ?Þߪ r ’N¢ÕÖ0zÊ£eEÒ°uLSÑBí01Ù­L&:¥AŒ®Ýud²,‘”ij9s-mr>«î÷*×µõšøXˆk˜r]{ût¦™ß=„ ?åã›xWdÒ5CÚJc_å}ZdØIÛt‰’©‘Åy½KÅÄ„ÁK—–/­hÊR®i{¤>ÁžuNÿÞÅqÙD¬ñØ´eÏ\)y¥kÞ(ööCð\Ë}ÿ3XÅšUAû.М`Q,2ÊýìÑ—Nh«ì>­‘º¯FÕr\ޝBåêä`¶”©ë¶u›…I§ÿ¯•O6Žbëò_DÕ¸ã0@"á·‰ ¡Y2sC±„ ãñh`CŠJKézˆæÑ:€sz‹µ¼©N$0äk*‹û ˆˆ½‰T¦¾oüŽBèYPÆ"¿D7ÎèK^„ô~šnÿÉ Ò2«oý T‡ÝôÒ'-tPŸÉ^ù9~Åvºï~bÒ!£*Ï»ES±±zªŒªÅ/ƒÞ I|>î¼¥Y5-cöÌ&7£ÞÑ«^1Y^ƒö2$oyØ2QP:â â 0/[.Ç.$d‚ƒµr93ëIŒh§reÏì0ƒ+R Þ+—ĸ2Œ‘Z®*FÞ}ŸÓ÷äÂñ274úPϺÃ|šÑ¾Ù –øÈÔÃ3åcÍ`CRÎU¼¿¹TTŒ^¯r§jîɱ—·ô sÂ}ª`!¯„O¨…$½Ç®Nhç‘)'rðº€pÀ~˜4Oö¹²†°Ðì}cA¹¡ËA³ë%Y‰¹`“eœžà7~|@ÝW[êÎø^Ó•Ïtn~A‡´T|M«’|ƒéS5i–#)’†1*õíYË’Ê›ï>]tYÚm«÷+ocS7S G嘶‘†ô”¢Cj+ê> ¶öKô›~[ O‘•82ÉžKo:ÝUÎ.ésj¸§DiBVÜý¢tÔ•dØŸ2h4d.Ñœ'j Ý­!,Ûâ«\Å'»Ã×RGIÜÃé{¼~ ˜4™Ù‡¯Ä¡d»›Ø„Ö ‘ëáþ‰3m [.s_=Q¨¦fO}ä"L“ ¯SFÿˆÃƒÞd‰Zx3”°‚Õ´ÜÊðºÂ³Xf©÷°Ð¡H¶ÎÈõSµÚÌDž!ÏnEšV±ç.àHú1°*[` ¼ ðK#'“9Žr†w(Y H b²y—^oƵ9$N-ô¾]ØÄëëš¼ðº̘[R‡ ]a|+^<þ!Šóvç0®{§10mø¬õñÔ«¼¢ã&:O±Œôî‘YF >¡{÷ŒUê²aOçÇäd“sÙƒ4¾ ÿ Y;pXýå@€¶B:Äï¥ÖØmÞG¶Â‘½:±Ì{œ ò*Ò;m0ÆŒÀšYÓÕ§»š+µ*L§–ÅžÞ¤÷XÌFt㳬¹…!1–2G>ûC‡HG:¹É—¹ê|sßý¾œJ³ÝËͬnÊ$fŽxL-qÏ!¾úÅý,'²—ö,ˆn {½ýi¬¾7»ÛsCÈ÷XU1íîï(*².ÅEJŠèÊmÝíã¬h±;»ç±˜pAÎ?±«n~’À®ó“©û½|ÛN T⻂n+ŠmÛHoý()´¨‰9!-“1cÑjìxݪ輼¡TL8Z¶ ŒK‘EYH½¾"Ͷ)tJ5l+¯/‡Õ͘Fîø×|>X¹‚kØÙâSá¥H¨_‡¼a2àî]ÞÑ_¸v\ç§\Ç*=SyÂÛDñ5Mý\e‹¤¦¦F.öó¹y·ˆW›•ÚæŠkº[ò”-HÆæJ¹“¬sõ”Àø,ˆ…¹ï«ÝÀÝI1î7«kƒRUïéGücB':Áf©j¤œc'TÉ0ŒFç¶í׃Ãþõ6ÄíÙ˜%z®ñJg—_íմ߬ííCU ’ØÉŸÙ¯%^Ž0;íÊŸÝm ÿFú¸»„‹î¹²¶G›*q˜óŒ> JoËœp|)‹éÐ_Ùeªµ<íM7M‰(JózÑê„?YÃu8~W ‚™N±Ž·—ù¬Ó±½Øö5+5IkŸ]bêò^äâœswŸ»¨3á¯ê±MAëûk;±¬GK>5}Ž3Þ–Â_åžw|­pr$^Ž Y6‰%¬Ã74º;oÿœû9b6Ðõ|´Æ„ሱUNd‹òVv?‹Cûœ™Qd0²LÄ„Kml¶Kj€iDœq1n_·¶lÙ‡—AYh³7²· Å`Û;ÎyîW©¦t`+{†bŽ|O+G;ñãéñŸøø‹V)¶¥^Sõ¾hØ ú—;‰º͇9¸³ßä*5×Âfàs‡‚þt~$â´Z—pãºÞ^¹côVsvA2™eËegÞõ),–cÐ"•ôé°ÓD°½þ^˜lœÁÀ’ßës%“ßrKõWËÍ©bcÂà#¯;s Óu³ùבû©FXÏG*¨Å}]mÓÒŸµGÍ>KÅ=+Þxpy»s5{zª‘¶÷G}&š½ê§…9Ë ľkÙ¼²Ñ¥ 3êa˜BÌèÙ(ÍsJÞ/§Ré°§ûç’…‚Ïvi^Û~”3}×øÎÞCœsݸš¾¦Ošs­åŠÐlKÛshíBs,ääÖ>Qý$<×l{„é>P;qþ´H¶IquôWr…2¨Ñô«IjÙ¤o8눙æÒC6§í˜ú¦öŽV͛ƫï*«Q ßs‹™åöj½RT1zRåG©dõòÎ`ù±Õ19ª#d,ÿáŠn GÍ\§ßóJ1é{ð¼øi'¯¶F,ÖX:S×M-§½î›Ù:€(¦sGÖ¦F+/°Î,o”Íó59GöØ$¨ð],ÿœg‡*CªË­â/·ŠÑÒmÖáY†ºß3fj•_)ƒ7)Þôâ9díTÊç·¼Ÿ¡m]¹¬šÄ“væÂÇ´ÒèýOêÖcˆF(žðr›î0×/•°õ½Qóâð/t‡À´r>X`p?+@/²ãIÝ'¿9Fîæ$µ9&"ýÙ±/_32Ô¹œÉ Ç4PU?ÔêQÅÀ•îý×"ê­j(w Œ¼|¸P5ER|(XXû-fͧFˆÉüÄæø‹r\¬ùt¡D“t-1QÈ©ä=®¾W~`¼Xo?Uo·3¡dnUÿˆår:ØŠ™àk*#:0²3s_»‘¤Õ8ñ–qò)ôÒC2I†ÚÏböü²]“Ÿ«šˆŽ $S…‹?ñK#Eˆ[°iÛâÖ%ªÚ»ð¯RYG}†W?÷SE§ö?x°¶°žÇ‡¶lý<<4+P¯]åÝJââÀ8*Ms«Ù¢Èzß2#h'M].4ø¨xY0ë㲚eÿã=î+̆{þ¥‹§‘iÔp8•¼\g+ õ1þ%{Œ¸+o¹¤{Ñã­*tžøÌK¨é÷ mr[˜þá³æõ±GØàªŠC¡zKãÒÛIe;ëmp™©YÂNÕ™ß=œ‘ñußžm[±ØþõRÝõ˜ÃùEDÎTÕÞð»’òE×€ÌÅ/¬|²õé·ƒ ö#ž§E- 4…%<Ð$’Æ=ñ9YÆÄé äqôC¯u”à?Ãn[ú´tèòW>¦4P p¡òÙèhe“Œ3¸¸³¿ªá0Ôæ†8jÀ±œÍº£H¨®É«n2¨Jì{yõÄ·¾#?”½q×—gí«tÔºk÷”3ýh<|Ëu1 ȳ“ÿv”“µBBðÕwÇ¥hÂÍýgÚÝ\.»MãÊ*xZkŠt-æ ÛÌ ñ –ŒhBA!Rë£ÇÙüc™„bçOb—vL<”³¸r/IÒ£`¿CFÀ ¡LߊU¸kôÐ턪æÚ¹¦7ø?Ìñ Ãv^xµ~‡ÉpG„ðGó$E>á]›Šjí{§ŠjJc×}»Žü•å­ñ–ôÑÂ+:liÔ'åÌ<‡èjMÒÞR(ÛdËvÃÛ6)Þ‘χÄ)%§tûih G¸x³Åç¿XqkpxD™oí(‹a²£ z™ßðÄgCóá’¦È`­%'@xw|N :*ÄÌÐ2 ùVÇ5³ƒüÉ@…h½±AvR ¾÷`0JólÛ­*«Ys(Á’40$9WËåóSN{Hr«ú-¸A‹À@K::Ûòàñ±bÊGoÁny@N¬|KÚý\¬ Ì‹y–»½ú4 týáe|EõµSZð‚žtüeõ°¡>4ª_cÔI}ê;Ò^a’‹©v4ÚÑŠâ¡©VüÂåÂÿ¸ ÉÉz˜›ß]–ýú~e~ȉ–I­âís£S™sÏïx°ˆÌ©©fý Ã]s˜U½>Ñ—ÆškÍdA}ã¡s;fe•¤í ðlö¤žq>?ïNšB0ø.óÖ&Eú=ÁÌÕ#ìÖÃ#²ä=Ë$¶Vf Ñu½E 2|ž§¡øhœñ_Š̆®þ7¥G¦¿r|zéÛž­f³8ä,6JDòìª-{èð¾!O¢¶O­´ƒ(Q”B„û–³»îš˜~X§DçåJ ¶ZF¦²Æg'¹Ôr¡¸¼ÌOÈô¨õõ» ÷‹hT2ÄT}ɆN9õ¹ß1f]$óK¹¸:ýµ¿¥L0Ó!î%K*QJ¯º*f Y,üÐJqí®8ÚÙX&¥Þ©‹¹rkêëô+9ÁØÅä¤mÆuKö¿nØ(Þ`zZf+éÈ•kÜJ‰÷˜‡a9™!g‘à˜Hôªe9½ˆ}¯Ðfwl#ůñúømd)J ¾HO­2>ÀK×ó…~§VÁ\La¿éiÀÝ­.\ÚyIΡ›£¢iMv_c#ï{WL~¦X+´ 5MÈÅ9˸·?‘÷ŠÜ‘kû›\@.ØSl2ÿ±„<ÇáC“eßS_P2›[Láì‰m}Ëó"75±¥&0WHf_¾ò§÷Ú£IòÞy†¨È6ÌÐOÂì>³Gc€xU•ÀŽ~DѸ½Ñ7šï¶SñöÃìœØÎH7·…inÓŽò\µeiÕU]‰ˆfb’;é-†›¹c­n• µ°WïØrÅÝ yj»ŸÀ0Ï›!æÞv±TTQ n—ò_Ê.Ï”Õf.îõgyÒ›¸C*D÷JèÿnÅØçzÎ8DÙÜœ1ÏÉaØS:ž(7­½W¨ϰîÈ㵊?ž0Ís±âé£RM’óöñnHV±1ý‚Öíú¢ÜåÊ·ûðÁ¾Ì¦°0«1§Quá %þƒMï©>é¶cv… =®…zБóª©5XëÌ<’°©kkcÐŒØÄ*t ®©Ÿmåkü^(ÓPj¥&+½$Úª5ù,u)ÈH²£‹CQ'Í¿“a‰]•§\Të¿”ÌØs’Ö¯$.KÑëÃÛÁñV}B†3Q0@ô²•‡=8!SRTÜ‹Q›•Ï%Ï}a½>iðMÒù$É-Cãï²½W¬®æy‘D6•Î|I:ÅÇédíÔR¦¶únS‘ÖÒÙä°jÑn¨6iQ4›üšþÆWQàÛ×;`㵘ú,-7¬ òÙ†ÏíítE¹Å;»Påñø¼˜î!h]+N©Kí&ù¶nñR.r‹¹§Ñ'¤‘}ŒL¤.*À/™ÆÊ¦à÷¿_EƒÃ´CÌú A¡“–“rÅÀÈ,#dxAË?Þå8ÌÓÜgïðYK@ž­ºgf ûÅgjáÕõžÍ¢xÎõÈ éˆÙoñ"¡8R\_<ŠSÇÕËÊ¿ê=yCb|Z’ûãH…xÿUß'î¤óJø«Sñ‰|&ç?åq¿7*~R0µÿÕ×P†3è:LÐ!þ ò6v‘ìboi³!íW[¬ÎêæÜT{KÆ[d‡ UØ…Z£OxãK·áb«¸sÄ1×CÌ™g{%z>)*åÄQíÑvÑŒÂ]m°1lÕonÙM†cižŠT‘T yÉÏæ «QËó<÷yÞøR¤|ç;žIã¸ãm9îÈÖSÕÖòo},iu± ‡¹ŸÀ[i”/fU±.ºÒT\H¦X[¦ì„îZ ÓEñãKSÖ––MýXx°µÖÚü~†™'sp$˜î`_ãù_äg<ߤ•¶³ŠYLõ4ûºœùÐÊÎZÇÀ¾ßeÍf¥éš-ß8˜ú¶ð‘â¾ë?qçgÂvX÷=’(Ë"ËíÍX?æBÀ¥Év˜ã€h^Úþ MN$®¸²Øí⸟_÷ÄP¹[BÝZL¶ h•J–ž~ã‚È«ý‘t½z¼ëþøD\AòÞ@ª+è@”T‡BQ¶Ì¬ ­Y}D|SÝÙ×M6ò‹P»w˺ø@Œï0Ííj8Õ³â'.øãeâ~%w»x$ˆªà‹Otâ6»tðÜ›ØäÀ¶ø<Å%7zŒ¤ñÌGÚ û‡[MøÄdh};~ {Ãn_[æ[‰®ñF@ºq÷DÞɽûðÈÉeb?;ƒjû‰ùÖóÓhõ°CM]ë‰ÔQX¾Cº!yj&‘‹eb‚}©Tó£¾ÅãЃ·'މrôôûC‚ ïtâȽ¾dŸtèÖ¿¨Ä:v‘$ßSàj¶Ý°wöòœŸsŸölúήŠu˜˜Ùñ6¯´O>ÈFeT<1rÛ$«ÄN;¦ŒˆÄd³8˜>œÖÈ=y‘¼¸!2N¾IÐOȲ¸7Þ©öY™:¹•\OÐØ²Úä¶å“x ôÖѻ갌Áy=µÑFûNTŸÓ×5µnF_,í÷!@ò•‰hú ×þ»Á¬ŽQ½œ(BÙuå²Ùé¶£êÔÛ#¡š–dõlíÏ ÑwvJÚ /d[™åò&î6\ƒ7û‡¼´½›äOé\UHŒf“$1-,È7KŽ)pX_?t‡¸ÌŒxè£>F䦘^„gŸc;MâG{ ¥vƒzÖ‰ ínÿŒÁÌCÃTÓâè9\ÍRš°J!—A ÿnûjm‹ï¦{‰ýV‹Å¤äH†ÿ6‡“Ø endstream endobj 1629 0 obj << /Type /FontDescriptor /FontName /NRKXJC+NimbusSanL-Regu /Flags 4 /FontBBox [-174 -285 1001 953] /Ascent 712 /CapHeight 712 /Descent -213 /ItalicAngle 0 /StemV 85 /XHeight 523 /CharSet (/A/B/C/D/E/F/G/H/I/L/M/N/O/P/R/S/T/U/V/W/b/c/d/e/f/i/l/m/o/p/r/s/t/u/y) /FontFile 1628 0 R >> endobj 1630 0 obj << /Length1 1626 /Length2 11933 /Length3 0 /Length 12773 /Filter /FlateDecode >> stream xÚ­teTÛ’-Np·`ã®ÁÝ‚»{°;wînÁ]ƒ»»[pwîú8çv÷íq_¿?ýîoŒo•ÌšU³Ö"'VP¦2¶5ŠÙڀ虘¸ræÖ†NŽJ¶Ör¶\2ô²@csÀ‡ žœ\Äh2·µ5¹ê@c€(ÐÀÂ`æââ‚'ˆØÚ¹9˜›šTªJêÔ´´tÿ´ü0tûOÏG¦£¹© €âãÇhekg ´}@ü¯•@È 01·Dä4%åÄTârªq  ÐÁÀ  àdhen17Ú8©&¶«F¶6ÆæµæÈð%ä08ÚÌ?Ò€®F@»¿\t; ƒµ¹£ãÇ?ÀÜ`ê``ú˜È`ncdådü»‰íß„ìl?"¬?|` ¶Ž G#s;࣪‚¨Ø?x‚Ì @Õv4ÿplM>"mœþjéoß̇d`nã]AÕ2ŒÍí¬ Ü>j€Ù9˜ÿMÃÉÑÜÆôŸ è@Sc+ £ãÌö_ÓùgŸ€ÿÖ½•ÛßÙ¶Gýs#ÐÊ„ž™å£¦裶©¹ <ã_»"icb `fú‡ÝØÉî?}Î@‡¿Dõ×ÎP00¶µ±rMàålA%Tÿ;•þ}"ÿ$þ·üo‘÷ÿOÜÕè¿]âÿßûü¯ÐbNVVrÖ ð7ðñÈØ>Þ€ ேÆÉúÿJ1°6·rû%ýk´:ðl…m­ŒÿÕ' 2ø‰é‡,L Lÿ0š;Š™»ÌAFf«yýmWµ1:X™Û?tý{¤zf&¦ñ©˜™YÚü%Û?\@ã¥ÿ!Õß䥄嵤¿Ñþëß KRq³ûàö­ÈÚÿ×á/aa[W€=3;'€þ++óÇÝû ÄÅÎäõ?”üˆùŸgYƒ¹+@û£o&æ¿»ÿïŸ'Ýùfcdkü×Ú(ƒ lŒ?6í¿ ¹œ>þûòtýŸç¿wtÁ/-ØñZ¤¤§‚ªq²ÆDµ{º˜!‚ìŠ~©äçúVÚvú¤üØä*Ó© b¨›à~kv›?¶{Ý•¢Ùê¶¢ìLžå|ñ"¥îÎE[£hå ÝógÔ+BJ=Qð8Ÿ“Ù€ÒbgRÛÛSTÒ+|!˜hýêðéüžÚ—Ô9דìÎÙÛ(¹6« µ ½:ïø„"þðþ޲oxp ¿óº{Ÿ6+ŽœÇ ‰"Ü×> _@KµÌ# '„¥ã» Ö±wã±³Ð,ÄVk’ÁAõ¡SåF4M•Tpð ©èÞH© <ú‘¯Gyr­TÔ32¥©þÍ-á :¯H›mäd£¤E¬ƒŸ@Ák¬“å7SåW”¸FÖÉ"ôq,HVáÄØ¹ÅøgCø„TAé€A4¥NFúNç°iìù3 p›-2TÜÛDѱ=~­St™»þ4Y//E±´WI“Ok¤Ž¿'Ãe¿hOcElЛ/µÛX'ž^Ë~á¥ünƒŒ„^•N|©¬¡.|”‚pÆ÷n7îŸÕ*¸Ôc>.”*3eÅBcYJ€% zýÄØjýòëõ=‹h(2! ¬PZƒœšËu1«+±&@©s•µ®##>>p‡ Ì÷Saaß®GC9f ô{Ñ‹£´/?éOôÈ¡´ªØj ŒÃÅT€xnÅ}½Ü/³`ãÂÓK#¥±¸ ƒãzÅ%?¸æ•˜æ qo¹–©>wÒn;ëŠÞh‡×Äme=¯Ý»x‡0=yuñÇuÍN@¼ŠKó*ßî=Ëô¼CiùˆBt‹­ áIC$¼Z†ÜÔ%%OãE2²HNÓ°Ë(pgñjóÚ‘HAª—Õ§)ƒTF¿œgElP×kÊ”——Á3Gï¦4˸kÃD †‹Ûši37w˜¦ØI¸—€uTüÖC¾¹àAãe]Êýñ ª>J²ž#é›Úï‰É·Ó},]ˆHQ=#ìfÚÒ×Û¨Ú¡OVƒ!Îd!I=7œ¿:‡ÀÑ4{Vì%+BYØáä’Ž…ÿ!÷†Šñe©(R¸tp»ÿŠÚK2ª·Ô`£@ý­£æÔ¬f°­‹”ä|Á¼‡º6bkj´ ìû£éT>'?µrØÆ„¬c–„—¤×ñâA »®ÿo¯Xâ´I5¥8Nqª½¨Ï1*ü¬u… }©ÄQ>“"æ2àxAŠßõ–wÆ%x~^SŠä„Ú…]ŒÚ¶ÔÒ ]±C½ŸnrqŠÓåïw~/#ø† û8K5£»Zè¶ó´'Æšç¾UPäÊ®£EFiüIkЩƒn’r[FœøÎÃhž3koƒ 4Ö0ügöw˜Y2Êf1$‡jèáˆf{xÕòf©¥Â¹?óÍ­ Ó“wÙìœì^žg‘ëûöZ¹Qýkïu„8Õðz²Áo—¬?Ó5 ´½wHpMÞ†¦¿÷v9 ggyÁìv+·¡D‡C2|§˜Y÷CŽó¾_Tž;â@Û»12Î|³È`ñBpñ¶ý½‚–ä2(LJ€Á6€jYK¢|F½ ©õÔ¦Œ\ GÛ÷r9s“G…š·2±ˆ~†7€lŽT Õ3£ûœfc6Qqæ¡à*Z¯'sœÉìæ”>ƒøp[‰‘0aÓN³a¨’F£YÀ¾ÐøM"LËË(~´ÛGЈê:àªuçWma©Ñ Å\NØg°Ìû«Îöúæ¹n(ŸÕºÍ±ÔrÀ*ƒ¢ßq–dÏÍÌЩïÒVÈã–ù­g¸–Úð{„¬\ϹJ†£!ÓWwy×ø×õ"þÎ5¬bŒ0œ?_,Eß\ÔüÙrâo}æ‰ôûÔ2yu£dhNÁ.Ô = Àìð¹¿]ÙO]ʼ»]¦úžBÍÞ‰žL€©™&þ’Û‚ð«øoSÏ mÿ:•ðÉC˜“5÷$ !0Z?ƨÏ{CM%k·èßLÚ°OqR2P•¶5!HY£4x™e)ûx?56’6·É5+‡1†ªƒ í|®O¢1°ã¹u‡°Š?\³ûÒð7µ–"n^LäÄåÉTö=Mu>ÊvnZ¢Ü‚',¾W@soaÏ5wžWs¯‡®Ô#U(ˆÞÃŒý*Ü®ü´ØJUë“ Øf@1ÃÃXétAë³0ñÌÞ#Êøê¤¯í%°}[HÕ_ÀÙ­a_¢õ*L ª3¹“QºÀ©ønÑj _/JÉÚ8Û":ð²sÂÛ ÉzÐÓ¿Í|²¨{?e]úÁLÏ8OÒ¶}ì&3©¥[ïû%É¥´Û8nÊ•?ÉÙ¦Z ĈÐðËœä3ˆU3Ù}K„–.’ÔõkéàáoÒVú£ª>ÏÙU÷;Øê¸ õçs±WãÝŒ#1G\T6º:˜w59o^ I«G­tOÂC³Õ¢lÕ R _T§˜j_CÑð« ‰¤>ûL½„*çpXÎXqÁêfu—„4é,±“÷ÏüÄc=îh¬JŸ˜`ÃGç¾Î%Þ׳;‡^^a˜¨w‘W™Èd½0ž–ÑŸL#Þ'eW7Œ—Š Âê‹4éšXóÕBÎ=pÙk“?“Ç®…ÿ®4™ÁUnˆ¡ÌLµ\ðn½±1éc½<«Xä õCþÅw#­q‹=ÆvÕ—Obø‡» *Ó”¯ŒëNo’5:ü(œø²»&mpÔW€.:øpD6ÕB-eª/(ã±ß¶:ÍDþ«E´brßÓÛ±‡NÉíÇúºN•Ï8¹B]T.ˆeTsõ´YþÖD*œúçS†½~ú î¥Òžýð+º|"jö)ÉŒFÉý¤‘Áµä¦‚ZH=JdM A×¢mš)ª’p ¬ ZBŒiz%®&L9µ<Å£õM3€€8TA&chn˜NÛ;(ísQåÙ„™CèôÓØ’)6cO_‰ôŸ«²sûùÌÁ¶±šÎ—þAÓV ÓA/äjqhü}ÖF[lÁ (U¶])ÓŸ~„ŸÞ»™V(CÒ®R¯ûh5±à,¿ÿjgÛ) ívC£î§88I¶ä ¹´h¿ò½·)*Áp;Ž}r7 CÿÞÉsû”;°PÒQMŸ²[»·±?C ‰0ð9Q†ÿèÕþ^Dàç‚/—»ë7TøÂYxá¡yˆŽm,Nd&ydØîKGÒX¦^Œ±CùV¨æÙ«sˆ9ù  Ãå(Î2>`]ÚŸ š×ÿëû|鸅8]–€gc]¶ˆ²F.Iôf#œUÿkÚy•ü˜Æ1—1s»|bûwë‚wÏ^qU® ¾6 –˜˜UM6ù–Œä¯m™›»`Ñö!qƒ“)n9œ\à”^mM+/ø‡q$¬‘®…ëˆÇßMvÛ˜Ú^“8̦Æ Â!ÌÛm@Þ­?fc;Z]ìþ£ßѹpü¦UËOÀÎ6÷æº_Š¡D ¤Æ®Ç¼ U-©ÎaÜÞ ª“ÊzWu/.6JPx;Éé ö'¼+ò^e¢*û=W´¦~¿cKæQBa=tŽœ÷[C9ÁˆÝ¯üüú9ö'T"0x›ZºxSñ…ü @NÔ´°0§ðL{MáóírÁ‚n°,ʬÇënº«òM>•/DaÈ#íp|ŽåN™«Tþã›|iš7ìFÛûµþEùî¸%Åȣà d¦Gv}bB4=Ñë-Úæ£ûvc¬ì”jD¦?NKåÌ/ò¨QWøµ,ÜW†œ~Þ±ê3¤,^uHKhû­?ªïhϘ‚ß߉6Ëó³³o¬?°ìÉGŒ˜(f´sÁƇ¨ÅÙ«lð®§ì¡²ôÚÙƒœ“‚Ú­€(®Ìrò"!ôOtÀÑùQÚ͆ 3WõywÖ‘öi¤`ÓN”=©(ÓV}ç?´'X;¨ÁŠd\ÏÉ.œŠÖpÇÙÄ­%!„ÜBiánîÉë¹ÌªôUáA¿$N¦–7Y«6 >ʯ:Íýsj¬ºü7;"ú2Ãõ=‡Jýè'KÚ¿‚ð»'ñíh@”sc ü?¤|?¯@Ý JoŽd°„S£öýþüöLXQ¼ÒVŸ-Ã¥ËúåOæigc%]0åE—ºÇóëjš‹è·Ñ 6éŠËÞïæœknP©”“%m9wØ2ʦ¼Sîx_z]Ð-„Ž“¸„ì :Éýå]ïØžÆê¹»²'×O=£`—®Ë¬FÆ/вuƒjHÏ®uðù_$ºûÔ?Ÿl¤%‰iós³€„PóuPˆdXÎÑ]GÆíg‹D«âSéW¦FÎ àØðIqÍ’Y㫜:¯ºòíF£ÃÂ'Á"‚ˆmcFý)$ÂùÃbË£Uö“¬ž”ÜßU¬Šá‚)}#²ˆån†;%ßî¬gC}Ç–æ-3ÖÆ÷~úô´ã¼¢V2‹ª¾Ë3Ñn™¸Lµ“f§NyŽåé]DÁÈ'B*j\Žöï[‹Áâj‹=* ]ú Ð,3¾äòž)Ž GOÊ(É<†frÛ[M[^ ”Æ›CD9­Èq ôwDmXvó0˜¬8ª<åØX`û|–§K¿Ožƒ¡w¹·Ÿz°BL™ókÍñ:ÞÆ-zÀ¬ù½püxnRµS0.ÂN^$€…xþ¥ŽÅ5ÁAœS^& ˜Îx³H'kÐ`i5Ç…OtOﯸ2ψÛk‹–GÇf¤/5©6 ÅCƒƒ–{m¡f ¶î5j}òŽcmg ¾ì3‘·_Œ0‰¿š¿,1ß`·æG’€ªx±ïHéû¤}󀃥CprðÝs¸iE”ƒ•øL9\c¯æ›½ê‰‰eAµä ½I̪³·@·èNèC¬³,&¦M©=(ÿ¸Né$R†P´ÈØ ×Ô€B÷j«ç÷0vœ+¬D²UF»-útÔ\yÐ*ˆîT½š@Îzõh˜ÀÑpÇ ÷¬‡|ÂwL«\{ÍxïÝ}»„­¹}¤!”åß@¼p©Qà¦a²ƒã‹ÈÞ†Ë: ž4gÒôm…e]yM3`-Ý?š(uhú}“Å2ô4øà{êA²Òîs*±%,jX#Úè¹ìGƒ[—ߊUùâ—³U€¥©8ôC~uÞW]¡sÖmò m÷r<óµGö-´Žù9pw­A¡ç>@Svë>`ó¶\‘}ø›Ð`¶I#™>"¨ÛØ­”9 Åz(¤æôqJvä[?_@+©r5- ™ü<,ªCÁÉ"° \l´IvÁ}¾#!ÝÄÔ{oTÖ#ÜXÔeŸ H82`¿Ô>Q”}ñ~ôGɨÌ:D­Œ—är80˜ÖRíð×vJÃcŸªâh…ôw?1á,,¢Qž†”vžÓ.ᔪBSD”lÔ†qi½©WËwG›™wO— šH¸’t~2_ƒ/ôÔÅf¦¯w„÷v¼pA»ìW[äŽUD¦bmûßÈÓOå#´ª¨Ã0+[£áÛ;·b6*¬gI!ÍöG° \ÇIÄ3DϬ°@h1Œ¸«£Ç ùãçåáÙ2v·þçú›ÛãÚ“ Ê^eóp¥ûp>Þâ‘}[Qˆ '¹Y 9·ž´MS&´i&5<›EÖ~’¿î’ ÞiÏy| L;¯³ùxˆ xôYkòÍãBû×|<²úc±›G8‰½] u·V´4ʯi´­Žhx˜…žŽ$wr•…‹ 㑹ÂþÏ@; ûZ1ýr™&Mv¡œwµW›æ 4C@ ³æÝÅrJ„í…¥×yà+Ŭ›°I1až¾E}BŒÎb£¯ ºòî Ê®n8ñs[G/5»ŠÞ„ǧ>ÙĨ37즻?žn¹¿D6wŽù{ƒx Ü1¯ac7)=Ii”®‚Še6³ýûž®ä_¦ ƒž¢:Ô„Bq9-¶Œ÷ýŽÃ„›ð“ÎZ¬.™&¯$äß‹r%ªr®«hrÑdÎaw½¯Ôš› e£¶Q–ëàÔ‰?pûtr°ïÜ” 3ãÀ•Ùžù4œe€ û²Ü?ÿC}÷S€ÀmZ1O,íMç–- å([“)‚E»Èߢ+è¼ôUèÍá6×NÔ¾LÝ÷ê Ê€Õv† D“ˆ¾£]}ø¤Pz»Çò—m`ú­ÌF7—'Óòx ¬ 8¿¸(q ©•òe«/úÊCIeÑ%Í2ØS¥ã/ÝYçæšú£½döÅ6+‰îd>S¬áb $†B^´Z:=Ûè)=‡Ý ¬2QÅ€âV/ún¤Æ÷'àeó©Ÿ(-‰É"9 ^®´@»bܬYKy.±;¦èššÝ©‹Jk×fÅèìôuÃk“ØhÖò(»›Wìlјs´±çú܈.³ n›ÔÒ‹X¡ïºÅlaY­NŽÎ`QjõPÂêαÍÛOQý4§ªdù zoW¦E8âÃÏ2ûÜÌÂ<{‰ËK¢“÷g¾†@JIîûÖ®d’'ÖÉÙÚë…á“bI¸Ü[þÑ(§0ød\qZëIÈVío™ÐK½j‚Æyh·8hã­,&®!ôÜÂILEgZ;L£­!¡®KÉ%šìI”ïÒ½ &œúô["x%Q=ÄÔ£qN×Ò>žœ¾Ùyn{ãî?ÓjTÝ6¦¾œ?(I<7nŒöwD…ˆ)¨ïisŒÕÑ!æÏ/Q8xjbGkºÄéKY£ç3Ï—5Éǘ¢^o¾ÏêÆíž¬jSL„ƒ«ÍYRº¶P¡E5ÅÊÞ_ ð;P´‚@ÞDŸÝ;¼ÞÍ#˜õ´P«ÉÇýxd½[LjG:É?ÎvC³ß85Ä“v`H""ÇÁÒMÈÇ•³â)Æã¢¨ä(¾ÔƒKÖ¯ó½¦…íø¼TûršÛ‡ÏSþö‡‹™Ò³i¦ÊˆÚùDS™¶þЬ+|Ðõ¾íØïOõ\5 .6=É‚âÖÚ Y-µB°©ÔÏn`û2|N|2›N´±Žº’;£dîhZ:œoáó„‹æMÛL{Ä‘9þÅ2:ˆ¹H`k«àó%•-ý&vã•æÜìÂôEµ)ÇìµYç5K‘z,Å!ìcˆL$¶ûŸ‚ÙG‹¨£»Šö¯ØµÔnƒG»uPkX‚n§õ–ÒÌø0‘„"4tƒ†ìñ¿SŠfóé‘`®C r¼›>hØö´–LmìbIÔDÓ€¬|SÈþ}!’üq[B>¯jÑŸÊ"!£,5YÍôüðLÝR’ÒïÆ°Mv øÎ˜¯ü¹þ²ªbBOË{K>jÔóÉ.ŸÝ†é^qí^ú0õù뻓$JΉ¿[™¥[¢Äûw”ÖUâ;žÁW`b†cÂJ¶Ý·ìs‡Y„b?œs—1q ÕýE±¾ëŠZŽ5Ôg%°^íŸ×’Öê`_ä"SÞz@Ë©Là¤uéê)̈¶‰³ ÿpÊÑèMk沯óºáR¨çífT›¥Ò9-s›yç™æ¬VÍä×i<ÎÈPyøú]uŒ%dê³÷à¼O›o‹€hŽ`¦õÓp‰æëd“×ólÃùÞ@xÞ,ìÏà‘ͨµ\Á½ºfUbÎäDЫQ›{óÒ¯y0>·ñBŒ¦: Vô®IÑ~ûÒèMÊôèéf´×VY&G=õ2ԒϹ%0ű¸Îq˜2‡²’-âÖ×k׫«ŒE#=ßGFÁê÷OÎbWRäO…Üx+Uïý?‹ö¸Å(ð”Æܯ‰;ôôÉûÿ¬wà$kŽAà ¡ª -Oªp†Ú­ ÃÀÇŒ DšŽ£`TÄä–Äâ)›ñO,Ì Ú4(øû±\DÞ³˜2ŒÆ[3ÜÚÎgWÑû~(Ö÷³–ÊåçŠñÒ°#EÎ,Lkóuft˜Ø»Õ”9]8üW| ç¢bùñëâüb$Š”_àÇ3ÜLñ‘,ü F¨r‰QQ¤wK¿\qsr~ìCQ,±•mI!ýWŸqô(¥È`†2’þŠy~<7{^²Ù!AŒUÉ^ÜÛŽásäætÅ4§¤ý÷ 9²U¢ÊF51Ã?LnŒaóÆâÎ~¿‰ÙæÍõð#Ež…ñÜV«±÷F(¤ºÜÜÃNl…»èß$¾v\hŠß I=!¾ÝG:­¬îÀaÚý(·•›i;ú9Õ¹áöNv ‚Oß8¼gŒIõÕ²)šÅ0KuAuì¤õÌNûÛô)pêxê¹í¿»ñcLÒ{WéÊ’oœÇÕ¹f@6µ~ñä1ÒoqŽ> (ü­Â‡ÖrIR5]Z2 ÖþÁê(4a ” ï ƒƒÁHwŽÞ¬Ôq‹ÔµŽ®ÕÕž¹Z.²ßXh¨5-E¹O¢¼/7H¡ðµ€Ûd‚%2&~µíé3˜«/'¯=N¦u ƒíY9}ÖBØËS{ÑÛ¶F=ia…ŒÒbK¸"Þ=®F-š púݸ£³6¢Ædb$ó¿—¯éÎrá,Þìaê g¹íÁ¢åŸ"™œ¦ó¯sÁ÷d0ÈóëØ ¤ònv+S¨øØ½ÄµÇÇ~‚I6¸éÒCé®Úù¿ý¾À-Ùúóó€—>:F¢ fû¥.ÔaÌè°ß[w¾Å£$6Ö§Z@|¾\ƒáú¤>R¯V¨1báFÕÐS­*_@¢vP°âg®^ *ʨ Éo{ —Dø$ Q—ýaoŠm§e[íóìs˜pá:—|j†Êø#míÚ/Ñu±5ÕJa!1fú³¡}ò€0³¥–!ÈÚ#ÑâŽsÁÌÈ †ð¾ó`ŒL(T“‘â¹YŸTŒîê©¿ìJæáŒpÐ…­ëÑËš^6¹êÀ{tj«üè-Hu”Þ¢ŽQ±PGe<œ8Z×v*ÉÎ2tº†ìçÐu¼z&ËèP”Ä”|Š$µƒ9ʼ/¯b¦j€¾/vl9×ÂS éŒWþχ&fƒ<ŽY#}SFïfÜ_íö߆yаY²ÑY{8¶Et!V›dW@b :¢ÌÒuÛ>[†M”à3ðmG*!™ÙÉû‚É¥e Й‰ š¬# Ñ`!œrW"Cø¯TëGùýóx<Û(Ucóàòy‹Õ–_, ²M­¬ûÚƒCâIñœeBn tÂØ˜4Þ„o²quê…\È7Àà6àšjZéÚ$xQìîDZbîòR=¦¶ ^º:M Õµª0h(›àb÷|Ü­ñÞ‚Y;¿‘-0ˆpkz/4³”©u&Cú#%ã:Þ±ô÷ë#ê ŸM5gô›Dn&t¢ Á@{Ô†F„׿û‡‚Oñ‹¨cøŽ`Û­g Ë*¾D‡2Òµ;c–·zÁÛkFl²X„ìvM$”lR´ÒcKâ””H¦7ëoV9¤º£ŽâPtÑ(çN?AÏ6UÈ™ñÖ/‹a,!oio®ä½UÞm;­ÕýD£V- ©ƒ$(wyÁ3ÃN÷¶“ÚC¡òQLÍ(^z»ÙxTS4(Þ LLÏ} äÈ™Î,6Ʀ“vOº7jù¬1ZeÀÓj¸ƒv„ˆSm~BªÊW(b£‹ø#ënFÐxØö§}!cLŒ¥kqˆ¿¢©Wh =Øf)üos䓚9ì‚êÉ/JX4ë²fð?¹n {qÔR&Yh‚iñWçÈ!ž ´}óõ«y¶U¢‚~^çI}V/4G§®ýùC'÷i>°´|ðµ5N.ŠnÑ0nrËeg«éâI¯#A5lÒÍÙ®”­å÷&|˜1 “Gäýçæ‚š»è^¾”Ç?%=à„@hTêÅR—e8yb‰“ûÚ›yà#¬NREFˆÐøIô7ªÿŠòbTÔó‰'Uàj[Mªîß’7,rHÙLf_ 2¯j â¶ÌûdÛ±óäÑ]öXôѽ£¶µr–`ý[ùåÔs) x¡“üZS ¬í¹•{²ÎZ„ìz¢¤íô¥}.ºú§%µ0$®y2Ò7º¢M¦8 ¸M®D00äW511‚í­­J±µ kü[Ÿ%Ô²1]~Þa…ƒ© ¸e² íU•ÊHïM¬eïöÍ‘Z¨}¬¸MǶk3ò· C0F»;Ûa¬ø~[;ÊnP#ÜpRv—¬ÎaLw°Â[w.˜[vRJDyæt\ž{TíCÕj1Ý7ìšP˜ Ømz>/Œ•’ˆº‰UÏgˆ9ˆÝ>¤m ,ãj½¿4-×§çõtì´Ð÷Ð5Œâ*ÐË&]óÒôÕŒÔͤBÍP”1šlÁ›¬Dú¿¢…§ÞCI†øá¥\]_´Ö ’ú–Ç/½Â(ù ; —yG3 •Ícj|zì@Ï´P xÓ–kCÍ Xòï±J‰‡Ø•á{­q‹…_¸OŽ)ßž†‹eò=¯w[NÙ××ù‚|tSÔ!íWk†twÌiª`Ïˤš·>í-ú=)^™µÅž®]wý4ïÊu†7VQð¼ŒÍ³·Žÿ%Áy•³v]6Ìæe~Ó\™¨`M;”¿¤gs͆y$1Q |¬ Ç·Å{ní.^$»»÷îUä]=K#Tñ‹ó›Øª¸¥ÿðĪoÂêÔIž»“v ^GÇUâøUäd®½àÇYýädia° „=ßÔ{R¿P¹’PMÒˆÍ5_MOžŽÝ¾š2\~9aÌf€€u“Ùpf[J¤ÌÝ¢rìÈ®“ñ)¦èJóägÛ•ÍzÓP-Ô?õ…?¸ŽÑ Û~#Æhqz¤„•„l0 ÂÊ¿:©âšEÖÛ*⃋­À¨Þôƒ¬dCLèœh{;œy‰²Ã—‡[Ìò{´HR¸·¿Nª> ;ûGl‹£ÍÚ…Háɺ‚ |8VVãr¤oU¯åBÉìÆÃøH_¯yœÈîO®‚%&Αզ§«Tª(ºÉËú8_Uàbû^|%°<Òµsìtì²­'Џ"µ~q|Ñ1’¾–°ˆƒ*#d ü#“H±UkùJJ¹ó{³Ü®½ç«ÕöÝHëÐW! `p ׂ‡C‘a'¹!Ø üP{ÜÅHkì+tÉoùý٬߮Ÿ\Öâë"+RôäºÑëÂ=7¨ü.¨¡áâÞ7¹ÜÓ+þÒ1„Õ_̱†,x¢µœnÙ'+uÈ‹ýµ;ÐâÔI~E dE ‚\‡èíbaqu&ºY8‡ <¹Ê ‡<;pÝðxqœ¶×ˆ÷Ë·þÎAÁÅ#ßÎ$bÔå9¨ø´4Wý€,Uù ½ÍÒ:ìŠfpå·ðùÔ¶]Ó\zw´³ûïÈX(†ËbÔ´nŸ—Ûå÷b¼ @\fgEòÀQ䤧þ©ÃbvÆ#@zÁFª‰GG³'œ=fïʉ_ç§«å.°nOØXOš®—#î—¯{·áñŸÛrù³Fõ` ®dXùp‡È%‰¿¢}ž>år´î8Ñðt¿íî‘”h°ÎË7 }W+ÊqÛ>#H)*LF«ÄSöú©Eùþ+ò÷h©V….Ié|¨ëû”…ñ Ý-‰&sγ¾örq>aË@K¶[V¼Ì*†Êõé²iwd¡ý†q\D«re¹Ñmž Y,mrÖaŸá _*ÍŠ¨_l¡ ›Cþ¨G|/¢‡ÉÑS’ʼ°J¨Âp©_íKöP&˜lSµQʸ_,ù^ùœã¢¶ÓEÜ0»«æ\\åw@·¢Èª[I«Z´ô j[Îoh .Ux˜—Ž ÌÆzá!8ØÖâÃ5Q°Õ!oà' äÔè+kì)–®Ôûu‚¨b›Qz5"ÓTÜŠ¾WÝÊo³èеyއ¹Þ´8ºÉd¬dªØ-üɰæ¾1ÌkøG=2Er÷r¤©¨unrýŒ•òÏóþûò×+øÄ–ÉÛé×4_À_XÛFÚœ¾¸• ñPOQê¡>ª^À\ÑgahÑ’,L è¿ØìGr¢ñz¡óÜd®m¿kÔèâÇwö;ìž#]6xDò7°Š1C¢;~»ÖÚ<¦êÏP_”ø˜¾ÙNÒÄãgËés­Ü©xë'c€ùS};„óÇÚo&׈ð‚áèå6L:ílÞs"»Î­Ë‰ÞÓ–²Yg±„MiY E%=-lx0CÖ¯ˆi›ÁÊMnCË <1þÒú`ù=†L$¸µ±lÉ|9ÖÀìªøa¨ ìT3>`Ä5ZþCc°ñ.“ôw¤3>ý?•"c†ëà&ÂIúàû~«È‰ úóAxÖÐ:‚–áq=g13äÑ ªR Û‘B3é±øiê;È'8ÁÕºùçÞpn¾§”’r |"¬Xäƒ ÕV'XרÉE©þT ,$3O'Þ¹Ö†óWE^Ó¼·H«ñ{ÐKŒx¸ëÉiûÃ÷â^¿a˜[ÆóŠ®Þ^…PÏ3C¡ Ö›ö-ë’}¼-ƒ“O®pZ8Џ÷ôÑ}óœi‹Ã¨†z‘9z•j¹™ý ¢“ã®:©+dÖM 鳄ÚU‰Ecʨ¡Îidç«ÊNíNǧ+Ki©§±Gâ´^BI,›ø FSâ“¥ý CD¿Iû/X‚Ê߸»`À*šçÖ~ß0IYÂG$ô¤¶»P_”>T~-˜ ©—=°6kZöì Ñ‘'ŽTc›zºXoìk&M…Ì…†®æ¥ªÿ9EˆÛÓîWô=yŠ}^ÓG6c?±çWs:3¹DðÜäšhj‹^þ5Ò&Ñ Kªùd ×aÜ dÚ–/~ÚsÁû—eñ8´ømS#Ã]ަoæËò,‘tm¢’Nʹyñ´ ˆCÃv7¡B-¦jψAˆisš÷¤tG¨td«‚ÌæÓã¨Ý|xntC£ Ì’{ŠÒ±éËð3½.&8X:(nÜQnpžÛN|q޾ èˆP?Ì y¾êRŒm9Ùï|9¯ Çš’Ê Gÿä2¾`¾)¹tGQd²!­8­X3 .8–“Q•h-€d2ß\€"·­¤ÀËx¤tÄ a´&T(üu&ÉôÁº† ý‚½>…žOk¹i&Nªcÿ³Ççªô?&ÌË®y#Ï-„'NÁBÜÞõ9g¡UÖ_;°”µ°Ù£ðH:œMáJ[©4úÊÃ~…äܾ´ºq6—؈û6H‘ƒ(ocØc619,hîí»MÑ5LpÌ…–G˜ŒâPþ\h‘• endstream endobj 1631 0 obj << /Type /FontDescriptor /FontName /JBOZKE+NimbusRomNo9L-Medi /Flags 4 /FontBBox [-168 -341 1000 960] /Ascent 690 /CapHeight 690 /Descent -209 /ItalicAngle 0 /StemV 140 /XHeight 461 /CharSet (/A/D/E/I/L/M/N/P/R/a/b/c/colon/d/e/eight/f/fi/g/h/i/k/l/m/n/nine/o/one/p/q/r/s/seven/t/three/two/u/underscore/v/w/x/y) /FontFile 1630 0 R >> endobj 1632 0 obj << /Length1 1630 /Length2 18879 /Length3 0 /Length 19719 /Filter /FlateDecode >> stream xÚ¬ºst¤o·&Ûv*¶;éØv:6+¶Ž“ŽmÛìØv:¶m[_ÿÞwΜYç›ùgæüQk=÷Ƶqíg¯{U‰¢2ƒ‰PÜÎÖ™…‘™ oacäâôÃÎFÞŽK–áÐÌðWÎGA!â4t¶°³5trÔ&Q 1€•ÀÂÅÅG±³÷p´03wP«þP§¡££ÿOÉ?&#ÿÐüõt²0³Pþ}pZÛÙÛmÿBü_;*gs ÀÔÂQPÔ”’—PKÈ«$€¶@GCk€¢‹‘µ…1@ÖÂh뤘Ú9¬ÿ}ÛÙšXüSšã_,!'€!ÀÉhlñ× èn ´ÿGE°:ÚX89ý}X8Ì mÿöÀÙ`aklíbòOå¦vÿJÈÞÑÍ_Ý_0E;'g'cG {gÀߍТâÿÎÓÙÜÐùŸØNÕ;Ó¿–&vÆ.ÿ”ô/Ý_˜¿ZgC ['€3ÐÝùŸXF@€‰…“½µ¡ÇߨÁì-þ•†‹“…­Ùf@pš:šXœþÂüÅþ§;ÿY'à©ÞÐÞÞÚã_Þvÿ²úŸ9X8;­MáXXÿÆ4vþÛÌÂŽéŸY‘²5µ°0ÿ[nâbÿ:W ã¿DýÏÌÐüMÂÐÄÎÖÚ`4…c’·sþ@ýÇ2ãÉÿ ÿ·üßBïÿ¹ÿ•£ÿå%þ}Ÿÿ+´¸‹µµ¼¡Íßø÷Žü]2†¶€¿{ øgÑX:þÿ| m,¬=þO^ÿÕZøïtÿ`RΆÛ"dkö—fFæ -œÄ-Ü&ŠÎÆæSCë¿=û—\ÕÖèhma üËí¿Ú ``afþ/:s c+ÛH`ÿ· hkò_+øK׿ògÒR×¥ûß,Ø*þgû¿¹ýjäìLþçáaa;w€ '€•“åïû÷7!.Öo>ÿ›ÿbùϳœ¡³£…;@ûoÝÌ,ÿªþ|þó¤û_`ÄlíLþegC[“¿Óö?ÿ¨]ÿ’ü¯ð·êÿ8ÿkî@w 1ÜÊ¢1OejFšs-vÎЄ¨v_ øP°}IƒJaþÏj»n¿Ô°m® ƒ÷š`ÆÆ)îÏV…Sû}iÚƒ‘,kªîdàeMo>êeûwºƒ&½Ä´3õH¯«yÙ--fµƒ ¥zÅïP„SílŽ0WO4?É\ób?Ú#ù§ÔÇ`v 4‚ ÕœžQ&?=R Œ vß@öîãÓeÇÀRð¸B RFütÈÆÐR­ðŠÆ¤<ˆŠ—ºÝï7Ô|>Q&7ß³„ˆ»ãÕ6?+} Œ¦oõˆ3Íž*®Ûâþ`V|Ä”,2¬Šb¤"¹z.²g‡sÖŽ·K€èIQî[Š©KZ·9h$w¡4 òÍqÚ2…r¾ÖZæÆ²)ë ¿e:픡&#Ûýî`FcîC;X̼„gCÍsú…êì¹kŒbµ|¢OQ¯-3TSËãQѪ»ª!T˜¨JH‹ÁÖ#bäû"ŽÈªðIš ¯`SWÔ #øëÛyt¥°©šŸŸ‚<Ä÷d6Öü™HÄñ^þ²q±ëRîE­k¡Ù/(Õ‹´þÍe”óNÖÁY_‘ùv³ÀÔtÏwÇ!I4—¿Àâ5"г3¿ú§ÙÞ÷ŠÄ7±ä%…á‚jù¸öéʦ?î(âõ¨Ó<¥a=}Ø{óX¢\näÜœH“òÜÓÀÂP)¿Z™b‰ã$´s|ÓB='þòœTÆLýrݤDEµÝT÷žIö¨5ãîhÑ.æ=ýe)ïË3Kÿ©R„ËÌㆠ$’-‚O² ?¬–¢NXÖL[5³™„è‘Î)³ç…¬[ø¥‡(œQs¢¤Ïµe»i6³/g˜*þ£èha9ú%&«-!YY–+S%D¯"9{qx@F•¿ÓyAÏÃÞYN"þëmýðMØÙAlgÌ^]Ù>»n¬ i­¤i˜x$]÷d0¼û âçôÖ·ÀY½ÕCïwIPížhOÉÅÀʵ¸m}ñL„pÜ%n]載:§=$Ws¸qX°B8~Ô8ÃÔN‹›|Û06dE DMì“lOÐ*!~òò“è }”Ãj9‹”¦ãöG‚D¨¬‹àqºÙ”ãÊwÛiRÿª +oëßAÁ€6Œª¹~е84Y¸iÒ0Þ·kÙÄ=–4¢X—YÚß—HAß@°‡LJþ¨J­1>8Þ$ÔõÒ‰³÷5h8Û‹ojè‹P8E`æ_ćV׆¥,¼*ôÃù·…䣾~¡!A%ª™R!î¿ì-gÙäa\ënvïgNô[Išœ2X»æ<ê÷‡ÒfÆß𮚠[°î/BgÔŽ1âQ{]„ËÂÜiŽ‹:QÀ&ƒ2둎qFÌÄêv"oJ®ùºáoÈõW•a×sµßJºÁqŠgqIžÅ¹f3ù"ö¯׋!·rÖÇC¼'U 4úFr<ŸVØBo@Æ]Ÿ|Sj öŇt¯µ÷èC$–ª“˜âËóâeáÞMHÕâ‹U3ì­‘Ïýž{øÎkŸáÔÞ””£@ÏÉ*Øh(? f…š ì·Z¹AÔíÜæWY2 bÞ Êï‘~ Nª€:;y€oš¹˜ò7tmÁÍá-ƒ°;ªB¡ráÜfòšRƒìk?2ÁßCk!Eaá¡zî }×íàtÇß}‰Œt*à+‡KX+¥¨ÝŸ“È•§è¥Zfˆ“g$«¤3ábC÷—T¯yjÍ;8" ¤ñàNHà«~ü„æã‰²Y×Zåk­5.” v…Á&3SËìc—órÔa¤ãœEòﱇÿj›Ív*UtPý…ì /T¡š@IF†Eœ&ËR$·ž8Zzl¤ªÞÛKÖ³ÖwlU\>‡~bÙnôŽžû±w¸žp@û”P˜àýž>Af½ÍWöæXqk/—`F†)V³hŠ…ybómQ³Ã[EF²*K–ÐâçCõÚgæ®Ç†%DWog?È{;T«UÆøx3F¯B µ#¡dU’gÙÃ!#ÃØÄ¡ý1ÝýpÛt·ÎÆoŸ~¯CëcŒ‡O3?‘ÑZùçãv#À0ኇÝn/Ì@NnÒ9¸È¥®[öEÌÒ§û=ÓéÉØÌ=3`ßT­PýCò‡T{³~ o*£îW”¹µ,ç®à‡/˜ñà}Pj–ò™ôjóÚPY„²U8GÀVÞŠ¼–¬D_œÿq“†À‚ûêlƒÉ|r ÙŠ; Êk‹ê~gq þºUïÐmðK§¡4Èf:ïl¹ëäf™s< 65.]%–£€eÂm„êžÜ,2™á9h‰¹)ã3êä´ü‰¾¢®~DÇãIFñ3‚Vàx®xêÎBëµãdŽ"Nè–§kéV}t]sØ;¡6‹ÿ‡ãL=+TZ¼Ûú=¬FE ¹ýÛJàCX¥qì­Èø:A©*ë€Yˆ¨šbcCƒ\³Y£-VÕÚwXË:¦ XÉ_«aÚ‹#03NzÅÊ# =zÉòStôÕú³{oI|*T„hí˜âßlG¯iß—S0mSuäÏÊÔŸ¡–œS½óܵw²FB÷[ÇBZÅõÊXÙ£q)X%¬—Osˆ`ŸkßÐð›ö¾Þê!†2¡°ùuS×Ί¢‚Ç=Ä~Æ‘Q4üœÝêgƒ“‘M `ksß"úfŽUÊ®qqoá >ºõÒ_Ä |¾·»0;]écè-±X¢Á­."Ï^¿¤+kqP«è36o$µ®RÔR%ÝÝÂ÷zKßk(<|ž$Cin(ƒ¤C)Ü¥Ÿ™ i)êË/ È(qH© Í ‰j­h¿é´”=» CbÉ’ŸN7ÓB[o˜:]½p³y†‹â;cÿÍX#¿‰€`R¨' r\ÜÊ-¼.‘d÷Lçè€íþ‘*݉ŠObNhˆ'T^Œì$6úúºdy²Û3+Ù¸¿O¤4j&÷Om"jáu¸éƒI¥ |&:>áOf{M‡&°È„E¦Þ‰6^[5I©5'}5ê¢ýè¤gDèE÷~ÔoIÐH, wð›­In‘ÃÕ%ÿ>µN£ó4÷mŽœ_£’§U:§dXòFÏwF>Áå‰6¼3=b6m  …'Ù.ܸ®]xJy°ÉâDùúó'¥O|8Zü-N ä•ENï¬Áã:ßåN z2Ø.GBù TD#ÞPc¡vxN³ù MdúV@óEÍvIžä‘M“W®åìwÍoæ”,½'œ£kgm~xº¢gS§<Ϭ¿b.Räë;¿pUÓ¥KÎÒ@Š0ÄtÆoúkÆOðóüñºÔT°z¡,Î «œÞiÕ)“‚­7L÷ÐyÏÍeÊ8b”Vrõ°œ?€ÔΈ%µaðácdzÝy þéŸÿŽú°2"a…­Æ îPª-¨YøEìXQjš\bäüSø}Ød4qp Ëå­Ný]u§0³SMûM¶ý0³N ßÄs ¹(["|wË ¶Ó¿œUa¬ÊŒó]–Ñ“[´%ä,êY+';©Â 3ælæ†]‡gq-ÆVW:—ˆ†w–5âØýŠ8_ºªî®Èƒ»Çù#n½gI`™(4ã6_¥1lµz7õêE4ØOoRUÖž¿ Èù°’Ú oJúšBVÛßžU™”îpÃPàw¼Q ]ÒË™´ˆ€”ÝØ®9šMõê4¼Ð,²2q¸•®`Ucû´ ;úiÚŽ‰Õó« Xšè@$1âðïÐõS÷Ó†’χÛú·â„2„+’‡Ö:å¸\ßÌ*®B:}J0) ït¨î¯âÿ’qàÃJ¦ÆgW2 ·eQå*É2G×g7ý˜ÁǾïÕŸÕ½ÏSQØ aÂhco·Md¹¦ºù¬4o„p~,lmr,uºRB/rk“Ñþ~7‰ˆÞŸÂs¨\Š|òû¨ÎÑ=†~¾a®³àé43LÀ˜ | ƪÂRª[¶ Šü~pÈr3[NÐmã”B‘½8=‚ØOàÆ’/Š>Zd™¢“}Þ‰ñ3ƒ³=TšúaJ9/ܶâïź$üt+$!=CØ‘ÆXè£B>†¹žx\S``¹\s+¹Oí½kMÄ”‘ËRbÙÐ}+ˆˆ e‡3Ílinsæä‚’_lã|&8pVâIãPøUF[f·BËÈHÌ ÂóA'!‹e!’ ‡Š©!–ØÂ†)Lã/òŠÜª*þa¥ÐÇ ±H%þMsXÍ j.i3ÝÔ›G/ýí§È€¥‰vZ}]‘B/moTO;°(&ºÖ- 5„ã$C8?r³y@Úä›û@Éz9;ãú©?Nƒ¦ý‡)¶ªªnE_O[X«tùZ#w3×ãÌDù½ Ý3+„ck®'î…†„E¸›æCÈ©ùòÒ9ü­=Þ„?$>¼ôT×UÛ¥»KÞã²ü>ˆ||ò ¤Öô‚0~Ë®4¦m®ô?ýA§º Îh”^x–¥›„j]Pƒ[qËÄ“|Ù˜KV-ÃÂj»·ºF˜«eãõÿ}@¹—È.öy6Õ Œ—½H‡<$åÁh¸ØÛÖ»GNàíCLI€£ÂÆKÚÒ4ß纗Ÿôz–†ƒRæ{ìþ'¨1B— ‚kr6é‰R§sÐÇX–(„áE^¡ÿŒRém O9`UÂ92ݨ WZäøÇÀÞ*s /î÷S½¼5â*‡¡,ž>Hua.* kÒ ^‡¦ß43º”,õa¡«ŒD¯6©ž:e ¾Gàbõ3WnáÁM9® Óˆ&߉}ýHàÛ\´«(˜if«@É7ñxOåÄøK1ž']¢â,¸ŸáD½Pèи´Üû° Ÿ÷°•­W·Ò:~“›W:xWM S¦$܆Æ-²xëuÝ{è Ë7îj·–š«PåÑ  Œ†=it¦«ökÈ:Âl-jÛ?"Fá`£»Ï#9”QGTSç—[½=ö1“"½‡På$»LLÒú "•Ç>ãÇBZU £,ÒÅU©‰¥«g’èw§¾/òÝWS2=z¢rùÿÌ}uÃlÏõŠÏ=8Ÿä^…9¾ Ýié‡âxs½j‘"kTOФ”Œ Â\9£Ñ®.ã=âêÐOz`$i¤éÅž˜X–dF(ÎeЏÕ+õÜÇ[kQ?(ú™È[Æ: ©¾×v‘s&4ò£gD¶Ü--ÖXÉ|bo ¨Ë×96š±Nè -Nçû=ÔðwÄê€àð‰2´!“¸IäWаÓL05]Í%¹'ÙȦÝjYìg¥~ϳ1\xIoñüW¸›pƒ˜8çô®×Wt‰<±C;ë¹ÄS¼âï±ç:v‰‚ Õ¾·¯¨ªº‹ï®/FAìEæ±9S†ë¦©ß{ù(²Ð—óž4ykç6cx™p]­~¿*ÕÜÈÍŸ]UI7ÉJ+é 2:õ¡ðëò,xψ¨ê‘u}ŸxqŒ`RÄŠ¸Æk5þÔ9^Õ><¹ƒÖa`w.-ìõ,Ø2‚ ]X½Š%ÑIÌ¥†+逽ƒò´oãkd)À0¥žsè›ÑÈkŽÌÎ{y±1½~‡]ÿœŠ3‘²Íºò™ð8¬Ÿì^ÔëƒžÆ ‡™&v»ÊtÁ6ÒwÎÞÍ¿4PÅ8'h²$UÑ4ÚÈ_ejósqÔ,š£­â¶<g$ˆkêžÅÑxÿÖèûú&š#EèA¬®Íè3‡àŽ^aPåz8Qtx âU†B2­Œ Ã>bã_CÕ9¾Ú¡®áq‹©Ù˜ÝI˜ˆÝz£8ô³k¶T¡g­¸rœ6õþòlÿ¢Eû¨þíÝŠ¤ï.V–Ï!³"¿±&6¥áÎ#2Ùgõ}›êöŠ>3všÝÎ Àr; 4"`„‡DÞlù1W|ã[[¥ž¨}‚c¢!PÚ̦žu\~ 3’“SšøÀús8RØ7?íD µ²(Š’Y|¤üC„Ž—ÕdZ¯éMçc²°UÁ¸ ºòÔg‡{Kœ‹Ò7Ÿ^Dðö(!÷" ƒ'ó÷G¸â•{û·K{Œ‚þtÙ_¶éüKüN²ÜE¶ T^61"‰` ö«÷ÔÏ€’Š\¥ãëá ít˜cËߦ§ïäs3 K€EÜ]gåËbÓùÇ’Êß²¶¸yuU¸,ßquO[3D­Uc€Òm¥e™ÏÓ$‘VOÞo#KúÞØnAfpL$±MpnBðŽB(õ†6.n$¢Ö¤G±—B‡´t“‡Hk&×›\®ý!ÙÜ8äÎ)®œ"¸‘ÀQ¸µ^éëÙ¼R, ×dJµsul|¯¤ÚÑÇ|¡A¿+yÀ®L™&BÏ“O ªY…èëè ¯4”­ÓÉ _V#y»øµlN†P·ë-(}H×Ñ-•=þ<"[rlîß¡KÏ"'žV¯b{Ä^žþµÛá'ÔVy_roë™õáJÕ<ëéFµ«÷fœ=qDb ¨¾Åw+$îmÓP*¢SÑp,Çͳþ!¡ƒ|!ozMhª¶÷¨ö†¤X„ yÕ>óëO­7qˆ4þÉðdÚ’“ZRpÉî •t ÆßÎå¶múb ¥1½˜aÚðÞ!ÿl¦@3Øë ž<ÒúŠîA×ÃÓUð™4"øí‘­Ëâ1ƒ,Wž¶†¿k²öE$ªÀ!Š÷±ZsÁcy»¥PXu¯½% ¾QP¬}[]çÅÍË¢H†œO¼(}ün+Âå”VµÇÕôd¥÷Dý!x5y_qûüÔ{âÚJà„qÙ*»- ö("$×Qøi~ØFUi2ºS$0à.èqg2 šWZ0Cœžt4 “tZ™ÿÕj×ÓÇ+›µm…H8Õ00bR¹<ôl™»»$âÕœš÷'.„$@óü%×X8$&…õ7˜Â\PÏ“Yj°–+f:´•þ* B³iªb»'Âûùq¹ù§ýXªk{kn+mQI$ØY®_[ä7;f/^-/jÔ Qx‰$dH“'À õ!“KP:úÎÜEê›\¹øQHq¹ìN(œ¯ ×DôŸ^ÐÅ|mn¯é>o‚;jžv£ëˆW4(ïºÑü‡MJ"ÍowŸØº\ã¸L÷ã!n¿ZoŠ‚í”ãí]…œÿ^Ù­_J¥:—“Ë„í‰Ü¥kO-ðä}‚B»7“C_,tŽ?ìÎ'îŸ*õ³;ræaköÞÞ¶Ÿ³²á½¦çþݱqðRoe¼ð…\ìvœ|ëT-…îtiä~N;ÏÚc7m× ë¨ÞG·¹Ýµ[ìúöL;NCï®#£bvßÖU|æ”ÒL,ð8®dX¿XÆXb¶ærDñ»‚²×•kÏTñ X$†É£¸¼~ÖƒœÆ‡Ú’E„ÑÜ…Óv[v"…°Ð 2a5é8C¹¨!_s"¹?ä¨SŒqë£5)¿¨”Z$ŽNáâï&Ø1ùÄ©T`s'ëŒ_#ãcgßñmIŸIÝyu§Þ©o =A ì¡íû’j}~“_…&7f—ê_¹‘»õoÇr¡>ËüyÎrsih«ôƒ[¥51ÏÞV»ìŸ„­<@E"äè{‘â‹ÔDDÐú£CÅq CÐ,2»Y &èPëUAìû2Ë?\ËksŠJ£Â·[%båò!Dœ¸C»6zD A¥úëQºŠ£é[NUÁïLä‹ÎÅ)xÑ  y\þ(¤ÏÒQqñ\ áÒ*”âí1ùR—ÂDô3 Káç­ Z²b5M”ÄcM²ß¡?¨%”µ* fÌ•¥‚T9V!Y¦ *æè·±ë…T;sVýÖ_ €ÂÓ6†6ÃD^ ñnÝr;Gܽöì‰Nkðf…ƒZ6k*ØÕÿaÛß“DœùÚî k-.Q¤P8Ulpb,Û’íY¥†µZJ³Ž'kÚú•'LßÇKÐ¥IBýa¢NõGÔÿyÛ¾¾gÄ"Å °zåCœ Üût„îX÷¤Þÿ}Ü̱ 5iÍûH»>r²}$г*ÄÛâ2\úÐI™í²mQvÜ5ßû9eˆd“…ëXÓL¢âÇå >''ÅÔú>g·N-rÑI…Îl‹ƒžñZ=µ·›G[Ü´nZ_c#ºµzbÉ"¢IÏt[„{P™‡lìÎZö°6ÑÂScE­%[ L9^€Ý~q«'f -é‘cÛÔ0M·\-C»›Öh'ËúîzØ;‡«¶ÞC¨x¥ºVày,̰"äÙCl1Œ¨•qÝ` ðoîÎÉTbL2KÁ„»hÔÆ[Q Ö¤KV–¥(S;Ëö‹ÚZ0_‘ª•‹»§–ù”eØ/ù.yQÊĬUÎ?m•â<Ìú´%½Ñ'ñÿqÒ‰h¯!0_xÎ 2XJÛ€êM»…ÏBWv–¯èð‘%—­Ùi¸ÿnê'½+«/|~ ѹ,ŸÚ]Ù+\'Z-%û}Ó–-érw¹Ù*%«lMn¾¬ €]Ÿ@Y\0ëh·œfȺÿ6Áã‚3 "_™tÆâFk7à¿¿£Õ»Õë÷=’É‘«–k‡ k3KÀêáK« ¡Y‹×™¼X­~ó¹• ½ý­„zi”JTnOß)Å‹ê%ïºDí3¢—.ç t÷±ÞëÉúƒ0+fE¿ô@ÚDHÈRÑ?+(èͲæxßEš¤ØM„é‰-ù,OŒ6]?Ëp™ß{bD>j-NDš0‰Ê‘§"½²ʈèîóáѽl"zB–‡¶cCgv¢?`´>86ëçpZO†×{¬D»µ %¬ªßß°˜n³ Øh¶‚šä‡'–á¸e˜Ócp¥÷Á@¹)iæÔlÉãNC:1¡v,˘k2ÂW~ôeê$–»´žääRŸ/÷é*’z?úücÇêº\L›0ºlÓÛ{gÄNÅSd}¯ChªUl•—⑈¢¶í£l¯¹Zï\Ñ)[sºMjZc:7ÎV•ü&=se;ÇdU{={µÓyjüŽhÎÎ\‚ôù…M›5…–OØTÑvƒàù>°¤2 W6Õ¤ÔÉ4ÌsúË;lVÐ?î¿¶iMÇ*¾(¬ ì|U3Tjбഢvs®¦ÁãtäFÔ%Œò†ïnŸùjÎöìÀQåv¢{»Ø2›WØ êX yZ€7e"±Ñ 'í7,ØáÈP|59-iVšVöK[W|%w ÷3üÆKÌ;Î3lÃEöi ÁR “Àðmc^‹ª’9Ïë‰ñÁ¼–`P»@@/3&TSÒŠÝ4,óbòf­DŽ|((Œ7Å;”O$Nø™ëÜL²/cÔ¦—.ø…€Òn–Cv#Êõêä¶n_:÷9hØ>Ÿ³S‰Ý`à ]•àÍYa–|B¢LØçðˆWH¥®ë.´¹A÷Ýbé†êI¼l*êÒë;a-ìa £úŸ´¼ Áó<ÓÏ•2ÓŠ'µ|ø%PµØC@F'ºp ̧àÍi!{¢:É(aÓÝcéªÁÛÏÈ}?æùÐl¢ïÏJ]£¹‰À¼²™ƒp9&}ª/bŽ"uà'`É£|8ñ{›÷Ú‘ý*K{al×qg4ÌïÀ®ý†rmí-=> {põ¨ÂŒ*]5ÑGaé}žÏ ¤Oò «ƒ¿Á¬¯êÙnübô¢Þ›öΆO_ÉÏ’.£ÒÑ<=¬òŒ‡G¢o?(qË„ÄK9^ºÞXß•Cý*T9Ë\·=åº>ÀX]û¡l\ß‘ŒÿÓÀ¸™=ÚüHWäº]ÁÞwâH€I{öÛ Ýž#2ÉD½#øÅ‹ß@y‡ñ ¼Æ)d9’ó£kJ®k…Ìæ º03n¶ZXÉ#åËw1ñÍöÁ@z£|ƒekwÐFü›Ðf Jߪ^й/_ß%5E0O7zÍMñh¾ùPw0Ɔ1ÚTKfÈv™ŽíÝ&ÐF“*ešß>.ö£0†wçoª¡%É]‹î_;þ­R-ê U”gt’ÉtsœZ"êà€[å¢Ò¹HOÏü£¨{e•™>Èâ=³\MvÕ1ÑjóqŽFÆkÉìu÷ÝV#K Ï‘²f™Gw«¤ _­Û˜è¾wèÄú¨¾ƒjí%N×Yº©ŸFü @òíUJùöÖµÝζ=C†Ïôã{¸{¬*Úö€*däÍâÏÙèªà ùÅ­ ©‰ÕJé.'O Çð YÎâáòEŠsrÑj•…¡/êiÿó‘íþ<‚ ²þõª{–oš&Kç w*×<)Ö©QÖKäs+ëŸ:~†£3%RdÁ¼]Hm1ýñ²±Õ•YAÿñ#γ’¡kekoAÒHŒÞÒËŽ aÅda¤r h¾nií%f1¨‰êŽã&B ¦Æölâ\0ƒ¹£m´ \Ð7="vŸ…1_µÙ0úgn\ÇÈÏ=Ÿ×2 6­råÂå¸ ÞB(å9åI÷Oan©Å ÈR&Lð¸´Ãw2EU¾€D:t°žªMšG²+ÅÑ:Z¶¿lÖZ _†»ñ+ÖR)øœ²É%Ú±ˆ_ºT¦œ$¢f,F(».åà è4·„‚hœ“¤øªý®c åËO? ïГž•Ë}ˆÔ–»nº6±Ñ"_/*i‚\Ïoò¹y«Ôï¡ÔÒü…—Ð4µPB:˜$±(¼. Ôm{ š´ü;ÁÔ²EiÈw PÌ½Ç Ô­¯fbU›÷/TðŒÐ)6\ǾwHm=GV4õ Í=hZ’áJp'U,ÿ&9 wÛ'Tž7ÁH>¤ï«Ž§ñ¿Šñ&å‘øÆ ‚ä>Gõ°šl4%è{–ëÛFPÝràf6Žd/CÏVyþ&-¸mŸåü¶/Ã4 ¾†Œž@…Ž1qóÄÝÄ(6]tº;‡ºÑL¦Û¥Vy6·bZY¹ü|Hâ­:†/uP0ÝO¡F,=)»k³nÖRNXìdzœ«wÔ&a#ÈÊ׊d§^aWšz/Öé>Ç.ÚÌLæ\\1˩ޚ1ic@ÛQO)ኃ’Ôò¶ÀãF ƒ± 'DÇâ´é3”<Ü勦Á F$ùR+õŒÐï"8½tfÞ´R$×iÎr­£­MYb²:ËDÁ*Ç65Щ¦?³£f‹ÌÏ«G'èMö;r¡ßÿ¼ÎµÂ,—D´o£6ˆ’/Júµðø$y½LñÀfQZ1&õô´å!€°qJrt[-š;T‹gˆ&>µ‹°4ÕŸ<›|w8 çNhˆô ïVaŽàÕç^†¡Õ=‚ä¦'õºQaÙœ5…ÓÚH«•Bäk£9éHÇq%'ÒÜÀ%Óó£µ<òåâ ×]C`ûZh!›´rO¸dÇ~eeqk©s\•ä Ô’Ut µ÷ø¤§Ê﹤Òbû{ /ŸË'â†ÝÛÙ‹NH6»¨DºõE H÷L.‹.ý]ðHÿö¹»„Ú¡'WÇo14_wjQ¥_AõËW»fÌr÷æ]ƒ,§û “äN£3¨ŸnÇI{¶“aþ=Æ­ß’i‚†?FËZ4ÈOJÇ&ö¦‡fÀªÛcrèqX”­ÿb…° Æübl÷Åùft ×¥˜ü­”hZ‹4‹ëÊ­ Î]ÑWhYC…L$@3¯¦\ØCóRŠÊÀæžB·Öp°bïå3¦kõÌËo ò3ÁE;VBî*–§£?üëÒ6ËØ¤ÁMðNÇ£æòê1Q+vçe{t+¾¨^Š…€ù܈ úºTÏÍp´Çk:iu<LϵO‡ìqi6]d#I¡‘k>MŽAÌö™”Oa Qálµ”L‹€<uVÓbE4=Ì@X‘XÝ)P¯ßE?"û1MºÏ}Óγ(!Èï§ð‚KòÊi»Ø¾¶JPXBt^ÆîÓwˆ5ØÁö¶N¯¨ÏË^°“ú¨±Û—4“õ+‚Ê܈‹4ôéÞ’‡WRS9½%¿%CÈLmf‡4ð~îöž†èOéήl¦i%fa¨B®ÐAèhˆø³ºT ­s>a3ìn¤OSËŠN*²_‘¡OX7TSÞLJ¿ˆÆŸÉ‚înR‹µ5»¼ ''úbw¤~éW‰§w·]稜·Þ8âBðZÄR^•šWÐ?)xt¶Ê‡c\Pp¾äé~èZÐUIßPèkJ§ÎQ8§i+.W¨¯K.=šw“ˆ”Ÿá 9ñR è|­ xóƒË»aüMâ “ÔÚô :xïääUÇEªŠÎ^á}krd4?^Ò0Â?åÞûÂEùÕ°æ ]À—¬Þ‹<¸»h¤1b|“Í¥tNi¿âóØo¼z9Tñ˜Êë´Ü¸4¸éµviMØmçz~è.ösv§op2 !1t]!ûÌç——ýEù…æÂùǘ›? @„yUݘû׆û@¸7 ·\’T+#1•zÍ–ú'VlTR¸í*ýTê#¾Éð®±9–Ëèîè OϘŸpÈ)û|œÖrr5@½±ðY”#5J7ö"ޱ9g³ðKפÈ÷HåÔ¢w  ð-NÃu†¼µV=l”AiõÃ``¤X)­ð„É7Åû‘I¯M†óHY¡‚…Ô Ð»G­é™±ÜæÖQ‘ÚÍyÝ÷µ££šÀˆ$ø ôëæì‰—½SPÙÊZ¶«À¹ñ‡³Ë(’G>­ÿàÈc­ûÙ'+]DB{v÷3]¶èÉ_«yÚk‰k-3*ߥ} òQMñ@y®²T_ZJLCDí#"¦œ´çÎ\kIL á½gŸ5º‘"'´ãî]ãÝçÉ¡T]»`V)¿²Í¨70Çᄄ\9îY“9Ÿ³em]Æa£¼o¢Ô†öN“½»†ê’}<âý`Œ[ÌÁôœŽmØÐׯ'!ó`¤hüªût`Ï è´eUXcm÷~g7à0(ú8í›ß]ˆ¬C ÄáÝÓ|‰‘û)Þ«RûCКàA¡¼óð¤ƒåü@eømðE€Dãn|×Y™ES§“f8ÜuÚN"b*•Rèju$¢ÚrÑF»üõGvÇŒÝ`ó­§£QTµ†a¸$°fÄz4Î)8i®°!9ÃÎb‡ŠdÁ]½oN?p†õ)ƒ ¬‚ñŸ BÙq‹:ã}ò¶ aI´¤µu²“ódÀŽÒ‰0åÊ(OÍ–DI‡0–†e¸}q5€Ýf˾>ïN·i´î›ç·“ܱý….Ø´áyƒ¬o;¯:e£¨îepê—Úq<(ÔïÏ5žmÎïORŠ~çèHZÂ1Ô'Üko>?jYq§¦'òæÔ@ÉåtFƒÒêŒ~«ã®€ªC ?6­|Ä›má!à´$ ?ÊM3tüÈæ`öâɃúe´Û:‹ø!¬¤Ü£*çÌ›»‡Å¤ÙJÓ”oÀqô¨²óžcå,hŒÖŽ3Ðá¾v…u3~€Ÿ2J€§~î»ø-4a§4¬¤Æ\ÒÊÌ=’°2Æ=*Ë|Ú.ëe·ÍU`É™ºÇEOôÆs¹ïBLVG˜Ð¶˜gl¯2%úè¶ÍC„”˜üÉ5Árc9ù;Ž …”T:y!ÂÃÀµÞT/ÔœcôwŸöwû¬óJàýâ»d¿U/âCVO†0 ÒãŒB«¾h¸¬á.t}f°_IRü@B¶‚8"ŸBË&N¡~™%Ü6ÌË ôœ86ãy"IjâyŽšlÂD£ýp4Äb”ž_?ÊsÔ`OëMKrD¿ýµ2ꆟºä:úmŠ¥Ùβ^ôçRÐó@Bb¡™F”è&o¥8;¡©j%¨•Ûôb¨¹À¹2ˆ¨©ï]º@PÿÈ”rÁœ¯À–:-~7¸×&@C PCžI¯äi*—ÌŽ;Â˜ÄøR?A”U…%ÒD x*΃–Ú YòÓÆ‚×$€ ^¦-EýþqIP¬!g»ŽkW((6"ûx ¢å¸í°Ù<?óKNðÀì¸yµmg÷×8)åSuRÿûH:¬ð¸qŸdšð¯«R+RöÙZPc¢¸i%ª*í%6ˆ:“tÿ"SäoµûÞè11µœ§tæ;î}—“ûðY¸®Ÿ Ó|8bAåg‡‡ÔKºmמðp!ùDiŽ¥AsrÄgæ¼Iýü{C¥ñ[z–ç*u3ßEßæãV•Õäõ}¿GùF»ÎÔeÒÎò k§X¤˜¶[Ÿ,œ æ6·º¨Þ˜MÂ.ë9zx9.•²Å3ªÜ¾¥\ù_£ºØ°²½Ð½n1 mÕ±2&ËË?;ÿi>C‚zÆN‡áZκ‘ÃÉ"Ì7–›7–ÙEhÕq/«àu‚¡óˆõžfà—’›Å QÒ|&É¥ |NÆç´yè¡^¾L8£µíú™—ÉÑ5± wK“{CÀñyšB¾eÖéìн«ytm +ü.=Dò+Vãüã‘=pß&×&@ÉxîôO ¿­=µÆ^$|½£„†Ù»nÚÖmªÎ`Ï‹Ñ Í¨ë7 oAXNk’–ƒže/ô]9 –¡^t·+šŸ—õg(éRA7–$ç@ÃqºÕœ™Àëß›[Š‰ë—€’°z·™-µÛ(þžGlÒè  ?/#ÅÏD&S§`È,û5ª~ñ_ÌÎj ¤Æàd ÖÇ¢¦?³Û½x ‡˜‰ —Ô°øBðuø½´eö° çí·‘,6ÈÄKê©Ùz¤ñï^¹6ú!°I4Vðo=Ù±xC/·jÈœ½yÖŒÈÜ b~Iþ`N9Á`_·lâ(?Ø_×M®o+¸,‹Z½-¢64Ø÷ú†ë¸ µ½võa«›¾›ó}Ÿ¿¢¨òL `ÝÀüó—èݲïÄ5t`øÃÐyuÅ}»ËRÄqL5hª l>|àéê hN‰Bêvö~ÂóQ3ùýPàßrÑ!m>yÝ¡©aëýA¤·œï¸œjyÈgÚ`y"HIÈÔ/ \¨cá7‡*nmÄÒ§Ñð¯•p2›öÆÄº¯q{åbÒ-^Àè¼ÖÀèeÙÒ©]‚®dù´eÔÏQôGyÌÒy 1$‚–‰N@œõŽ›%çWò-3àIgì‰!ǪÝõr(Å#0/ h@[¶ì×Ç­;À+öý}ò5ˆêkêeÓöBFïO´Lvj¢Â E'üÅöŸ&ÉEÖ×Ä £1œò—yô÷X™“;pÜ¿ÿ0Ùèéz›“s|°¸ À¤Äuü<ûôù.]jÊÓ~Ã[,£Ýkæ Œ³&À%BÑc8Ãw†›§‚ݘ5’ÞŽÅ™ ×„1Åñ#ƒûJw±Pæ–Êw¯lãº!;…’­Ó¶þÅG"«]Í?”Ïè }™E*Ž` ð#‡"è[‘ Ú½9ë['ü›LI¯W#ì-mù DXë‹ÉVq*¸]йI¤XG7ô˜pJ’@ˆ±÷­Gt†ã°=FqÑ)*82V(-§Ì6ĹÚôÂZš6ú:9õ‰~þ¢âÆË ]Þ­ü¢+u('üèÈß›³Qµ-ØôØv|îñzM‚›‹Ï9¦PŽu½Ðfú=†^|ö#ºi’èhK[Ç Í:¨šuª ‘Q…Õ×—%8²2âñmî¸$GX2=ؕö±µÛÎÓ¢²¨–ëǘ×È*J$dô4Ñ/ŸÒ¼Ï%—II E0!z\Ƚª§ÜðD}&¯TÉjêË ç¸<õŽI,„u+´ß#HЖ¼>ýi¦Æt‡¾ÊÅ}°ÎÈÕŒ“vª­B1p¯Áƒ>®}{¡ùL N ]åp"fAš…/ÙjÆKHÎ)xÖOz¤Dtf¹‹›Q%ª»þ¦Â|„¤_Ü{Ä›C¾QLV˜ž¾„¸ç–}òÊ#kÍÛÁÂ]ü³äÎõ82€áxRgZÙE¹Å¾y5ø‚l§:Dkl‰Ç3q¢¹°áW4Æö΀‹&f|4[(á—‰_˜òŠÕ|½ _ÚûŠ“š¼YðܰžüU,XQ$L5…zöª¸S~9ËÖŸKh…,¥ Š¸§±ÚDœK}®‚ñŽfìа٠‘lƛ߬ž€÷Õ´ô\\úrÁ`ÁL=Š7½GgÔ&Yå%«m8—µ¾óÒÿÒ£ÙÇïü°°Ç(­Žø É;®bÜõÚK„†ˆoï _h,+?~„¨‘ÖÄM‰eþfŠ@»â,pj‹£ »¯¶w®'>Zéºôšõ©\ÕЀ7õ÷ œIQfôïY.Q£®(aV°t,RæŒË˪ÞÂyê;¬†¤.•Ú-<ýž_ç*úbuÛ Çñ d¨;Æ[h+•ú4§JÎ3p  ᱄ Ü­–Ùòx›¶&ÀÐ>ïÿùQ&ø'c:ØUZÅ‹šŸ™kâ5•³ú¶»ÙU*žÌ×=Vð˜B t.g˜ŸlÎåô¤‡dëhçk[ÿpæ !¶†i-T^JÜz¬rˆˆn,)åë—+ìDÑZÏ—Úœ-ûN‡Pìt…OÁy£¿· “Hòn‰ìi/FKvæ•w2ÃOÂxîHÒ£Le»©˜R©‘ÈgŸÐáµÚ x‰fÂY…k~Îv0Š\7Ú¹\qBògn\t¦4›EIPÍûl1t¯ñf]Zð¼Yl™=…"j¢RJ²Œ<ûËô:˜¨€ÞÊÉBS[}ýÉ&¬{ýÕ18]FF}iì‹PÌŸ¼†ï_CÉP«òaxÄ0VôBϹÁÇ¢uÑcwP¦lÍØ»ígø¤—–ן³ñµI\NA+Ú¶°.ïp’é©o̧`_3cšFÆÛƒ2ýDìTrºÎz¾ÿô6èºè.§±Ô¸g!þÌãÔÇÜ7HhI)ò‹]QÛy^FB¢#,xD-ÉD6nhÓ9Ä WLðâÝ<ºŸdà "’äŠä¹£PÎÀà—·‘î_Áwש½m7K&êu­7ßÚEþÌž&¿™ŸS5ú¾ùgž©Sî#³!©ëšuBù6¼œ_yÿôÝb*³üfUÍ- ý\Õ~\ž¡é<‰&Îì›·ÛpÙбùÓ«Ýä$¥é©ëzây;_ÀŠHñ¤ÉJLöúÊѶ,íØ¬%¬{À¦‘jrÒ™uÀ4­7±[‹fA è±!t,'¾Vÿõ ôå•{Êã&¹ªÎÄ„Aõÿú¸JÝ/ “$ FªHkT/é‹íýy@ ¾Öÿ´äÓ£]DÄ”—j çlÖ‚eQ@vÍ¢Îó%EÛ¿UÎ"Cœ2J«’‹êåß&G™iÜ£]vð!~*¾ÖNe2Žt„›IǪ¦°>¤sß´µÓÃÕf·æHÖq3¸ÛxÜ£Ùb` em^Ž5êÎeGÅ ;¶ÝÈø®DÓbZ¹†B:w‘ˆáë¡j2zDçÁ”¥ŽjZ »^S¥²?Dœ©[g bBw HLPn2œ Ð>[ÈèBµFÅD«S†ñÄSÛFëÏnt7ðrO“y²OgT4a@3)ÿ¨¢,S’ËÍÅýìPs®UÎb_#­JG|ën‚ÎŽ}Òk%),4”w$Õüuî5=³ q¶duEL¨ªÀM4£ïY¨‡O”¤~¼QCâ!:%éT¡¬íd*áþ+ÍÔŠJ¡ù™`zŽ(·.‡Ú>0LÀ›;±mÍŒ`¬£OàèÁ?ý–LSƒ7ñHI7ß,¾¦ .°·ÊÖtŒÖÅ"}BT@eLÖX:ËÆ„Ðè¢õ•]ÚK²ôï µ:¤Á‰al(¯`½ó0A3 ðÁmXfÔ¤ í( I0eùÁë.eËßûpój ;Ñ-xx˜a ÉsôßOÚAÁÈž×9€·µÊœ<ì¤D’ +ãh Ãr¶2lËò«ê+}ˆvÔR"‡–¤(GñÎØW0—Œ%¿l'(âöl¤ü;£78À†ÏóB{ä›»íçÒ*Mû€—ñ÷SÙ¡{îMUd¡W’tKÈNÖòå%–VLsz"Q•­Æ]jØõ^‹àü]‹+8Âi¹¶@Ún•[ :òãVrx¼ò«?hz)t·Œ6ºOzªö2ÿZôæl<Õrîš/¶ŒLl¿–ûÞ<5ÀAµTŽ]d ¬1„gôïÝé} ŸºESÉÍøÜtW §Ë«4og‡.”§®$ Mò ­Uòփ΂R$#BÕBÚòÁH½mÿK<Ò§ÕèÞëkAÔ2—Rã°8äRð £2ôžTbSaH¨Ëè&n…¿Í>{J‚ULól]yo’×írZÔ5T3ËS gj†oa›WŒ£+Uk›~ ŽyOï94,®œOˆÌ#ŸÝ§´½Çç)£bÉ›Aøn¡ ùü,g’Û· ø>X‘í&Z˜)™DçJŽæ,°ix»÷òɺÊs°TH'‡h Åf« chß  =kËå1ž¸,:Qß1‰» Ž%ätõ±]÷¾~Uèwo‡o¿”õþìÄe‚߉9°¥¼©í|Í3ÀH‘ÉܲÈä§ ÄjJ 7jyäA÷~Õ>[ù Å–¯ÍªwÒ~¦(Ëë¾¹~á½ï´žžu#ÆTóžÇنΘå×W= ¬CépÀ·ä2¦}žÿ”¶¬íl NZü)÷ýN-µ¼ü'æ$PÄëàD.9E ¸t+"Cz‚G —ßsXàîñàѯÞÍ´Pÿdû/ýÛµ‡MªÉ“G,xÃpÿ˜‘™äxßu8¦ÕbÑ:å0 †Zø¦æ¸Ã¿ßÏ= øH¨Lt4…2É1!¥Ä[Íà«·†änÎÐ7 °SêMe£×ëQ»»Ð-½»rÍ›N膓¾+|N÷•ÊS¢["E%\íx½tÖGíªtúÏ.9TÞÔ$£§Ä^ÌËXÝ gþ“ܵõðžzCŠv¨^êûASåÄJ£$(¼FTîÂÑ/¿“‡è?j—>L«þ¢jŠïÔÀO&Æ`5yÁo?«7Z"òV²Vß[¥ ™ôu¹µÐ_a¢HXøZ­&trÖ$ÁPÕJPÈ=ÉXí*2Êoƒ”!¨ö ø1qî~uQƒc|é?u}§-ô°bž¤œ©ÅR Nq5‡ãqì‡REBÓ¡e=‘‡”P’2™‹¯QÕLqþ8ùFáú'O÷Û ýÖâ¶±[± <ÿà1â •©îmù:(1ì¦ÓHÆ‘®™­æ£Üz¿Ãƒå=Z-+6]ÿBL ‘¦8ÇÓtWiñä=BÌý¸á­%!¤QJžZ3hñ´ýŠNuŠDSh †ò>euFkÉž,)4¨¢eÑîr q…s$ñÈÚ‹å!jP9è¤(ÇËQ¸â…„þ4ŸGLW\_ÍF:®ä5ö§=¨_á­ªˆ²vQ“È÷‘±‘¸K°´„PÐ;—Ö"9È0?”¼Ø endstream endobj 1633 0 obj << /Type /FontDescriptor /FontName /AYAWXD+NimbusRomNo9L-Regu /Flags 4 /FontBBox [-168 -281 1000 924] /Ascent 678 /CapHeight 651 /Descent -216 /ItalicAngle 0 /StemV 85 /XHeight 450 /CharSet (/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/a/at/b/braceleft/braceright/bracketleft/bracketright/bullet/c/colon/comma/d/e/eight/emdash/endash/equal/f/fi/five/fl/four/g/greater/h/hyphen/i/j/k/l/less/m/n/nine/o/one/p/parenleft/parenright/period/plus/q/quotedblleft/quotedblright/quoteleft/quoteright/r/s/semicolon/seven/six/slash/t/three/two/u/underscore/v/w/x/y/z/zero) /FontFile 1632 0 R >> endobj 1634 0 obj << /Length1 1647 /Length2 13526 /Length3 0 /Length 14374 /Filter /FlateDecode >> stream xÚ­xcteí–nlÛÙ±mTlÛNvlÛfŪØ£bÛ•TÌŠÍŠ}ó}§OŸçÞþÓ·¬5Ö;ñL<ócìMA¢¤Ê læ`”p°we`adæ(XÙ™¸¹¨8Ø)8ðÈ1¨-ܤ]mŸ:8 Qg ±«•ƒ½˜±+  4ˆM¬¬8 €¨ƒ£—³•…¥+€Z]E“†ŽŽþ_’¿L&^ÿÔ|zºXYØ(??ܶŽv@{×Oˆÿ±£*pµÌ­lQE%miIµ¤‚:@htþ,BÉÍÄÖÊ ge ´wÒÌœ¶ÿ8Lìͬþ*Í…ñKØ` pqšZ}º=MŽ©èŽ@g;+—Ïo€• ÀÂÙØÞõ³®+{S[7³¿ø”›;ü£³Ã§…ݧîLÉÁÅÕÅÔÙÊÑðUILâyºZ»þÛÅêS p0ÿ´4s0uû«¤¿uŸ0ŸZWc+{€+ÐÓõ¯X&@€™•‹£­±×gìO0Gg«¿Óps±²·øWôg …±³™-ÐÅåæû¯îü«NÀ©ÞØÑÑÖëoo‡¿­þ3+W ­9# ëgLS×ÏØVöpLÍ‹´½¹€…ùr37ÇêÜÎ7ˆú¯™¡ùLÂØÌÁÞÖ `4‡cRppý  þŸ±Ìø¿GòÿÅÿ+ÿ¯ÐûÿGî¿sô_.ñÿï}þwh 7[[c»ÏøÇž|.c{Àç®ÈþZ6¶ÆÎ€¿Ž•éÿåjlgeëõß9ÿ»µ&ðYÿæ¿«ÿBØÞâ“!FŽˆ­\$¬§ÂUÍËøHšòfÿyø JDÄÁàÃÀÂÉ``åbþ¼ŒŸ×‘‡•Ýïÿöo –å]­<ºÌŒÌÌ,€Ï÷?Ÿôÿ FÜÞÔÁì¯9Ru5¶7û½ÿü¥6usvþdüïmðYù?Ï_ Ðh ·²è`Êj‘éZ‡7<)¦ÛßË>æXÖ¨V\XãйÅSiôZÆØ4ÍûÞæµpâø¶'C»?Ú‹eKÕ“¼( ð#£é+DÝ ìà¢Ûf2(CÌ<ÕŒõ¹œ—û ¡Ãɬ±¿=©¬bPú E8ÝÁæ sù@Hæ^ˆA~ïˆäošÞ€Ù‰Ò‚VWtrJ™|ôpO5862<Ôs Ù·‡O—›KÁçH蔋/¨£^é./÷£rz.k˜c„(“½/£ì©í„ºÜ k ‚ä÷òIQŸ¬Üž> œÌgö]>¤þ¢³ªÜ'ïþô -¥înÑwR¤KÃFݾi˜nèã–ß÷êß-ìêW‰7ä0çfÌm®Ø–¼¼Á¢1íí}ìƒ<”ø—ÖnÕ½ùÞŽjÕx?\žÛ‘†ˆˆÚ¤ H(ýð¢;2(|Š:µË°‚,%.ŠIÛîen³ÂD®û‘Q–ò8 ã#áìR¡DÏ®±éÊdÏ·.SvªýÑ4¯uÀÛÏEÍRû)$°5p ÌèĠ…d¼ -˜œçDÞ |Ç'f#¯ô Rž¬»jP8­†6&ÊÖS HB@ØtpÑ‚ oU¦\õ{Ihq;ýrîQ<‡å¸êv”‡M¶;oÝ1Ÿ+<̆Ëò[€ø¯D‚P‹pp1Á9Ž¡í…á½<¢aéÆ=¡Má Pó{YDØZÖÈ C_FÏ^;&š €Ë™óÈä¤ã[¾¼¢²ìôyž¦÷5äß¹T¯,õö¤(i\äË.Õ¤t‹¸ôrY£D]ø»8èút¡Fu_dÒ2EGh‘jB_@‘ñÉgÐ [ûìŒ4;¼Ë6ÞS¥k[ˆ¢DD0Œò6…³0¨“v‘ÁܱO7eÑBôÚTzï!%lAj·²»‡ò¸ÁF2¨© úÁ’2ç_Ô^à.¥}pé]ÕœãÞ†½ÖlGŸiéãåÞâ8m^ÕE±àdøœÊç²ëQçH©6Ar-“î*^ouQãþ"º‡Î@Ó>ÊC*\‚Y’àÖú®ä–5˜s»±Ù[èùnÔES•kMžÙ¬²7Òƒoa%:5hÚåmoÔ°K6ØõšçéD=†Æö-ÿ)®é[\ù%ÞÒšbÛ8«U÷‰paPçi"Út'¯µväÍØe"h­ÐöMcL¢ÐÜò|ÓH”©ÁAqÅf‹p&ÖGjdáQŽn ªMÑ›És“”ÉaÈ·Ô" zÄ63kæY­\#Lú÷Û4ù»ii¢\ÿݧ†ú³Àz\cør‹sÈg÷%éúºMHÙÇo”·gyßôr†iÞìßnö#›¦äDŽ×£+¹ª –Õ!‚ó¤8·…ÍšÃÍÂi¾gf`,ží¤êÁIa‡Æ6¤ãF½|ï^«x8OÔ·X©jïN¹Šé4ú±–ƒ›¦çJ#Á,) K•ù…è‹ÙŒÔ4»|è ñNñì:v”ý\[_™‰_°û×q÷j̨"ìExK}RÆÎ¢ßÐéXhf‘*làm‚©å­Ýpã¸`ׄ[ß¶‘í-œY£ü”k±Û4(M9(W1™†»/¸R0lr”üsFÊ0Ê×(€©t_­Æˆ®¦þ„¹ü(¶ÕwFX) [!l/Ü4Bô†„jbùsÀS£šËCî­×ø4àý(}ê&¶73á'$Þ£˜Â½ÉP^ʬ X 2âL…Ü'žÑ¡ôÓŠÈÐ×®`ħ·‡ßÛwÌfƒ‰w5ZÙ3R²/'éBèžçFçv„T m0¿–HÓ¯Õ–±ÐÞ±¯×…Íçªd„~¦£Ï¿þš¢k¿§ %Vˆ˜8çA!¤\oÆ(®aæÝ¤À#=D–HÅõA‘2=ŒÚI}4Ò|[´*žw„ü jÉõ8Ê·#O]ºå#©õŒ¢1H.R2ö›õYWµÇ609-Áº(Ò+èbcªŠRœI©ÖªK}ª"ÆfÕ.ê–%–»F1Œ8±¦lÓŒmHBâ0ãø¡ ß¼ŽýÃoª½ˆ“äCˆå²íóV»h… Õ÷‘Ä]æ3\$íßð±(â¿Oû|c°4 Ú´¦|.*Àés÷7$Ä ûc¹e†Ã+]Ç&Ú§"ì‚£1n‡–6Žõl±eR{ `æ ˜=¶ùó×ìµ-ÿÈm¯³ÖìOh¤¬LŒK N¯B&°FéÎÝ›îi+-`MÔ6ïZÌE”^x<²[µšuqéЗ´A"¶‚{‡d₌‰´5£Š—xâé*»Þ¼‘²Ÿ|¯Ë·ˆh½‹‹GçÖí  ÝwžO¸™¸ŽépÚ7ÐA«ÍWv¨*½¯kOÂ]ÄlØú ±b’ ì½Vr $+š†XJF°¦Õä©·dfá…`zY?K˜ñoí Œ¸yc¼Œ„þ´ëú#ƒJ…‰*Nz#kD£v*K·«Q¼•$ìÜýC/QNˆ.H@Øa4nYõ?€ ˜kO~Ó{k¶ ϸ`!ûӈĩY¨Ø69T¶Cáï˜"²ØíUÏ3¶lž\fK’ ¿—+µžKøq•¹´ÐÔ35äQεˆÏϳhq„…ÚAô8à.Y1iXY…f ½‹øKw°dÒ–‰zÖy•hNVžCÌ/‰x¾ìv3ß—¡ O<8YŽPÃŽÓO"rðŸJ°ÈÙ©X9oXæ‹­ÏøŸ¡ {2ø'W«‘mVZã7.¬”ëHìiˆø^d¢•cJ<œS[£7ßþ¤a:µPæŽJÇ΂†O©‚µøÍÉ9x×O :ˆ$ %]f®& <¸ÁGõ\6…sAœ ïÙnµ²Öªñe] ‹èë2²èšS‡ºrâB\ѨPS±-c4bcF:.éQH Ô¹ÙlÑC¿ˆGž€©ï¸‚³+—u%F3¦9ËQÀœ‰*‘ù/Ì õ_jZÔËF$Pèîõ¸\[{ç@·$%IJ¢;gÍå»ÖÅ{Bæco&èެx¼œù£:°¶‡ö¤¬Ål7qjBð>^ºíƒ™ Ì#šè©—–›K@ ý"ÙexLQ¥Ö0‚¤ë è7ZM¿M„†A–ËÏScàzn¡Ø5ÿL‡²~*cÞ™@‚”[a̞Еu·l[B¥y] ¯½y-a¡i„¥+æê!Í]};¸’?œ N"Ò:™Œ£É(‘_”ïá Fšó·Y3ûœðSqz$M©HÕgnºÆZÂÏŠÖò#üªå.g¼™×òf¬…nÿV>Ø~Í0’öMA?P‹þ»Jj…dP¯µ^ Û;¢³P«W$Ídz‹¬‘ã$f¼ËƼig{{t. ®µx°×°ô)•[OøjS_y·˜‹ö6˜T_’§{¢÷.jƒ½”wm£_¿í×8§Þ*¤Ým7Ç bCKzžvNDxe­é °Ê.äÜ_nÀ¦Ý@ø­Þ©Â9a¾t@‹(WG1yxÏ”=ŸLÁÿAÅð)XŠMÝtöÔô@N‡ywWº/±ˆõ°%Ù Ât¡ú8ý8Ø ïVF¯h}‚άT塸ógFû»*áÜL??¯"ßÓ«ÐGxˆ¢^úVθ¬sÝyæN¿ß~N»DÑ’eT‡<ïø0ÕSù‚ë<}œ;õõI©«ZZ ÌÊÇ}èaœrbÎ2kŒ=ŠÌ˜¤#\勘§RXÐ96˜ylÏmHccN½’aå2ׄ¶Lè$5fù€ O€)Á<%T@ÔÓ«*â{ _ÐGõæÝ‚Dí9¼hjw—ß kÛëBÞKê¥Û@÷¦ÇbnÑmu›G$ðN)Øõ ‡KÀžp'X2ù¶íÕ#ãŸbpÂý´+÷Ý‚5X‚íYt/·sB?é·—õ;ɈãÐå5ïõ©ï5dav=_¡!NM&ªLJ¾ƒVgÙ’Ç‹­V‘¦Ä¬ãì´Is1Xþ+î+ûBìáP”$ës÷@ÚCi|ãX±UûH)c_(ÀÜXiÎjBÖÝ» VnýÌ:{>x_ÀÔPbf‰âEŽ^.c‘«"y¾ÂÀŠÙXß÷fBc„2öà¬YR.Õ -WÆÓàÀÜH–+‡™råј…&VDb¦ÅœœYRsÊYÀ»ö5ØÁ|\–„-°jzã%àNý›ž •zp‹& ¸±Zü ÂP l,µî¢Hñ³BÓMì q/‡Ìâ ¼óøaáO.¡+H͈Ñ™ÍzÊ]l ¨}`C§}ÞJâ/˜À·£[LtŒšvº¨|ŸÄ¶.̯ʼn^Y9-Ùî¡»äÉo8¯ D¼7q!¸í¤Ì“;æ¼ P‡°5Å¿m€‹SŽ=ÏùáUêôTŸŒ>Ÿjåîú}Ëò+ˤ-(E£ö#ÕP,](×÷ÇÎ¥qüÏ^°Ûñ <Ö@“ Òó¢¢vYþÁr{[W öÍ£ÆA]…rÀÒ`9o¿!^€ýfM+¡A¥F 6ËÔi9‚ãHÏÑÌ&ç ¦BÅ3…c°ƒúæ•É=}HSÚƒw}cPÓæÂÝW¸¼˜ç_5ª­KÏÞõ®ÀáL^Èë;r¿­”^ÇUÛa/žò§/dÔãn^ä‡áâ©.Íóì]åMÍ— ÇìŽíwJ3ÇûH ›ÿGo8ŽóqµË[Ým¶²yJt¸qîŽÏ÷ÔWn±g¢·!ŒóñèÓÈm̾p±Æäú!8›[;\Ÿ,3Z(6BÏJuÚMl|.Êðú÷“!ß6¼4ÒÂZPô·ÚÁÿ’íþš \ÔK{p\7+§'càw1Q+µÆbûÔ®^I·_!J_Pb ü\c›{¥R¸»Á8ÝîÕŽó¸Ø!-ŽQØ œÇ´„%Œ‡(¹×‚˜«Œ3yÑÂìûž4ˆax8›¦ç„vºÑ`¸ta­uaðD¸«Û+O‘ËeЉBWWî&.ô³‚¥cpLu1þöx{EðózeÌlž* wŽ:sP [°™PÎgR>gx?`ž÷Š?ïS/˜ÄË}»\ÛwŽh'Sr~áëÝwÉÎàà#ɬÙˆèþâ³vR «z€ó¡f“ELõkU|Bç¢Ùö8¯c…æ‹ Ytq~\ͰcaÿðúÞb:ÓÃ*kNFz›P2‹ƒÛ·?Ò÷/‚¨œ0µ«aR•m­œÅ´(³SêS¼k,М×Γ† 险<’D~ÍÖ„”é`¨˜… $ù9 b}~ògRI&NGï‘”D©Ñ•!²:_iÄÓŸ8ŒE~ùp~U0ÆË„í·´¨¶e®üýÜl›†«¼½Á$ĺ]"8|æ:¦$Òt“Ùƒ[êFÜÜÚAÕZg$”/hƤ¿/E¨—3"Q$RoËv’ÃØDX㎘d›uk$\S)[ý~| ínE¥ÑºË~ðÍbCË” ï…éÎ÷w„Ï2ÏÓfeØ‹ÌAáU¡YwÕ=¡ètȆmÆõqèe-À¿ ?Jë;¢Ȭ™s®Üú¢âïBjf}ÞŠî4¨ ›,ŽMðnð¡Qç ƒžï±Æ—ÄSÐù™«Æ³¡ÌÀ“]æ8-:ŠäµÄƒ0aãÜäÌø"Ÿ¹ØÖ0€¢?÷2k¬7ˆß~²Ì¹pUÔ¨l»¶&RpÔ–îsh§³ÀI“_HRaÙ))%V.WÑröËw”Ë÷[NµÎ‹HQ“öÈâ;—Ãå-rYÀJ«'ï´&F¿±Ür¼¯'z½sw–GÍ®m%°Ô³ô7©–ù·Äiuó“Ørm­?œÊçmï#íáåÒÈm/Ï•zO geèÀë§ašTÈÞjý«úJÒN I­°#'¯ÎIï^mgˆònm‚“auïuUÁŽŽŸ½VfÉÿ ¼á¦ÕÉ d|…Ž1{è6ù˜=ªò%1Î{Ã…zñaXúär.V("é€í¾¼83ƵÛn–û'N'Ò±C€;g .Éx€JÏ#>'N|sZ‹ÉD'2áHxØÐYÚ›m#Åýê‡Ûª Dé¼m"ÔuÜÚi2ðfi'B¤4 ‹íB&¿"~Ð)§$!¥&öx£•t³Oìc˜Ô EÁ0Ά¹ƒ—ø0À“*J0N¾d6<ƒ]RsȆ÷Úœøô¥zÖW°m‹´Y]ßÁwÉ|1‹ˆ-¸iÿÒû˜}jÙMø:Ÿ2ѧ—ñŽÿ»»?åTx,oA ÞÆ·zꦽaÛ¡EâøG>ä¹X1"Ü•À~~ÁÆoCÄÍXSªÓF @þÖì~³C(ƒÌ‡F¸LëŽè e¹Ô)ÏÍC~àŸØšïùUáð®æäH¼Úñˆ,MeBãónÃdj;-ÖÔÌöõƒ ÄÜö—¡«Åû™î0»Š­u¼?2:ƒŠÈ!”fÅy žì{Ñ¿IïBº ¬Òl‡&õà‚³YÃBÝØÜ »|'D±ï"@lÉ{™ƒMõ0ª=™vž÷sáÓ_Àÿª;­©ÕDzy‰„J?jTÌ`ø’æÄv'ô|*u³ó˜ö=‚ÉœiЭ=òHN²–ëÈ©R{7.ذVR™„ ·P[-÷¡Kçòθ¸ŠE3œµ/@-¾†âÖ2޵Àš›*KŒ¦!ù«¶2‡ž¼Œ"°Ï9i!Õ[OÊ+¿]ª–ÝXùù;s w¹ŸWw1ÇOQ”ÛŒkS¶|49úô;uØðv’×[ÿÙ°ß77ü³™³NKç¸çÆ$õ°Këqk±Z “¦U8 Ç@ÌNÄÂ’V­|L ´-ºî0Ñ`E*iùñ Š÷lÛ?ëé¸\°bòñJ¯‹F=Í­½—÷ó†ûx.ÇÂQ­2lé S9UÑè®G"Ëwíe•q[Mr'QB_Z¹JNuUÔ²A‘ ^Eªñ¹‰½`ÕµÞÀ2I±äÇà8̦~A"I­=Á^ñ¦~÷¬íÐM‰ƒ°<„C|OÆÈð)»‡°=/)•¾lu y’v"‰ Ž—ºÎç —=´ rL2ï)D뉱èAioKF%¾äváç RVçuˆ)­–Zá”üÉéÅrÓÏŒðSOTÖầH·E‡ãÛ5zgã¶?¾o¿i¡wŒ?ý/&´72( 8ºf†¢e3&"‚<îš,ãá¢ã¹$.‹åHpÍ}P'É}.&µ³òÍ1Vúý͉ã뜩W&"5bãUз½Zró¨§gFϯ€úIêú:X8ºÿÂÍÅë˜)G¤Úƒmä§1BÏ­„ËÌÛÐ^»/x½då´îÆÁ¾mÕÉ,䔦O$ô8þ/š\B xr|ê#T"kÕ@Û{V„+öJ–þôØ$7“ï(¿­ç;³‘Y"K´©ê9„-Qþù¯¬Û.>ík† ²uë7Xþ?ý¤a³¹Ø9¡Ý¼þÛÏa¹ßW+®99ܧå+‘®¾Ú¡{ÂfE!BESxz ZÏL2„”<_ï™ÆPPÓrsbº‹ÎžæmÚõšW0[ãá:ü>øZßàuóGQyS|‰ÓZë›ÚûËÊÆæœŠˆ?„iÔ¸ûœ:l'èn6&Ï` ±D`ލ*kj¦ËÏÍð¤ÎÝñü³¤Tv:‹FÒZÔ€˜ yä°`Î÷ 0r"h ®ŽÝ-*i~c^XiäMN‹å3&/2ƒGÝö`Θ¤ò´›Gù¹)!ò5ºÁÍ…›i(Û¥ü2¥´P¯ó%JÓj]Í“ÝFnùóõz$ý·¦¯bùo' \ÚžÑ4ú €šöp\}<ÂÒÀWS«9}~ö²?¼®%mBP&¬aê‹ÑªPY¨ŽÍëÜÁXåmì`&åÀˆMˆ,!Ýq™ °ãøTͤ9¦ÃÉcªù‘.ð·h†Ëf. Ò©gS½ǬFý—9ÚCã_¹ÞŒÆÓ“4òÅàj~Õ ùÇ!èZ†ñDi”Óœ)Ñ;ÚXY˜¤ãºÐ~û‰ÖâNÊ&mIá+Ç^j2MºO­¹ÛgÂPÛVÄZžU ?GVÌ.€ü¿Q•U9d‚õ¾`‡;à®&P+‚ïõÔÞ^„©šòÙ®ÓT˜ü`§¦ÿb~½E'|°ÃÍðà‹Z· äor} $à7œ  ‰<´§¯—ÔJaKÇ·Xâ$ @ÐÆMásÓ žÆ÷Ø+7ªH8}B/´VÊs²²«b &Àhöj ôJŠ¥2ú¡sC“}¤vW“wT[({Ù±y¯rz¬ŸEÑ`͇O·(u1;IRFˆµâH†:}¡à6Ó`ùÍ/ED“7"¾Z—W®p5:à†öøs@Cx:`|<3"Š3–õÊ1œN™{ï>óµÒƒca®B‡4."!HSCjFDX¹Suxcs{]¸€H•§•I=vŸð¦ßkñ)å2}…2yQrUK&‡ï<,ÎâÄâ–é%G&¾U¦D­µBAk±›A£ 'Â4Ò¶(о."ÝK‚EŽi!D6ìv°|gfËÉQN@Ê@­Á³Ô:®užÐSíB2j¹ói Ò"ÍRHÇòâ'þ]´¨'«ò¥[¼û{¢(è/…ðþCMѽò¢ Ø|ÈÏÂFíÔ•UÄ~¦*8OÉ]EŽZs$»U³]õ^Z½ÃN›ÒCþyP»Í “]ÑyƒâÜ~+åçõBÈ÷*¡›Œôå>ý†ÆYqÎM|~‘!œ Ëi Vu^OÁLQB$‹Js¿î7 €Fá‹qïÁÐâ8KØŒxÕ{ ó ž3öW»íÚÕ5Öè¦.ûDrî~ÚóJø¹”{1__tg¾ªB·.ÇèÜu#àe|äAù±½Îÿ–‘bßñŽç«¯?ß+V5Cò·QJÚ‘I¥Þö g"ý9Œ÷N° ªÔ×û S2ª Ì-&Àœ›Š`{¸ÖÒÓa×ÿŒ–-ÂGÔ`£èn}-]aÐ}Ž\w–p™0škþ©™S¡Ÿ²ÐNè9¬¼«y9ªþ5“ªó"e1Lj¬ã×3öBœ­GD+ž8F‘oú‘wFRMr$7CÕ~ÇÃ㹉£~±­_ùm¦ ã÷Öß•n–Ò?p; AHG!-v^hU=A‘l«Üàß-èR= ŒüÑÆ-YVNms0°M%–Ô‘¿h~ q…5ë³Ù£–Èa#«ÔÐü^¯ÛßÚ¼7¡ŽWðDfšýÊô,¥ò;¼o8ßµ>ho@Ö,©׉)™ùSl2ê†7†œË«k'ÁƒûO¾ïùü9⮺†6$°ñ¢ŠéÁSF÷ÕXØõ?–X(Â.`xеà_$aé½5u;BPö]bTи§Ì8-íB‰EtË m\^R±‹¹'²æà[‘;‚g©¬kóT»wF‡^eíÓl'ú±7uiý»áö5¢ßPÄ ´ðCìi¹³Ñ«LîW$9Úï–°Ï8j¯H¿¹ØID ÚÖ’ àÁ)—o[HK²çhè}¸¹ÚpÕ«ˆÜuíØe5¿¢\ªô×ñá=µþF°ðF|y­/6J;Œ¯»(§eÀèÆÈ¥§=þ¥ PÂÐt“´Íðp¼Sï ×ÞeC0ƒrߌk,Ë+‚Õ†@ʘ}ß-N_˜ßI·³Û+ÜèŸÑuvJÁJ>Qß{i]óã©A~˜•TÜÅÝq£§õà+‡òÄ=ÃM”l¬(@†}¹¯ÜøHUæ™1R׈Yä[¯Ô²¼V…ƒŸý>é IÉ%P6*—doÞóƒ hšw…–°ìÛYûB-1z«#k <ðøâm™kŸ€;ýû› î«Ø³‡[ŸOMR¸ÝIA)ƒn»ðLæTÊý±ã‘ƒ+=Ÿ~J—÷|î>ÔpË•¬”@9LH~ªÐÁ-¥ßÑ/ø?9 å+q»Î/ f'ËrúÕVÛRG§Ä, ëüÙK”n‘‚U[…<×^a md*2ך‰–ÜLÅõŽ©·$ס:ób4¤˜ã5tÊÓ¯\ëúÄüÞïëŪrÞzÝÂiwf b/µÅËCüÃ7Zô¦/}¶ B#‚Ò&îxÃ6èFÙ¾ÂlÏàÂ_{sÊÀEG¨ÁNDà"Â¥iIf©á’Ûeè×ÿôzW˾ØÌun:ÀS!NQÈQÎ_[-›¤DÝiû>e!Òe:Á-«§µfÎײ4Kü>.Çj‡ÌŽógœÆûè;—à4+¬þ²5H¼_¨Z‚e¨½€jx÷L€BÝíìãý×ÖôÇÁ|¿˜”n¯ä!)#Aðºþ,†Äºù#÷²÷]ÉÜŽRe­^"GÇœ€~Qå[Y8²¯˜Ù©eÙ™ïéü¸ELcóßHU%\g§ö·DÝ»OÞ@L#5Ó]/œ˜iÓD^:!¡"~!:K„Pü®žÒ ¥r¦% ¦40V8‰É02ó\„k\QŠGDîeÈ—–ñÐ/êV=t(Sx@1ôN8³ôšÞ:È.ïP¹B±Öâæ’ùÚC?QÙOè}0œRðúK ±ß¡f3ð.7¶—ÊmFÏÖy¬ÛÿØrž§˜‚+2ÞƒŽ¯Ù4×'˜ˆ³ñâ -ߘoÛÖ`©íæy°ú{ú¡))%) Ø°Lœ \Ã1&a‹•±Ræ´ÝEÊñ‡ëI<¡† AÄ—Á:Õ áj)³ˆ é4Äv…ó]vîn,”8$‰xŽ«ókëÒ?½¦¥.}*áûzlÂÙM·†¸ZQ^”ilÕò¹ss>„¼Wˆ2éJKíožQ€?¼Ô'Ž;Œ*&ùúÓcؾ/R'Çùyg¤eZÅg 2×Àh5M©4['öÎÇ.pÀ­EåM„êHåùZæ°‚–ŽÚî¬Z'D`ŽÝr‘ êƒ#f•Cü ææó Yêˆê¤<ÊH+½÷<=ˆh°r0žT‰üãÑ fS…f Ùäí/ sÝ^†kÓËé†m#ØI ³1–ʯé¬pifÉiÖŠtÉ7 úRQÚˆ.$9_ÈòDð@º{q„xóæ×@`~s~耮±IV"GYä``®bÓ—†"D™qÖ/ Ò,1µ¢âß»zdóv¯.¿z”Yb´U+îÞ‹XjaáOÏnÍ%DI‹ÜÆÄ`SËj`é.“ÓÔ …õÀ÷êú½×7Yê”p“[ÓÒ€¦l4¼TíP eÐÉ“ËȾ¾IŠ;‹pÁWÊ$mRxtÇ‚ÒÌA“ÁSÚÒ‚öíDüD@Oz*fy˜SF•SËÀk—vý®%…ãÁ¸ßò,(Aï­ *r†hµL6[õ÷\êÀUŸc…-÷ãl×ÍÁ®ÆBÚ†;gª2¡Å0$Ûÿ[·,ÃÏÆSr¤qÍ#aÆB»mùE`~>ÜúË/`@!n‡ŒsºÅdD¨w[>ø2x…ì½g´³è„IÝ/ Ȭ۠¥Þ%©Ò¨’•ç©Üty-îîwé_€æyÑÅ©€ûD3[–Û°¼c¤øt–À X;;ô“uˆÚጊÔ2„¥( oÙ±&óc<…ºBnL¼¿„X³ÃvZ¶0{(¥’ôãnr…|~íØ– ¦X2GV¸Gï„ë—wñexGÙŒ¶êÊMyîïF•Ë­?Kýš2òVö@ìúûKMž³]Û½ÑdØ—ˆÈÄŸ&¨Íèi4Yoü “.FzCîh$­Ú(éNâ2áTFA9ºn¨–֥ƛ>ä3Û<½€Jfª.ýÉ föÏÏo@óZÕC?:œuƒäå®ï‚™ù3lwß×(¸¤D!eiËè…¬À.)›C3äa†Rà *Ÿ”âäx’ð|Ãò›«ÒüîO¢ñg_Óˆ]øz’’lÖ{&0]¡]Ïßh¹K²ÒêœòZ)ÏSš<«Ò¼ÔVX6âç.îT35í Æbr†Ã,ôÀx\²t»â‚<Ë|€‘`!ðÝS-bò@4 ¾Ûdbò¶J! ã¿åº oÉByŠä ´šûèWv‹¨Oóì^†FŒÌ‚9 æƒÝª÷Q€ä@‰å`-Àš5 ¢ÓW'Ãk›”úR]‚´Çý¦`÷P'Vƒu5K ½ú5BHgµq¹0¯'Ùp™—6ÐöO{Ë#î5ŸäèÍ ?”Ioµ³.Yß1Ùx¯éo§4€ÐÑ¿펈UQV©<„J¾¦ê¶FáìG4°¾OÎmo}(R„ÁžW@јÁX‘Ìû :ä³¹9v¡”Zn—²Î3œ¬-OV'¦…7ÊzÏ{AE!o0Ùàãy?^óò{A·洋l‰±ß`¨SqÝ-¿J¯<,·(v©K¡NÉ)ú¹ŠË«.™ØáˆðWŽXéÝœDÉ)šñj"Ÿzs—¯y1´ûŽÑl>iòsf›öÓZEϲéÀ9g×<‰~­ÖœPt?>Ý­Mȉʫ en¢ˆÑI›?&…0A'•߃NM¸<öàÙ6¼ÒdùðG–Ó3¬Gî‚é³ ©vQÇE- çŸf‰û;ª()ÙZÙsŸ¬ߌÁÖ£aHæ1$ i‰Öƒ«“w¶ žìæ¬ô%=ew8Ô7‚˜P4L#Aì6ž˜©@ƪÙG‘69\KÑDç¹ÓÅ—¨çùmˆ!.%Þ Îç*}O;…"ävA}a«èYþM?ƒ*"ƒ§.®>#ÍꜩdæOtÕº&Äjÿ~¸­6†²Ñ ¢–ùyÆfµ(¿ÝõKÚ!¹ÖY$zoSTV‹Çë~´1W.Úõ”Ö±&š¸RÙkn±nüÇÉí_-ÓZz(¸IiYg~ËÍG!Ôtfñà–Ž"1.>ˆG³ãÞ—©B.9ÛöÉ1РìŠBêÓµÎ|^Ï»p"t úþé«¿µ]ïJ²BW×¾Úù@Øð{¨3kw@/ÝÊ¿±TJ×}禓Ä]ƒÑ‘lÇgæÈáス½eβÚi¢=ç1ŒÊõñcÆZÑO&²r¸Ä œ²øò^ŽÑŒÊ;ÎäíJGùe`]‚Œ=@|c+Ï4ÜPð™ʛ¯æÆÑ5)ø…#žã°ré¿§¶¼]@ú»jT- ?¸ŸËÃï㓬Orµ÷c³Îd¨‘½¢Ç*3,KÙ éP¢:´Mxe~]7_ßü}Ô?^;T!ÉùÚ ™w@išÍpÁ`®µæx¼ {2ÀvA} 9÷LûB•ÕpÌ] ë$µÈ˜ò?Ârß=§e‘m-7ܹ«¨]¡M+Gc6Ék“¯'IJ+Å{λ« -:ʯ,öKñHéÝ×ùyµôßÂRø ö5öŽ;š‚µ01µq˜'åkÐNÞx~˜ö‹XYsφ:˜I97–WÑoŠg'ï%œ*™ùl?—Òch»(]—H6šœÎíúvzœið¢:úç ¾ùÒ@G÷ýÜ>ÞGឦ‚É1ü`bÅÀGˆøÑ˜·¯Á ýöÿ~ÛÐsÕ?oQIcŸ 6ñXàȈkköxOG–z¼âÈe‡‡¨Ì"”’©8G¡0µÄóýè ]pw˜?!ut|QQ’7¼¿âË’›_‹&=]8ÌN¤Œà¢ÁQÄKm'¼‘v®ËœÏ¬ãG\5ðŸã°›ìÍ@ älL åg¶uxÎ8QÀôƒÌ\ùx_g8v õ,ÛûGh»ÓiXÌlj»ðzü2ÞÜN£MžŠG€!– øöËþÜ@ÀG-ùn'_~æÛW4{QV¤eT?Î-)Ó¯çêŽ4ôy+!¬©~ Ô  ‰ÇãuÚ ; Uy8É^rõáµÉ „bHÖC!ÃÍ ý©h‚ßm†M¥¼÷¡Ê;°Î2Üßì¡‘Y{9ÎbJ\Š.JÒŠ©h¥o²%¶ÏâUË^‘½SóœÉ¿f›´NÝH´Þl†ûàéïÇ”ÊçT‘0J“ùíÊžöYÆA"iÜÏÒߥù9hf·4úÖ­O«°s™_ü7—CÐs¯mõˆuß6³>.Ïgoÿìì8Ý endstream endobj 1635 0 obj << /Type /FontDescriptor /FontName /IIOEKO+NimbusRomNo9L-ReguItal /Flags 4 /FontBBox [-169 -270 1010 924] /Ascent 669 /CapHeight 669 /Descent -193 /ItalicAngle -15 /StemV 78 /XHeight 441 /CharSet (/A/C/F/G/I/M/N/P/S/T/W/a/asterisk/at/b/c/colon/d/e/equal/f/fi/five/g/greater/h/hyphen/i/k/l/less/m/n/o/one/p/period/q/quoteleft/quoteright/r/s/seven/t/two/u/underscore/v/w/x/y/zero) /FontFile 1634 0 R >> endobj 1608 0 obj << /Type /Encoding /Differences [2/fi/fl 33/exclam/quotedbl/numbersign 37/percent 39/quoteright/parenleft/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/bracketleft/backslash/bracketright 95/underscore/quoteleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright 147/quotedblleft/quotedblright/bullet/endash/emdash] >> endobj 204 0 obj << /Type /Font /Subtype /Type1 /BaseFont /CHZLJB+NimbusMonL-Bold /FontDescriptor 1619 0 R /FirstChar 45 /LastChar 122 /Widths 1610 0 R /Encoding 1608 0 R >> endobj 174 0 obj << /Type /Font /Subtype /Type1 /BaseFont /JLVIDA+NimbusMonL-Regu /FontDescriptor 1621 0 R /FirstChar 33 /LastChar 125 /Widths 1613 0 R /Encoding 1608 0 R >> endobj 203 0 obj << /Type /Font /Subtype /Type1 /BaseFont /RRVHSF+NimbusMonL-ReguObli /FontDescriptor 1623 0 R /FirstChar 33 /LastChar 125 /Widths 1611 0 R /Encoding 1608 0 R >> endobj 131 0 obj << /Type /Font /Subtype /Type1 /BaseFont /FETUBU+NimbusSanL-Bold /FontDescriptor 1625 0 R /FirstChar 44 /LastChar 151 /Widths 1617 0 R /Encoding 1608 0 R >> endobj 132 0 obj << /Type /Font /Subtype /Type1 /BaseFont /AJOVIH+NimbusSanL-BoldItal /FontDescriptor 1627 0 R /FirstChar 46 /LastChar 115 /Widths 1616 0 R /Encoding 1608 0 R >> endobj 1122 0 obj << /Type /Font /Subtype /Type1 /BaseFont /NRKXJC+NimbusSanL-Regu /FontDescriptor 1629 0 R /FirstChar 65 /LastChar 121 /Widths 1609 0 R /Encoding 1608 0 R >> endobj 173 0 obj << /Type /Font /Subtype /Type1 /BaseFont /JBOZKE+NimbusRomNo9L-Medi /FontDescriptor 1631 0 R /FirstChar 2 /LastChar 121 /Widths 1614 0 R /Encoding 1608 0 R >> endobj 133 0 obj << /Type /Font /Subtype /Type1 /BaseFont /AYAWXD+NimbusRomNo9L-Regu /FontDescriptor 1633 0 R /FirstChar 2 /LastChar 151 /Widths 1615 0 R /Encoding 1608 0 R >> endobj 191 0 obj << /Type /Font /Subtype /Type1 /BaseFont /IIOEKO+NimbusRomNo9L-ReguItal /FontDescriptor 1635 0 R /FirstChar 2 /LastChar 121 /Widths 1612 0 R /Encoding 1608 0 R >> endobj 134 0 obj << /Type /Pages /Count 6 /Parent 1636 0 R /Kids [126 0 R 136 0 R 170 0 R 176 0 R 179 0 R 184 0 R] >> endobj 193 0 obj << /Type /Pages /Count 6 /Parent 1636 0 R /Kids [188 0 R 195 0 R 199 0 R 207 0 R 211 0 R 216 0 R] >> endobj 224 0 obj << /Type /Pages /Count 6 /Parent 1636 0 R /Kids [221 0 R 226 0 R 231 0 R 235 0 R 240 0 R 244 0 R] >> endobj 255 0 obj << /Type /Pages /Count 6 /Parent 1636 0 R /Kids [250 0 R 257 0 R 261 0 R 265 0 R 269 0 R 273 0 R] >> endobj 281 0 obj << /Type /Pages /Count 6 /Parent 1636 0 R /Kids [277 0 R 283 0 R 287 0 R 291 0 R 297 0 R 301 0 R] >> endobj 308 0 obj << /Type /Pages /Count 6 /Parent 1636 0 R /Kids [305 0 R 310 0 R 314 0 R 318 0 R 324 0 R 328 0 R] >> endobj 335 0 obj << /Type /Pages /Count 6 /Parent 1637 0 R /Kids [332 0 R 337 0 R 341 0 R 346 0 R 350 0 R 354 0 R] >> endobj 371 0 obj << /Type /Pages /Count 6 /Parent 1637 0 R /Kids [360 0 R 373 0 R 387 0 R 400 0 R 414 0 R 426 0 R] >> endobj 455 0 obj << /Type /Pages /Count 6 /Parent 1637 0 R /Kids [436 0 R 461 0 R 474 0 R 499 0 R 510 0 R 523 0 R] >> endobj 546 0 obj << /Type /Pages /Count 6 /Parent 1637 0 R /Kids [535 0 R 550 0 R 564 0 R 580 0 R 592 0 R 608 0 R] >> endobj 641 0 obj << /Type /Pages /Count 6 /Parent 1637 0 R /Kids [625 0 R 643 0 R 661 0 R 677 0 R 696 0 R 711 0 R] >> endobj 733 0 obj << /Type /Pages /Count 6 /Parent 1637 0 R /Kids [724 0 R 739 0 R 753 0 R 769 0 R 783 0 R 798 0 R] >> endobj 821 0 obj << /Type /Pages /Count 6 /Parent 1638 0 R /Kids [811 0 R 825 0 R 842 0 R 861 0 R 876 0 R 890 0 R] >> endobj 917 0 obj << /Type /Pages /Count 6 /Parent 1638 0 R /Kids [903 0 R 921 0 R 936 0 R 953 0 R 970 0 R 980 0 R] >> endobj 1003 0 obj << /Type /Pages /Count 6 /Parent 1638 0 R /Kids [991 0 R 1005 0 R 1016 0 R 1029 0 R 1041 0 R 1051 0 R] >> endobj 1060 0 obj << /Type /Pages /Count 6 /Parent 1638 0 R /Kids [1057 0 R 1062 0 R 1119 0 R 1124 0 R 1167 0 R 1226 0 R] >> endobj 1292 0 obj << /Type /Pages /Count 6 /Parent 1638 0 R /Kids [1289 0 R 1347 0 R 1410 0 R 1476 0 R 1536 0 R 1598 0 R] >> endobj 1607 0 obj << /Type /Pages /Count 1 /Parent 1638 0 R /Kids [1604 0 R] >> endobj 1636 0 obj << /Type /Pages /Count 36 /Parent 1639 0 R /Kids [134 0 R 193 0 R 224 0 R 255 0 R 281 0 R 308 0 R] >> endobj 1637 0 obj << /Type /Pages /Count 36 /Parent 1639 0 R /Kids [335 0 R 371 0 R 455 0 R 546 0 R 641 0 R 733 0 R] >> endobj 1638 0 obj << /Type /Pages /Count 31 /Parent 1639 0 R /Kids [821 0 R 917 0 R 1003 0 R 1060 0 R 1292 0 R 1607 0 R] >> endobj 1639 0 obj << /Type /Pages /Count 103 /Kids [1636 0 R 1637 0 R 1638 0 R] >> endobj 1640 0 obj << /Type /Outlines /First 7 0 R /Last 123 0 R /Count 5 >> endobj 123 0 obj << /Title 124 0 R /A 121 0 R /Parent 1640 0 R /Prev 119 0 R >> endobj 119 0 obj << /Title 120 0 R /A 117 0 R /Parent 1640 0 R /Prev 115 0 R /Next 123 0 R >> endobj 115 0 obj << /Title 116 0 R /A 113 0 R /Parent 1640 0 R /Prev 19 0 R /Next 119 0 R >> endobj 111 0 obj << /Title 112 0 R /A 109 0 R /Parent 19 0 R /Prev 107 0 R >> endobj 107 0 obj << /Title 108 0 R /A 105 0 R /Parent 19 0 R /Prev 103 0 R /Next 111 0 R >> endobj 103 0 obj << /Title 104 0 R /A 101 0 R /Parent 19 0 R /Prev 99 0 R /Next 107 0 R >> endobj 99 0 obj << /Title 100 0 R /A 97 0 R /Parent 19 0 R /Prev 95 0 R /Next 103 0 R >> endobj 95 0 obj << /Title 96 0 R /A 93 0 R /Parent 19 0 R /Prev 91 0 R /Next 99 0 R >> endobj 91 0 obj << /Title 92 0 R /A 89 0 R /Parent 19 0 R /Prev 87 0 R /Next 95 0 R >> endobj 87 0 obj << /Title 88 0 R /A 85 0 R /Parent 19 0 R /Prev 83 0 R /Next 91 0 R >> endobj 83 0 obj << /Title 84 0 R /A 81 0 R /Parent 19 0 R /Prev 79 0 R /Next 87 0 R >> endobj 79 0 obj << /Title 80 0 R /A 77 0 R /Parent 19 0 R /Prev 75 0 R /Next 83 0 R >> endobj 75 0 obj << /Title 76 0 R /A 73 0 R /Parent 19 0 R /Prev 71 0 R /Next 79 0 R >> endobj 71 0 obj << /Title 72 0 R /A 69 0 R /Parent 19 0 R /Prev 67 0 R /Next 75 0 R >> endobj 67 0 obj << /Title 68 0 R /A 65 0 R /Parent 19 0 R /Prev 63 0 R /Next 71 0 R >> endobj 63 0 obj << /Title 64 0 R /A 61 0 R /Parent 19 0 R /Prev 59 0 R /Next 67 0 R >> endobj 59 0 obj << /Title 60 0 R /A 57 0 R /Parent 19 0 R /Prev 55 0 R /Next 63 0 R >> endobj 55 0 obj << /Title 56 0 R /A 53 0 R /Parent 19 0 R /Prev 51 0 R /Next 59 0 R >> endobj 51 0 obj << /Title 52 0 R /A 49 0 R /Parent 19 0 R /Prev 47 0 R /Next 55 0 R >> endobj 47 0 obj << /Title 48 0 R /A 45 0 R /Parent 19 0 R /Prev 43 0 R /Next 51 0 R >> endobj 43 0 obj << /Title 44 0 R /A 41 0 R /Parent 19 0 R /Prev 39 0 R /Next 47 0 R >> endobj 39 0 obj << /Title 40 0 R /A 37 0 R /Parent 19 0 R /Prev 35 0 R /Next 43 0 R >> endobj 35 0 obj << /Title 36 0 R /A 33 0 R /Parent 19 0 R /Prev 31 0 R /Next 39 0 R >> endobj 31 0 obj << /Title 32 0 R /A 29 0 R /Parent 19 0 R /Prev 27 0 R /Next 35 0 R >> endobj 27 0 obj << /Title 28 0 R /A 25 0 R /Parent 19 0 R /Prev 23 0 R /Next 31 0 R >> endobj 23 0 obj << /Title 24 0 R /A 21 0 R /Parent 19 0 R /Next 27 0 R >> endobj 19 0 obj << /Title 20 0 R /A 17 0 R /Parent 1640 0 R /Prev 7 0 R /Next 115 0 R /First 23 0 R /Last 111 0 R /Count -23 >> endobj 15 0 obj << /Title 16 0 R /A 13 0 R /Parent 7 0 R /Prev 11 0 R >> endobj 11 0 obj << /Title 12 0 R /A 9 0 R /Parent 7 0 R /Next 15 0 R >> endobj 7 0 obj << /Title 8 0 R /A 5 0 R /Parent 1640 0 R /Next 19 0 R /First 11 0 R /Last 15 0 R /Count -2 >> endobj 1641 0 obj << /Names [(Doc-Start) 130 0 R (chapter*.1) 172 0 R (chapter.1) 6 0 R (chapter.2) 18 0 R (chapter.3) 114 0 R (page.1) 181 0 R] /Limits [(Doc-Start) (page.1)] >> endobj 1642 0 obj << /Names [(page.10) 228 0 R (page.11) 233 0 R (page.12) 237 0 R (page.13) 242 0 R (page.14) 246 0 R (page.15) 252 0 R] /Limits [(page.10) (page.15)] >> endobj 1643 0 obj << /Names [(page.16) 259 0 R (page.17) 263 0 R (page.18) 267 0 R (page.19) 271 0 R (page.2) 186 0 R (page.20) 275 0 R] /Limits [(page.16) (page.20)] >> endobj 1644 0 obj << /Names [(page.21) 279 0 R (page.22) 285 0 R (page.23) 289 0 R (page.24) 293 0 R (page.25) 299 0 R (page.26) 303 0 R] /Limits [(page.21) (page.26)] >> endobj 1645 0 obj << /Names [(page.27) 307 0 R (page.28) 312 0 R (page.29) 316 0 R (page.3) 190 0 R (page.30) 320 0 R (page.31) 326 0 R] /Limits [(page.27) (page.31)] >> endobj 1646 0 obj << /Names [(page.32) 330 0 R (page.33) 334 0 R (page.34) 339 0 R (page.35) 343 0 R (page.36) 348 0 R (page.37) 352 0 R] /Limits [(page.32) (page.37)] >> endobj 1647 0 obj << /Names [(page.38) 356 0 R (page.39) 362 0 R (page.4) 197 0 R (page.40) 375 0 R (page.41) 389 0 R (page.42) 402 0 R] /Limits [(page.38) (page.42)] >> endobj 1648 0 obj << /Names [(page.43) 416 0 R (page.44) 428 0 R (page.45) 438 0 R (page.46) 463 0 R (page.47) 476 0 R (page.48) 501 0 R] /Limits [(page.43) (page.48)] >> endobj 1649 0 obj << /Names [(page.49) 512 0 R (page.5) 201 0 R (page.50) 525 0 R (page.51) 537 0 R (page.52) 552 0 R (page.53) 566 0 R] /Limits [(page.49) (page.53)] >> endobj 1650 0 obj << /Names [(page.54) 582 0 R (page.55) 594 0 R (page.56) 610 0 R (page.57) 627 0 R (page.58) 645 0 R (page.59) 663 0 R] /Limits [(page.54) (page.59)] >> endobj 1651 0 obj << /Names [(page.6) 209 0 R (page.60) 679 0 R (page.61) 698 0 R (page.62) 713 0 R (page.63) 726 0 R (page.64) 741 0 R] /Limits [(page.6) (page.64)] >> endobj 1652 0 obj << /Names [(page.65) 755 0 R (page.66) 771 0 R (page.67) 785 0 R (page.68) 800 0 R (page.69) 813 0 R (page.7) 213 0 R] /Limits [(page.65) (page.7)] >> endobj 1653 0 obj << /Names [(page.70) 827 0 R (page.71) 844 0 R (page.72) 863 0 R (page.73) 878 0 R (page.74) 892 0 R (page.75) 905 0 R] /Limits [(page.70) (page.75)] >> endobj 1654 0 obj << /Names [(page.76) 923 0 R (page.77) 938 0 R (page.78) 955 0 R (page.79) 972 0 R (page.8) 218 0 R (page.80) 982 0 R] /Limits [(page.76) (page.80)] >> endobj 1655 0 obj << /Names [(page.81) 993 0 R (page.82) 1007 0 R (page.83) 1018 0 R (page.84) 1031 0 R (page.85) 1043 0 R (page.86) 1053 0 R] /Limits [(page.81) (page.86)] >> endobj 1656 0 obj << /Names [(page.87) 1059 0 R (page.88) 1064 0 R (page.89) 1121 0 R (page.9) 223 0 R (page.90) 1126 0 R (page.91) 1169 0 R] /Limits [(page.87) (page.91)] >> endobj 1657 0 obj << /Names [(page.92) 1228 0 R (page.93) 1291 0 R (page.94) 1349 0 R (page.95) 1412 0 R (page.96) 1478 0 R (page.97) 1538 0 R] /Limits [(page.92) (page.97)] >> endobj 1658 0 obj << /Names [(page.98) 1600 0 R (page.99) 1606 0 R (page.i) 129 0 R (page.ii) 138 0 R (paragraph*.92) 506 0 R (paragraph*.93) 507 0 R] /Limits [(page.98) (paragraph*.93)] >> endobj 1659 0 obj << /Names [(section*.10) 367 0 R (section*.100) 518 0 R (section*.101) 519 0 R (section*.102) 526 0 R (section*.103) 527 0 R (section*.104) 528 0 R] /Limits [(section*.10) (section*.104)] >> endobj 1660 0 obj << /Names [(section*.105) 529 0 R (section*.106) 530 0 R (section*.107) 531 0 R (section*.109) 540 0 R (section*.11) 368 0 R (section*.110) 541 0 R] /Limits [(section*.105) (section*.110)] >> endobj 1661 0 obj << /Names [(section*.111) 542 0 R (section*.112) 543 0 R (section*.113) 544 0 R (section*.114) 545 0 R (section*.116) 555 0 R (section*.117) 556 0 R] /Limits [(section*.111) (section*.117)] >> endobj 1662 0 obj << /Names [(section*.118) 557 0 R (section*.119) 558 0 R (section*.12) 369 0 R (section*.120) 559 0 R (section*.122) 568 0 R (section*.123) 569 0 R] /Limits [(section*.118) (section*.123)] >> endobj 1663 0 obj << /Names [(section*.124) 570 0 R (section*.125) 571 0 R (section*.126) 572 0 R (section*.128) 575 0 R (section*.129) 576 0 R (section*.13) 370 0 R] /Limits [(section*.124) (section*.13)] >> endobj 1664 0 obj << /Names [(section*.130) 583 0 R (section*.131) 584 0 R (section*.132) 585 0 R (section*.134) 588 0 R (section*.135) 589 0 R (section*.136) 590 0 R] /Limits [(section*.130) (section*.136)] >> endobj 1665 0 obj << /Names [(section*.137) 595 0 R (section*.138) 596 0 R (section*.139) 597 0 R (section*.14) 376 0 R (section*.140) 598 0 R (section*.141) 599 0 R] /Limits [(section*.137) (section*.141)] >> endobj 1666 0 obj << /Names [(section*.142) 600 0 R (section*.143) 601 0 R (section*.144) 602 0 R (section*.145) 603 0 R (section*.146) 604 0 R (section*.147) 605 0 R] /Limits [(section*.142) (section*.147)] >> endobj 1667 0 obj << /Names [(section*.148) 606 0 R (section*.149) 611 0 R (section*.15) 377 0 R (section*.150) 612 0 R (section*.151) 613 0 R (section*.152) 614 0 R] /Limits [(section*.148) (section*.152)] >> endobj 1668 0 obj << /Names [(section*.153) 615 0 R (section*.154) 616 0 R (section*.155) 617 0 R (section*.156) 618 0 R (section*.157) 619 0 R (section*.158) 620 0 R] /Limits [(section*.153) (section*.158)] >> endobj 1669 0 obj << /Names [(section*.159) 621 0 R (section*.16) 378 0 R (section*.160) 622 0 R (section*.161) 623 0 R (section*.162) 628 0 R (section*.163) 629 0 R] /Limits [(section*.159) (section*.163)] >> endobj 1670 0 obj << /Names [(section*.164) 630 0 R (section*.165) 631 0 R (section*.166) 632 0 R (section*.167) 633 0 R (section*.168) 634 0 R (section*.169) 635 0 R] /Limits [(section*.164) (section*.169)] >> endobj 1671 0 obj << /Names [(section*.17) 379 0 R (section*.170) 636 0 R (section*.171) 637 0 R (section*.172) 638 0 R (section*.173) 639 0 R (section*.174) 640 0 R] /Limits [(section*.17) (section*.174)] >> endobj 1672 0 obj << /Names [(section*.175) 646 0 R (section*.176) 647 0 R (section*.177) 648 0 R (section*.178) 649 0 R (section*.179) 650 0 R (section*.18) 380 0 R] /Limits [(section*.175) (section*.18)] >> endobj 1673 0 obj << /Names [(section*.180) 651 0 R (section*.181) 652 0 R (section*.182) 653 0 R (section*.183) 654 0 R (section*.184) 655 0 R (section*.185) 656 0 R] /Limits [(section*.180) (section*.185)] >> endobj 1674 0 obj << /Names [(section*.186) 657 0 R (section*.187) 664 0 R (section*.189) 667 0 R (section*.19) 381 0 R (section*.190) 668 0 R (section*.191) 669 0 R] /Limits [(section*.186) (section*.191)] >> endobj 1675 0 obj << /Names [(section*.192) 670 0 R (section*.193) 671 0 R (section*.194) 672 0 R (section*.195) 673 0 R (section*.196) 680 0 R (section*.197) 681 0 R] /Limits [(section*.192) (section*.197)] >> endobj 1676 0 obj << /Names [(section*.198) 682 0 R (section*.199) 683 0 R (section*.2) 182 0 R (section*.20) 382 0 R (section*.200) 684 0 R (section*.201) 685 0 R] /Limits [(section*.198) (section*.201)] >> endobj 1677 0 obj << /Names [(section*.202) 686 0 R (section*.203) 687 0 R (section*.205) 690 0 R (section*.206) 691 0 R (section*.207) 699 0 R (section*.208) 700 0 R] /Limits [(section*.202) (section*.208)] >> endobj 1678 0 obj << /Names [(section*.209) 701 0 R (section*.21) 383 0 R (section*.210) 702 0 R (section*.211) 703 0 R (section*.212) 704 0 R (section*.214) 707 0 R] /Limits [(section*.209) (section*.214)] >> endobj 1679 0 obj << /Names [(section*.215) 708 0 R (section*.216) 709 0 R (section*.217) 714 0 R (section*.218) 715 0 R (section*.219) 716 0 R (section*.22) 390 0 R] /Limits [(section*.215) (section*.22)] >> endobj 1680 0 obj << /Names [(section*.220) 717 0 R (section*.221) 718 0 R (section*.222) 719 0 R (section*.223) 720 0 R (section*.224) 721 0 R (section*.225) 722 0 R] /Limits [(section*.220) (section*.225)] >> endobj 1681 0 obj << /Names [(section*.226) 727 0 R (section*.227) 728 0 R (section*.228) 729 0 R (section*.229) 730 0 R (section*.230) 731 0 R (section*.231) 732 0 R] /Limits [(section*.226) (section*.231)] >> endobj 1682 0 obj << /Names [(section*.232) 742 0 R (section*.234) 745 0 R (section*.235) 746 0 R (section*.236) 747 0 R (section*.237) 748 0 R (section*.238) 750 0 R] /Limits [(section*.232) (section*.238)] >> endobj 1683 0 obj << /Names [(section*.239) 751 0 R (section*.24) 393 0 R (section*.240) 756 0 R (section*.241) 757 0 R (section*.242) 758 0 R (section*.243) 759 0 R] /Limits [(section*.239) (section*.243)] >> endobj 1684 0 obj << /Names [(section*.244) 760 0 R (section*.245) 761 0 R (section*.246) 762 0 R (section*.247) 763 0 R (section*.248) 764 0 R (section*.249) 765 0 R] /Limits [(section*.244) (section*.249)] >> endobj 1685 0 obj << /Names [(section*.25) 394 0 R (section*.250) 772 0 R (section*.251) 773 0 R (section*.252) 774 0 R (section*.253) 775 0 R (section*.254) 776 0 R] /Limits [(section*.25) (section*.254)] >> endobj 1686 0 obj << /Names [(section*.255) 777 0 R (section*.256) 778 0 R (section*.257) 779 0 R (section*.258) 780 0 R (section*.26) 395 0 R (section*.260) 788 0 R] /Limits [(section*.255) (section*.260)] >> endobj 1687 0 obj << /Names [(section*.261) 789 0 R (section*.262) 790 0 R (section*.263) 791 0 R (section*.264) 792 0 R (section*.265) 793 0 R (section*.266) 794 0 R] /Limits [(section*.261) (section*.266)] >> endobj 1688 0 obj << /Names [(section*.267) 795 0 R (section*.269) 803 0 R (section*.27) 396 0 R (section*.270) 804 0 R (section*.271) 805 0 R (section*.272) 806 0 R] /Limits [(section*.267) (section*.272)] >> endobj 1689 0 obj << /Names [(section*.273) 807 0 R (section*.274) 808 0 R (section*.275) 809 0 R (section*.276) 814 0 R (section*.277) 815 0 R (section*.278) 816 0 R] /Limits [(section*.273) (section*.278)] >> endobj 1690 0 obj << /Names [(section*.279) 817 0 R (section*.28) 397 0 R (section*.280) 818 0 R (section*.281) 819 0 R (section*.282) 820 0 R (section*.283) 828 0 R] /Limits [(section*.279) (section*.283)] >> endobj 1691 0 obj << /Names [(section*.284) 830 0 R (section*.285) 831 0 R (section*.286) 832 0 R (section*.287) 833 0 R (section*.288) 834 0 R (section*.289) 835 0 R] /Limits [(section*.284) (section*.289)] >> endobj 1692 0 obj << /Names [(section*.29) 403 0 R (section*.290) 836 0 R (section*.291) 837 0 R (section*.292) 838 0 R (section*.293) 845 0 R (section*.294) 846 0 R] /Limits [(section*.29) (section*.294)] >> endobj 1693 0 obj << /Names [(section*.295) 847 0 R (section*.296) 848 0 R (section*.297) 849 0 R (section*.298) 850 0 R (section*.299) 851 0 R (section*.30) 404 0 R] /Limits [(section*.295) (section*.30)] >> endobj 1694 0 obj << /Names [(section*.300) 852 0 R (section*.301) 853 0 R (section*.302) 854 0 R (section*.303) 855 0 R (section*.304) 856 0 R (section*.306) 867 0 R] /Limits [(section*.300) (section*.306)] >> endobj 1695 0 obj << /Names [(section*.307) 868 0 R (section*.308) 869 0 R (section*.309) 870 0 R (section*.31) 405 0 R (section*.310) 871 0 R (section*.311) 872 0 R] /Limits [(section*.307) (section*.311)] >> endobj 1696 0 obj << /Names [(section*.312) 873 0 R (section*.313) 874 0 R (section*.314) 879 0 R (section*.315) 880 0 R (section*.316) 881 0 R (section*.317) 882 0 R] /Limits [(section*.312) (section*.317)] >> endobj 1697 0 obj << /Names [(section*.318) 883 0 R (section*.319) 884 0 R (section*.32) 406 0 R (section*.320) 885 0 R (section*.321) 886 0 R (section*.322) 893 0 R] /Limits [(section*.318) (section*.322)] >> endobj 1698 0 obj << /Names [(section*.323) 894 0 R (section*.324) 895 0 R (section*.325) 896 0 R (section*.327) 899 0 R (section*.328) 900 0 R (section*.329) 901 0 R] /Limits [(section*.323) (section*.329)] >> endobj 1699 0 obj << /Names [(section*.33) 407 0 R (section*.330) 906 0 R (section*.331) 907 0 R (section*.332) 908 0 R (section*.333) 909 0 R (section*.334) 910 0 R] /Limits [(section*.33) (section*.334)] >> endobj 1700 0 obj << /Names [(section*.335) 911 0 R (section*.336) 912 0 R (section*.337) 913 0 R (section*.338) 914 0 R (section*.339) 915 0 R (section*.34) 408 0 R] /Limits [(section*.335) (section*.34)] >> endobj 1701 0 obj << /Names [(section*.340) 916 0 R (section*.342) 926 0 R (section*.343) 927 0 R (section*.344) 928 0 R (section*.345) 929 0 R (section*.346) 930 0 R] /Limits [(section*.340) (section*.346)] >> endobj 1702 0 obj << /Names [(section*.347) 931 0 R (section*.348) 932 0 R (section*.349) 933 0 R (section*.35) 409 0 R (section*.350) 934 0 R (section*.351) 939 0 R] /Limits [(section*.347) (section*.351)] >> endobj 1703 0 obj << /Names [(section*.352) 940 0 R (section*.353) 941 0 R (section*.354) 942 0 R (section*.355) 943 0 R (section*.356) 944 0 R (section*.357) 945 0 R] /Limits [(section*.352) (section*.357)] >> endobj 1704 0 obj << /Names [(section*.358) 946 0 R (section*.359) 947 0 R (section*.36) 410 0 R (section*.360) 948 0 R (section*.362) 958 0 R (section*.363) 959 0 R] /Limits [(section*.358) (section*.363)] >> endobj 1705 0 obj << /Names [(section*.364) 960 0 R (section*.365) 961 0 R (section*.366) 962 0 R (section*.367) 963 0 R (section*.368) 964 0 R (section*.369) 965 0 R] /Limits [(section*.364) (section*.369)] >> endobj 1706 0 obj << /Names [(section*.37) 411 0 R (section*.370) 966 0 R (section*.371) 973 0 R (section*.372) 974 0 R (section*.373) 976 0 R (section*.374) 977 0 R] /Limits [(section*.37) (section*.374)] >> endobj 1707 0 obj << /Names [(section*.375) 978 0 R (section*.376) 983 0 R (section*.377) 984 0 R (section*.378) 985 0 R (section*.379) 986 0 R (section*.38) 418 0 R] /Limits [(section*.375) (section*.38)] >> endobj 1708 0 obj << /Names [(section*.380) 987 0 R (section*.381) 988 0 R (section*.382) 989 0 R (section*.383) 994 0 R (section*.384) 995 0 R (section*.385) 996 0 R] /Limits [(section*.380) (section*.385)] >> endobj 1709 0 obj << /Names [(section*.386) 997 0 R (section*.387) 998 0 R (section*.388) 999 0 R (section*.389) 1000 0 R (section*.39) 419 0 R (section*.390) 1001 0 R] /Limits [(section*.386) (section*.390)] >> endobj 1710 0 obj << /Names [(section*.391) 1002 0 R (section*.392) 1008 0 R (section*.393) 1009 0 R (section*.394) 1010 0 R (section*.395) 1011 0 R (section*.396) 1012 0 R] /Limits [(section*.391) (section*.396)] >> endobj 1711 0 obj << /Names [(section*.397) 1013 0 R (section*.398) 1014 0 R (section*.399) 1019 0 R (section*.40) 420 0 R (section*.400) 1020 0 R (section*.401) 1021 0 R] /Limits [(section*.397) (section*.401)] >> endobj 1712 0 obj << /Names [(section*.402) 1022 0 R (section*.403) 1023 0 R (section*.404) 1024 0 R (section*.405) 1025 0 R (section*.406) 1032 0 R (section*.407) 1033 0 R] /Limits [(section*.402) (section*.407)] >> endobj 1713 0 obj << /Names [(section*.408) 1034 0 R (section*.409) 1035 0 R (section*.41) 421 0 R (section*.411) 1038 0 R (section*.412) 1039 0 R (section*.413) 1044 0 R] /Limits [(section*.408) (section*.413)] >> endobj 1714 0 obj << /Names [(section*.414) 1045 0 R (section*.415) 1046 0 R (section*.416) 1047 0 R (section*.417) 1048 0 R (section*.418) 1049 0 R (section*.419) 1054 0 R] /Limits [(section*.414) (section*.419)] >> endobj 1715 0 obj << /Names [(section*.42) 422 0 R (section*.420) 1055 0 R (section*.421) 118 0 R (section*.422) 122 0 R (section*.43) 429 0 R (section*.44) 430 0 R] /Limits [(section*.42) (section*.44)] >> endobj 1716 0 obj << /Names [(section*.45) 431 0 R (section*.46) 432 0 R (section*.47) 433 0 R (section*.48) 440 0 R (section*.49) 441 0 R (section*.50) 442 0 R] /Limits [(section*.45) (section*.50)] >> endobj 1717 0 obj << /Names [(section*.51) 443 0 R (section*.52) 444 0 R (section*.53) 445 0 R (section*.54) 446 0 R (section*.55) 447 0 R (section*.56) 448 0 R] /Limits [(section*.51) (section*.56)] >> endobj 1718 0 obj << /Names [(section*.57) 449 0 R (section*.58) 450 0 R (section*.59) 451 0 R (section*.60) 452 0 R (section*.61) 453 0 R (section*.62) 454 0 R] /Limits [(section*.57) (section*.62)] >> endobj 1719 0 obj << /Names [(section*.64) 466 0 R (section*.65) 467 0 R (section*.66) 468 0 R (section*.67) 469 0 R (section*.68) 470 0 R (section*.69) 471 0 R] /Limits [(section*.64) (section*.69)] >> endobj 1720 0 obj << /Names [(section*.70) 472 0 R (section*.72) 479 0 R (section*.73) 480 0 R (section*.74) 481 0 R (section*.75) 482 0 R (section*.76) 483 0 R] /Limits [(section*.70) (section*.76)] >> endobj 1721 0 obj << /Names [(section*.77) 484 0 R (section*.78) 485 0 R (section*.79) 486 0 R (section*.8) 365 0 R (section*.80) 487 0 R (section*.81) 488 0 R] /Limits [(section*.77) (section*.81)] >> endobj 1722 0 obj << /Names [(section*.82) 489 0 R (section*.83) 490 0 R (section*.84) 491 0 R (section*.85) 492 0 R (section*.86) 493 0 R (section*.87) 494 0 R] /Limits [(section*.82) (section*.87)] >> endobj 1723 0 obj << /Names [(section*.88) 495 0 R (section*.89) 502 0 R (section*.9) 366 0 R (section*.90) 503 0 R (section*.94) 508 0 R (section*.95) 513 0 R] /Limits [(section*.88) (section*.95)] >> endobj 1724 0 obj << /Names [(section*.96) 514 0 R (section*.97) 515 0 R (section*.98) 516 0 R (section*.99) 517 0 R (section.1.1) 10 0 R (section.1.2) 14 0 R] /Limits [(section*.96) (section.1.2)] >> endobj 1725 0 obj << /Names [(section.2.1) 22 0 R (section.2.10) 58 0 R (section.2.11) 62 0 R (section.2.12) 66 0 R (section.2.13) 70 0 R (section.2.14) 74 0 R] /Limits [(section.2.1) (section.2.14)] >> endobj 1726 0 obj << /Names [(section.2.15) 78 0 R (section.2.16) 82 0 R (section.2.17) 86 0 R (section.2.18) 90 0 R (section.2.19) 94 0 R (section.2.2) 26 0 R] /Limits [(section.2.15) (section.2.2)] >> endobj 1727 0 obj << /Names [(section.2.20) 98 0 R (section.2.21) 102 0 R (section.2.22) 106 0 R (section.2.23) 110 0 R (section.2.3) 30 0 R (section.2.4) 34 0 R] /Limits [(section.2.20) (section.2.4)] >> endobj 1728 0 obj << /Names [(section.2.5) 38 0 R (section.2.6) 42 0 R (section.2.7) 46 0 R (section.2.8) 50 0 R (section.2.9) 54 0 R (subsection.1.1.1) 192 0 R] /Limits [(section.2.5) (subsection.1.1.1)] >> endobj 1729 0 obj << /Names [(subsection.1.1.2) 202 0 R (subsection.1.1.3) 205 0 R (subsection.1.1.4) 214 0 R (subsection.1.1.5) 219 0 R (subsection.1.1.6) 229 0 R (subsection.1.1.7) 238 0 R] /Limits [(subsection.1.1.2) (subsection.1.1.7)] >> endobj 1730 0 obj << /Names [(subsection.1.1.8) 247 0 R (subsection.1.1.9) 248 0 R (subsection.1.2.1) 253 0 R (subsection.1.2.2) 294 0 R (subsection.1.2.3) 321 0 R (subsection.1.2.4) 344 0 R] /Limits [(subsection.1.1.8) (subsection.1.2.4)] >> endobj 1731 0 obj << /Names [(subsection.2.1.1) 363 0 R (subsection.2.10.1) 688 0 R (subsection.2.11.1) 705 0 R (subsection.2.12.1) 743 0 R (subsection.2.13.1) 749 0 R (subsection.2.14.1) 786 0 R] /Limits [(subsection.2.1.1) (subsection.2.14.1)] >> endobj 1732 0 obj << /Names [(subsection.2.15.1) 801 0 R (subsection.2.16.1) 829 0 R (subsection.2.17.1) 864 0 R (subsection.2.18.1) 865 0 R (subsection.2.19.1) 897 0 R (subsection.2.2.1) 391 0 R] /Limits [(subsection.2.15.1) (subsection.2.2.1)] >> endobj 1733 0 obj << /Names [(subsection.2.20.1) 924 0 R (subsection.2.21.1) 956 0 R (subsection.2.22.1) 975 0 R (subsection.2.23.1) 1036 0 R (subsection.2.3.1) 417 0 R (subsection.2.4.1) 439 0 R] /Limits [(subsection.2.20.1) (subsection.2.4.1)] >> endobj 1734 0 obj << /Names [(subsection.2.5.1) 464 0 R (subsection.2.6.1) 477 0 R (subsection.2.7.1) 504 0 R (subsection.2.7.2) 538 0 R (subsection.2.7.3) 553 0 R (subsection.2.7.4) 560 0 R] /Limits [(subsection.2.5.1) (subsection.2.7.4)] >> endobj 1735 0 obj << /Names [(subsection.2.7.5) 573 0 R (subsection.2.8.1) 586 0 R (subsection.2.9.1) 665 0 R (subsubsection*.108) 539 0 R (subsubsection*.115) 554 0 R (subsubsection*.121) 567 0 R] /Limits [(subsection.2.7.5) (subsubsection*.121)] >> endobj 1736 0 obj << /Names [(subsubsection*.127) 574 0 R (subsubsection*.133) 587 0 R (subsubsection*.188) 666 0 R (subsubsection*.204) 689 0 R (subsubsection*.213) 706 0 R (subsubsection*.23) 392 0 R] /Limits [(subsubsection*.127) (subsubsection*.23)] >> endobj 1737 0 obj << /Names [(subsubsection*.233) 744 0 R (subsubsection*.259) 787 0 R (subsubsection*.268) 802 0 R (subsubsection*.3) 254 0 R (subsubsection*.305) 866 0 R (subsubsection*.326) 898 0 R] /Limits [(subsubsection*.233) (subsubsection*.326)] >> endobj 1738 0 obj << /Names [(subsubsection*.341) 925 0 R (subsubsection*.361) 957 0 R (subsubsection*.4) 280 0 R (subsubsection*.410) 1037 0 R (subsubsection*.5) 295 0 R (subsubsection*.6) 322 0 R] /Limits [(subsubsection*.341) (subsubsection*.6)] >> endobj 1739 0 obj << /Names [(subsubsection*.63) 465 0 R (subsubsection*.7) 364 0 R (subsubsection*.71) 478 0 R (subsubsection*.91) 505 0 R] /Limits [(subsubsection*.63) (subsubsection*.91)] >> endobj 1740 0 obj << /Kids [1641 0 R 1642 0 R 1643 0 R 1644 0 R 1645 0 R 1646 0 R] /Limits [(Doc-Start) (page.37)] >> endobj 1741 0 obj << /Kids [1647 0 R 1648 0 R 1649 0 R 1650 0 R 1651 0 R 1652 0 R] /Limits [(page.38) (page.7)] >> endobj 1742 0 obj << /Kids [1653 0 R 1654 0 R 1655 0 R 1656 0 R 1657 0 R 1658 0 R] /Limits [(page.70) (paragraph*.93)] >> endobj 1743 0 obj << /Kids [1659 0 R 1660 0 R 1661 0 R 1662 0 R 1663 0 R 1664 0 R] /Limits [(section*.10) (section*.136)] >> endobj 1744 0 obj << /Kids [1665 0 R 1666 0 R 1667 0 R 1668 0 R 1669 0 R 1670 0 R] /Limits [(section*.137) (section*.169)] >> endobj 1745 0 obj << /Kids [1671 0 R 1672 0 R 1673 0 R 1674 0 R 1675 0 R 1676 0 R] /Limits [(section*.17) (section*.201)] >> endobj 1746 0 obj << /Kids [1677 0 R 1678 0 R 1679 0 R 1680 0 R 1681 0 R 1682 0 R] /Limits [(section*.202) (section*.238)] >> endobj 1747 0 obj << /Kids [1683 0 R 1684 0 R 1685 0 R 1686 0 R 1687 0 R 1688 0 R] /Limits [(section*.239) (section*.272)] >> endobj 1748 0 obj << /Kids [1689 0 R 1690 0 R 1691 0 R 1692 0 R 1693 0 R 1694 0 R] /Limits [(section*.273) (section*.306)] >> endobj 1749 0 obj << /Kids [1695 0 R 1696 0 R 1697 0 R 1698 0 R 1699 0 R 1700 0 R] /Limits [(section*.307) (section*.34)] >> endobj 1750 0 obj << /Kids [1701 0 R 1702 0 R 1703 0 R 1704 0 R 1705 0 R 1706 0 R] /Limits [(section*.340) (section*.374)] >> endobj 1751 0 obj << /Kids [1707 0 R 1708 0 R 1709 0 R 1710 0 R 1711 0 R 1712 0 R] /Limits [(section*.375) (section*.407)] >> endobj 1752 0 obj << /Kids [1713 0 R 1714 0 R 1715 0 R 1716 0 R 1717 0 R 1718 0 R] /Limits [(section*.408) (section*.62)] >> endobj 1753 0 obj << /Kids [1719 0 R 1720 0 R 1721 0 R 1722 0 R 1723 0 R 1724 0 R] /Limits [(section*.64) (section.1.2)] >> endobj 1754 0 obj << /Kids [1725 0 R 1726 0 R 1727 0 R 1728 0 R 1729 0 R 1730 0 R] /Limits [(section.2.1) (subsection.1.2.4)] >> endobj 1755 0 obj << /Kids [1731 0 R 1732 0 R 1733 0 R 1734 0 R 1735 0 R 1736 0 R] /Limits [(subsection.2.1.1) (subsubsection*.23)] >> endobj 1756 0 obj << /Kids [1737 0 R 1738 0 R 1739 0 R] /Limits [(subsubsection*.233) (subsubsection*.91)] >> endobj 1757 0 obj << /Kids [1740 0 R 1741 0 R 1742 0 R 1743 0 R 1744 0 R 1745 0 R] /Limits [(Doc-Start) (section*.201)] >> endobj 1758 0 obj << /Kids [1746 0 R 1747 0 R 1748 0 R 1749 0 R 1750 0 R 1751 0 R] /Limits [(section*.202) (section*.407)] >> endobj 1759 0 obj << /Kids [1752 0 R 1753 0 R 1754 0 R 1755 0 R 1756 0 R] /Limits [(section*.408) (subsubsection*.91)] >> endobj 1760 0 obj << /Kids [1757 0 R 1758 0 R 1759 0 R] /Limits [(Doc-Start) (subsubsection*.91)] >> endobj 1761 0 obj << /Dests 1760 0 R >> endobj 1762 0 obj << /Type /Catalog /Pages 1639 0 R /Outlines 1640 0 R /Names 1761 0 R /PageMode/UseOutlines/PageLabels << /Nums [0 << /S /r >> 2 << /S /r >> 4 << /S /D >> ] >> /OpenAction 125 0 R >> endobj 1763 0 obj << /Author (Galen Collins) /Title (Pymodbus Documentation) /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.3)/Keywords() /CreationDate (D:20110519084056-07'00') /ModDate (D:20110519084056-07'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX using libpoppler, Version 3.141592-1.40.3-2.2 (Web2C 7.5.6) kpathsea version 3.5.6) >> endobj xref 0 1764 0000000001 65535 f 0000000002 00000 f 0000000003 00000 f 0000000004 00000 f 0000000000 00000 f 0000000015 00000 n 0000013934 00000 n 0000449994 00000 n 0000000060 00000 n 0000000103 00000 n 0000013985 00000 n 0000449922 00000 n 0000000150 00000 n 0000000189 00000 n 0000037108 00000 n 0000449849 00000 n 0000000237 00000 n 0000000277 00000 n 0000084665 00000 n 0000449721 00000 n 0000000323 00000 n 0000000376 00000 n 0000084717 00000 n 0000449647 00000 n 0000000424 00000 n 0000000494 00000 n 0000091026 00000 n 0000449560 00000 n 0000000542 00000 n 0000000614 00000 n 0000095993 00000 n 0000449473 00000 n 0000000662 00000 n 0000000726 00000 n 0000098493 00000 n 0000449386 00000 n 0000000774 00000 n 0000000842 00000 n 0000104220 00000 n 0000449299 00000 n 0000000890 00000 n 0000000953 00000 n 0000104784 00000 n 0000449212 00000 n 0000001001 00000 n 0000001055 00000 n 0000110848 00000 n 0000449125 00000 n 0000001103 00000 n 0000001152 00000 n 0000125397 00000 n 0000449038 00000 n 0000001200 00000 n 0000001265 00000 n 0000138559 00000 n 0000448951 00000 n 0000001313 00000 n 0000001371 00000 n 0000141734 00000 n 0000448864 00000 n 0000001420 00000 n 0000001476 00000 n 0000144780 00000 n 0000448777 00000 n 0000001525 00000 n 0000001576 00000 n 0000152754 00000 n 0000448690 00000 n 0000001625 00000 n 0000001686 00000 n 0000153152 00000 n 0000448603 00000 n 0000001735 00000 n 0000001796 00000 n 0000158595 00000 n 0000448516 00000 n 0000001845 00000 n 0000001904 00000 n 0000163557 00000 n 0000448429 00000 n 0000001953 00000 n 0000002006 00000 n 0000169117 00000 n 0000448342 00000 n 0000002055 00000 n 0000002097 00000 n 0000172774 00000 n 0000448255 00000 n 0000002146 00000 n 0000002194 00000 n 0000175200 00000 n 0000448168 00000 n 0000002243 00000 n 0000002316 00000 n 0000180797 00000 n 0000448081 00000 n 0000002365 00000 n 0000002440 00000 n 0000185691 00000 n 0000447992 00000 n 0000002489 00000 n 0000002558 00000 n 0000191049 00000 n 0000447901 00000 n 0000002608 00000 n 0000002679 00000 n 0000194586 00000 n 0000447809 00000 n 0000002729 00000 n 0000002801 00000 n 0000209139 00000 n 0000447731 00000 n 0000002851 00000 n 0000002905 00000 n 0000214474 00000 n 0000447638 00000 n 0000002952 00000 n 0000002990 00000 n 0000225443 00000 n 0000447544 00000 n 0000003040 00000 n 0000003079 00000 n 0000233623 00000 n 0000447464 00000 n 0000003129 00000 n 0000003154 00000 n 0000003559 00000 n 0000003781 00000 n 0000003206 00000 n 0000003671 00000 n 0000003728 00000 n 0000443776 00000 n 0000443950 00000 n 0000444479 00000 n 0000444835 00000 n 0000004148 00000 n 0000003979 00000 n 0000003879 00000 n 0000004091 00000 n 0000006027 00000 n 0000006178 00000 n 0000006330 00000 n 0000006483 00000 n 0000006633 00000 n 0000006785 00000 n 0000006937 00000 n 0000007090 00000 n 0000007243 00000 n 0000007395 00000 n 0000007548 00000 n 0000007701 00000 n 0000007854 00000 n 0000008006 00000 n 0000008160 00000 n 0000008314 00000 n 0000008468 00000 n 0000008622 00000 n 0000008776 00000 n 0000008930 00000 n 0000009084 00000 n 0000009238 00000 n 0000009391 00000 n 0000009544 00000 n 0000009697 00000 n 0000009851 00000 n 0000010005 00000 n 0000010159 00000 n 0000010310 00000 n 0000010464 00000 n 0000010674 00000 n 0000005663 00000 n 0000004189 00000 n 0000010617 00000 n 0000444303 00000 n 0000443424 00000 n 0000011091 00000 n 0000010979 00000 n 0000010785 00000 n 0000011687 00000 n 0000011465 00000 n 0000011163 00000 n 0000011577 00000 n 0000011634 00000 n 0000012217 00000 n 0000012048 00000 n 0000011772 00000 n 0000012160 00000 n 0000014098 00000 n 0000013765 00000 n 0000012289 00000 n 0000013877 00000 n 0000444655 00000 n 0000014041 00000 n 0000444953 00000 n 0000015525 00000 n 0000015356 00000 n 0000014209 00000 n 0000015468 00000 n 0000017448 00000 n 0000017169 00000 n 0000015610 00000 n 0000017281 00000 n 0000017338 00000 n 0000443598 00000 n 0000443250 00000 n 0000017391 00000 n 0000019426 00000 n 0000019257 00000 n 0000017559 00000 n 0000019369 00000 n 0000021502 00000 n 0000021276 00000 n 0000019537 00000 n 0000021388 00000 n 0000021445 00000 n 0000023141 00000 n 0000022919 00000 n 0000021613 00000 n 0000023031 00000 n 0000023088 00000 n 0000024717 00000 n 0000024548 00000 n 0000023226 00000 n 0000024660 00000 n 0000445071 00000 n 0000026692 00000 n 0000026466 00000 n 0000024802 00000 n 0000026578 00000 n 0000026635 00000 n 0000028613 00000 n 0000028444 00000 n 0000026803 00000 n 0000028556 00000 n 0000031088 00000 n 0000030862 00000 n 0000028724 00000 n 0000030974 00000 n 0000031031 00000 n 0000032942 00000 n 0000032773 00000 n 0000031212 00000 n 0000032885 00000 n 0000035067 00000 n 0000034784 00000 n 0000033053 00000 n 0000034896 00000 n 0000034953 00000 n 0000035010 00000 n 0000037278 00000 n 0000036939 00000 n 0000035191 00000 n 0000037051 00000 n 0000037164 00000 n 0000037221 00000 n 0000445189 00000 n 0000039135 00000 n 0000038966 00000 n 0000037415 00000 n 0000039078 00000 n 0000041215 00000 n 0000041046 00000 n 0000039246 00000 n 0000041158 00000 n 0000043111 00000 n 0000042942 00000 n 0000041326 00000 n 0000043054 00000 n 0000045183 00000 n 0000045014 00000 n 0000043222 00000 n 0000045126 00000 n 0000047460 00000 n 0000047291 00000 n 0000045294 00000 n 0000047403 00000 n 0000049872 00000 n 0000049647 00000 n 0000047571 00000 n 0000049759 00000 n 0000049816 00000 n 0000445307 00000 n 0000051532 00000 n 0000051363 00000 n 0000049996 00000 n 0000051475 00000 n 0000053265 00000 n 0000053096 00000 n 0000051630 00000 n 0000053208 00000 n 0000055492 00000 n 0000055210 00000 n 0000053363 00000 n 0000055322 00000 n 0000055379 00000 n 0000055435 00000 n 0000057400 00000 n 0000057231 00000 n 0000055629 00000 n 0000057343 00000 n 0000059410 00000 n 0000059241 00000 n 0000057511 00000 n 0000059353 00000 n 0000061284 00000 n 0000061115 00000 n 0000059521 00000 n 0000061227 00000 n 0000445425 00000 n 0000063476 00000 n 0000063307 00000 n 0000061395 00000 n 0000063419 00000 n 0000065624 00000 n 0000065455 00000 n 0000063587 00000 n 0000065567 00000 n 0000067775 00000 n 0000067492 00000 n 0000065735 00000 n 0000067604 00000 n 0000067661 00000 n 0000067718 00000 n 0000069707 00000 n 0000069538 00000 n 0000067912 00000 n 0000069650 00000 n 0000071678 00000 n 0000071509 00000 n 0000069818 00000 n 0000071621 00000 n 0000073563 00000 n 0000073394 00000 n 0000071789 00000 n 0000073506 00000 n 0000445543 00000 n 0000075806 00000 n 0000075637 00000 n 0000073674 00000 n 0000075749 00000 n 0000077800 00000 n 0000077575 00000 n 0000075917 00000 n 0000077687 00000 n 0000077744 00000 n 0000079661 00000 n 0000079492 00000 n 0000077911 00000 n 0000079604 00000 n 0000081374 00000 n 0000081205 00000 n 0000079772 00000 n 0000081317 00000 n 0000082866 00000 n 0000082697 00000 n 0000081485 00000 n 0000082809 00000 n 0000084264 00000 n 0000084437 00000 n 0000085227 00000 n 0000084124 00000 n 0000082977 00000 n 0000084608 00000 n 0000084773 00000 n 0000084830 00000 n 0000084887 00000 n 0000084944 00000 n 0000085000 00000 n 0000085057 00000 n 0000085114 00000 n 0000085170 00000 n 0000445661 00000 n 0000088116 00000 n 0000087491 00000 n 0000085364 00000 n 0000087603 00000 n 0000087660 00000 n 0000087717 00000 n 0000087774 00000 n 0000087831 00000 n 0000087888 00000 n 0000087945 00000 n 0000088002 00000 n 0000088059 00000 n 0000090567 00000 n 0000090740 00000 n 0000091476 00000 n 0000090427 00000 n 0000088253 00000 n 0000090913 00000 n 0000090970 00000 n 0000091082 00000 n 0000091139 00000 n 0000091196 00000 n 0000091253 00000 n 0000091306 00000 n 0000091363 00000 n 0000091420 00000 n 0000095590 00000 n 0000093786 00000 n 0000093105 00000 n 0000091613 00000 n 0000093217 00000 n 0000093274 00000 n 0000093331 00000 n 0000093388 00000 n 0000093445 00000 n 0000093502 00000 n 0000093559 00000 n 0000093616 00000 n 0000093673 00000 n 0000093729 00000 n 0000095763 00000 n 0000096386 00000 n 0000095450 00000 n 0000093923 00000 n 0000095936 00000 n 0000096045 00000 n 0000096102 00000 n 0000096159 00000 n 0000096216 00000 n 0000096273 00000 n 0000096330 00000 n 0000097808 00000 n 0000097980 00000 n 0000098549 00000 n 0000097668 00000 n 0000096536 00000 n 0000098153 00000 n 0000098210 00000 n 0000098267 00000 n 0000098323 00000 n 0000098380 00000 n 0000098437 00000 n 0000103320 00000 n 0000101136 00000 n 0000100061 00000 n 0000098686 00000 n 0000100173 00000 n 0000100230 00000 n 0000100283 00000 n 0000100339 00000 n 0000100395 00000 n 0000100452 00000 n 0000100509 00000 n 0000100566 00000 n 0000100623 00000 n 0000100680 00000 n 0000100737 00000 n 0000100794 00000 n 0000100851 00000 n 0000100908 00000 n 0000100965 00000 n 0000101022 00000 n 0000101079 00000 n 0000445779 00000 n 0000103493 00000 n 0000103666 00000 n 0000103820 00000 n 0000103992 00000 n 0000104840 00000 n 0000103156 00000 n 0000101273 00000 n 0000104163 00000 n 0000104272 00000 n 0000104329 00000 n 0000104386 00000 n 0000104443 00000 n 0000104500 00000 n 0000104557 00000 n 0000104614 00000 n 0000104670 00000 n 0000104727 00000 n 0000107878 00000 n 0000106635 00000 n 0000104977 00000 n 0000106747 00000 n 0000106804 00000 n 0000106857 00000 n 0000106913 00000 n 0000106970 00000 n 0000107027 00000 n 0000107083 00000 n 0000107140 00000 n 0000107197 00000 n 0000107254 00000 n 0000107311 00000 n 0000107367 00000 n 0000107424 00000 n 0000107481 00000 n 0000107537 00000 n 0000107594 00000 n 0000107651 00000 n 0000107708 00000 n 0000107765 00000 n 0000107822 00000 n 0000110332 00000 n 0000110505 00000 n 0000111186 00000 n 0000110192 00000 n 0000108002 00000 n 0000110677 00000 n 0000110734 00000 n 0000110791 00000 n 0000110903 00000 n 0000110959 00000 n 0000111016 00000 n 0000111073 00000 n 0000111130 00000 n 0000113235 00000 n 0000112668 00000 n 0000111310 00000 n 0000112780 00000 n 0000112837 00000 n 0000112894 00000 n 0000112950 00000 n 0000113007 00000 n 0000113064 00000 n 0000113121 00000 n 0000113178 00000 n 0000116723 00000 n 0000116896 00000 n 0000114872 00000 n 0000114362 00000 n 0000113372 00000 n 0000114474 00000 n 0000114531 00000 n 0000114587 00000 n 0000114644 00000 n 0000114701 00000 n 0000114758 00000 n 0000114815 00000 n 0000119245 00000 n 0000119418 00000 n 0000117575 00000 n 0000116583 00000 n 0000115009 00000 n 0000117068 00000 n 0000117125 00000 n 0000117178 00000 n 0000117235 00000 n 0000117291 00000 n 0000117348 00000 n 0000117405 00000 n 0000117462 00000 n 0000117518 00000 n 0000445897 00000 n 0000119590 00000 n 0000119763 00000 n 0000120442 00000 n 0000119089 00000 n 0000117712 00000 n 0000119935 00000 n 0000119992 00000 n 0000120045 00000 n 0000120102 00000 n 0000120158 00000 n 0000120215 00000 n 0000120272 00000 n 0000120329 00000 n 0000120385 00000 n 0000122121 00000 n 0000122294 00000 n 0000123088 00000 n 0000121981 00000 n 0000120579 00000 n 0000122467 00000 n 0000122524 00000 n 0000122577 00000 n 0000122634 00000 n 0000122690 00000 n 0000122747 00000 n 0000122804 00000 n 0000122861 00000 n 0000122918 00000 n 0000122975 00000 n 0000123032 00000 n 0000124825 00000 n 0000124997 00000 n 0000125738 00000 n 0000124685 00000 n 0000123225 00000 n 0000125170 00000 n 0000125227 00000 n 0000125284 00000 n 0000125341 00000 n 0000125453 00000 n 0000125510 00000 n 0000125567 00000 n 0000125624 00000 n 0000125681 00000 n 0000128545 00000 n 0000127696 00000 n 0000125875 00000 n 0000127808 00000 n 0000127865 00000 n 0000127922 00000 n 0000127979 00000 n 0000128036 00000 n 0000128093 00000 n 0000128148 00000 n 0000128205 00000 n 0000128262 00000 n 0000128319 00000 n 0000128375 00000 n 0000128432 00000 n 0000128488 00000 n 0000131108 00000 n 0000130200 00000 n 0000128682 00000 n 0000130312 00000 n 0000130369 00000 n 0000130426 00000 n 0000130483 00000 n 0000130540 00000 n 0000130597 00000 n 0000130654 00000 n 0000130709 00000 n 0000130766 00000 n 0000130823 00000 n 0000130880 00000 n 0000130937 00000 n 0000130994 00000 n 0000131051 00000 n 0000133406 00000 n 0000132498 00000 n 0000131245 00000 n 0000132610 00000 n 0000132667 00000 n 0000132723 00000 n 0000132780 00000 n 0000132837 00000 n 0000132894 00000 n 0000132951 00000 n 0000133008 00000 n 0000133065 00000 n 0000133121 00000 n 0000133178 00000 n 0000133235 00000 n 0000133292 00000 n 0000133349 00000 n 0000446015 00000 n 0000136009 00000 n 0000135161 00000 n 0000133543 00000 n 0000135273 00000 n 0000135330 00000 n 0000135387 00000 n 0000135443 00000 n 0000135500 00000 n 0000135557 00000 n 0000135614 00000 n 0000135671 00000 n 0000135728 00000 n 0000135785 00000 n 0000135842 00000 n 0000135899 00000 n 0000135952 00000 n 0000138099 00000 n 0000138272 00000 n 0000139128 00000 n 0000137959 00000 n 0000136146 00000 n 0000138445 00000 n 0000138502 00000 n 0000138615 00000 n 0000138672 00000 n 0000138729 00000 n 0000138786 00000 n 0000138843 00000 n 0000138900 00000 n 0000138957 00000 n 0000139014 00000 n 0000139071 00000 n 0000140880 00000 n 0000141053 00000 n 0000142017 00000 n 0000140740 00000 n 0000139265 00000 n 0000141226 00000 n 0000141283 00000 n 0000141336 00000 n 0000141393 00000 n 0000141450 00000 n 0000141507 00000 n 0000141563 00000 n 0000141620 00000 n 0000141677 00000 n 0000141790 00000 n 0000141846 00000 n 0000141903 00000 n 0000141960 00000 n 0000143823 00000 n 0000143996 00000 n 0000144169 00000 n 0000145119 00000 n 0000143675 00000 n 0000142154 00000 n 0000144382 00000 n 0000144439 00000 n 0000144495 00000 n 0000144552 00000 n 0000144609 00000 n 0000144666 00000 n 0000144723 00000 n 0000144836 00000 n 0000144893 00000 n 0000144950 00000 n 0000145007 00000 n 0000145064 00000 n 0000147596 00000 n 0000146915 00000 n 0000145256 00000 n 0000147027 00000 n 0000147084 00000 n 0000147141 00000 n 0000147198 00000 n 0000147255 00000 n 0000147312 00000 n 0000147369 00000 n 0000147425 00000 n 0000147482 00000 n 0000147539 00000 n 0000150046 00000 n 0000149537 00000 n 0000147733 00000 n 0000149649 00000 n 0000149706 00000 n 0000149762 00000 n 0000149818 00000 n 0000149875 00000 n 0000149932 00000 n 0000149989 00000 n 0000446133 00000 n 0000151950 00000 n 0000152122 00000 n 0000152295 00000 n 0000152468 00000 n 0000153378 00000 n 0000151794 00000 n 0000150183 00000 n 0000152641 00000 n 0000152698 00000 n 0000152810 00000 n 0000152867 00000 n 0000152924 00000 n 0000152981 00000 n 0000153038 00000 n 0000153095 00000 n 0000153208 00000 n 0000153265 00000 n 0000153322 00000 n 0000155909 00000 n 0000155173 00000 n 0000153515 00000 n 0000155285 00000 n 0000155342 00000 n 0000155399 00000 n 0000155455 00000 n 0000155512 00000 n 0000155569 00000 n 0000155624 00000 n 0000155681 00000 n 0000155738 00000 n 0000155795 00000 n 0000155852 00000 n 0000157680 00000 n 0000157853 00000 n 0000158651 00000 n 0000157540 00000 n 0000156046 00000 n 0000158026 00000 n 0000158083 00000 n 0000158140 00000 n 0000158196 00000 n 0000158253 00000 n 0000158310 00000 n 0000158367 00000 n 0000158424 00000 n 0000158481 00000 n 0000158538 00000 n 0000163154 00000 n 0000161272 00000 n 0000160539 00000 n 0000158788 00000 n 0000160651 00000 n 0000160708 00000 n 0000160761 00000 n 0000160817 00000 n 0000160874 00000 n 0000160931 00000 n 0000160988 00000 n 0000161045 00000 n 0000161102 00000 n 0000161159 00000 n 0000161215 00000 n 0000163327 00000 n 0000164122 00000 n 0000163014 00000 n 0000161409 00000 n 0000163500 00000 n 0000163609 00000 n 0000163666 00000 n 0000163723 00000 n 0000163780 00000 n 0000163837 00000 n 0000163894 00000 n 0000163951 00000 n 0000164008 00000 n 0000164065 00000 n 0000166718 00000 n 0000166151 00000 n 0000164259 00000 n 0000166263 00000 n 0000166320 00000 n 0000166377 00000 n 0000166434 00000 n 0000166491 00000 n 0000166548 00000 n 0000166605 00000 n 0000166662 00000 n 0000446251 00000 n 0000168657 00000 n 0000168830 00000 n 0000169740 00000 n 0000168517 00000 n 0000166855 00000 n 0000169003 00000 n 0000169060 00000 n 0000169172 00000 n 0000169229 00000 n 0000169286 00000 n 0000169343 00000 n 0000169400 00000 n 0000169457 00000 n 0000169514 00000 n 0000169571 00000 n 0000169628 00000 n 0000169684 00000 n 0000171748 00000 n 0000171919 00000 n 0000172886 00000 n 0000171608 00000 n 0000169877 00000 n 0000172090 00000 n 0000172147 00000 n 0000172204 00000 n 0000172261 00000 n 0000172318 00000 n 0000172375 00000 n 0000172432 00000 n 0000172489 00000 n 0000172546 00000 n 0000172603 00000 n 0000172660 00000 n 0000172717 00000 n 0000172830 00000 n 0000174576 00000 n 0000174744 00000 n 0000174917 00000 n 0000175822 00000 n 0000174428 00000 n 0000173023 00000 n 0000175090 00000 n 0000175147 00000 n 0000175256 00000 n 0000175313 00000 n 0000175370 00000 n 0000175427 00000 n 0000175484 00000 n 0000175541 00000 n 0000175598 00000 n 0000175653 00000 n 0000175710 00000 n 0000175767 00000 n 0000178370 00000 n 0000177747 00000 n 0000175959 00000 n 0000177859 00000 n 0000177916 00000 n 0000177973 00000 n 0000178030 00000 n 0000178087 00000 n 0000178144 00000 n 0000178201 00000 n 0000178257 00000 n 0000178314 00000 n 0000180167 00000 n 0000180339 00000 n 0000181138 00000 n 0000180027 00000 n 0000178507 00000 n 0000180512 00000 n 0000180569 00000 n 0000180626 00000 n 0000180683 00000 n 0000180740 00000 n 0000180853 00000 n 0000180910 00000 n 0000180967 00000 n 0000181024 00000 n 0000181081 00000 n 0000183423 00000 n 0000182633 00000 n 0000181275 00000 n 0000182745 00000 n 0000182802 00000 n 0000182855 00000 n 0000182912 00000 n 0000182969 00000 n 0000183026 00000 n 0000183083 00000 n 0000183140 00000 n 0000183197 00000 n 0000183254 00000 n 0000183311 00000 n 0000183368 00000 n 0000446369 00000 n 0000185288 00000 n 0000185461 00000 n 0000186372 00000 n 0000185148 00000 n 0000183560 00000 n 0000185634 00000 n 0000185747 00000 n 0000185804 00000 n 0000185861 00000 n 0000185918 00000 n 0000185975 00000 n 0000186032 00000 n 0000186089 00000 n 0000186144 00000 n 0000186201 00000 n 0000186258 00000 n 0000186315 00000 n 0000188528 00000 n 0000187791 00000 n 0000186509 00000 n 0000187903 00000 n 0000187960 00000 n 0000188017 00000 n 0000188074 00000 n 0000188131 00000 n 0000188188 00000 n 0000188245 00000 n 0000188301 00000 n 0000188358 00000 n 0000188415 00000 n 0000188472 00000 n 0000190492 00000 n 0000190665 00000 n 0000190838 00000 n 0000191732 00000 n 0000190344 00000 n 0000188665 00000 n 0000190992 00000 n 0000191105 00000 n 0000191162 00000 n 0000191219 00000 n 0000191276 00000 n 0000191333 00000 n 0000191390 00000 n 0000191447 00000 n 0000191504 00000 n 0000191561 00000 n 0000191618 00000 n 0000191675 00000 n 0000194069 00000 n 0000194242 00000 n 0000194869 00000 n 0000193929 00000 n 0000191869 00000 n 0000194415 00000 n 0000194472 00000 n 0000194529 00000 n 0000194643 00000 n 0000194700 00000 n 0000194755 00000 n 0000194812 00000 n 0000197782 00000 n 0000197218 00000 n 0000195006 00000 n 0000197330 00000 n 0000197387 00000 n 0000197440 00000 n 0000197497 00000 n 0000197554 00000 n 0000197611 00000 n 0000197668 00000 n 0000197725 00000 n 0000200635 00000 n 0000199950 00000 n 0000197919 00000 n 0000200063 00000 n 0000200120 00000 n 0000200177 00000 n 0000200234 00000 n 0000200291 00000 n 0000200348 00000 n 0000200405 00000 n 0000200461 00000 n 0000200519 00000 n 0000200577 00000 n 0000446487 00000 n 0000203458 00000 n 0000202873 00000 n 0000200772 00000 n 0000202989 00000 n 0000203048 00000 n 0000203107 00000 n 0000203166 00000 n 0000203224 00000 n 0000203283 00000 n 0000203342 00000 n 0000203399 00000 n 0000206473 00000 n 0000205890 00000 n 0000203596 00000 n 0000206006 00000 n 0000206065 00000 n 0000206120 00000 n 0000206179 00000 n 0000206238 00000 n 0000206296 00000 n 0000206355 00000 n 0000206414 00000 n 0000208498 00000 n 0000208671 00000 n 0000209432 00000 n 0000208352 00000 n 0000206611 00000 n 0000208845 00000 n 0000208904 00000 n 0000208962 00000 n 0000209021 00000 n 0000209080 00000 n 0000209197 00000 n 0000209256 00000 n 0000209315 00000 n 0000209374 00000 n 0000211934 00000 n 0000211405 00000 n 0000209570 00000 n 0000211521 00000 n 0000211580 00000 n 0000211639 00000 n 0000211698 00000 n 0000211757 00000 n 0000211816 00000 n 0000211875 00000 n 0000213774 00000 n 0000213481 00000 n 0000212072 00000 n 0000213597 00000 n 0000213656 00000 n 0000213715 00000 n 0000214528 00000 n 0000214299 00000 n 0000213912 00000 n 0000214415 00000 n 0000446611 00000 n 0000215102 00000 n 0000214927 00000 n 0000214627 00000 n 0000215043 00000 n 0000217004 00000 n 0000217159 00000 n 0000217314 00000 n 0000217469 00000 n 0000217624 00000 n 0000217778 00000 n 0000217933 00000 n 0000218092 00000 n 0000218252 00000 n 0000218405 00000 n 0000218560 00000 n 0000218716 00000 n 0000218872 00000 n 0000219028 00000 n 0000219183 00000 n 0000219339 00000 n 0000219499 00000 n 0000219655 00000 n 0000219810 00000 n 0000219966 00000 n 0000220125 00000 n 0000220285 00000 n 0000220444 00000 n 0000220602 00000 n 0000220760 00000 n 0000220920 00000 n 0000221082 00000 n 0000221243 00000 n 0000221405 00000 n 0000221566 00000 n 0000221727 00000 n 0000221885 00000 n 0000222045 00000 n 0000222205 00000 n 0000222365 00000 n 0000222525 00000 n 0000222686 00000 n 0000222845 00000 n 0000223006 00000 n 0000223166 00000 n 0000223327 00000 n 0000223488 00000 n 0000223649 00000 n 0000223810 00000 n 0000223971 00000 n 0000224132 00000 n 0000224287 00000 n 0000224443 00000 n 0000224603 00000 n 0000224759 00000 n 0000224914 00000 n 0000225074 00000 n 0000225229 00000 n 0000225501 00000 n 0000216399 00000 n 0000215175 00000 n 0000225384 00000 n 0000444128 00000 n 0000226082 00000 n 0000225907 00000 n 0000225614 00000 n 0000226023 00000 n 0000227848 00000 n 0000227999 00000 n 0000228148 00000 n 0000228299 00000 n 0000228450 00000 n 0000228601 00000 n 0000228752 00000 n 0000228902 00000 n 0000229052 00000 n 0000229203 00000 n 0000229354 00000 n 0000229505 00000 n 0000229656 00000 n 0000229807 00000 n 0000229958 00000 n 0000230106 00000 n 0000230256 00000 n 0000230406 00000 n 0000230557 00000 n 0000230704 00000 n 0000230855 00000 n 0000231006 00000 n 0000231157 00000 n 0000231308 00000 n 0000231459 00000 n 0000231610 00000 n 0000231760 00000 n 0000231910 00000 n 0000232061 00000 n 0000232212 00000 n 0000232361 00000 n 0000232512 00000 n 0000232663 00000 n 0000232814 00000 n 0000232965 00000 n 0000233116 00000 n 0000233267 00000 n 0000233416 00000 n 0000235847 00000 n 0000233681 00000 n 0000227378 00000 n 0000226155 00000 n 0000233564 00000 n 0000235998 00000 n 0000236149 00000 n 0000236300 00000 n 0000236451 00000 n 0000236602 00000 n 0000236752 00000 n 0000236902 00000 n 0000237053 00000 n 0000237204 00000 n 0000237354 00000 n 0000237503 00000 n 0000237652 00000 n 0000237802 00000 n 0000237953 00000 n 0000238102 00000 n 0000238253 00000 n 0000238404 00000 n 0000238555 00000 n 0000238706 00000 n 0000238857 00000 n 0000239008 00000 n 0000239158 00000 n 0000239309 00000 n 0000239460 00000 n 0000239609 00000 n 0000239760 00000 n 0000239911 00000 n 0000240060 00000 n 0000240211 00000 n 0000240362 00000 n 0000240511 00000 n 0000240662 00000 n 0000240813 00000 n 0000240964 00000 n 0000241115 00000 n 0000241266 00000 n 0000241417 00000 n 0000241568 00000 n 0000241719 00000 n 0000241870 00000 n 0000242021 00000 n 0000242172 00000 n 0000242323 00000 n 0000242474 00000 n 0000242625 00000 n 0000242776 00000 n 0000242927 00000 n 0000243078 00000 n 0000243229 00000 n 0000243380 00000 n 0000243531 00000 n 0000243682 00000 n 0000243833 00000 n 0000243983 00000 n 0000246302 00000 n 0000244191 00000 n 0000235224 00000 n 0000233781 00000 n 0000244132 00000 n 0000246453 00000 n 0000246604 00000 n 0000246755 00000 n 0000246906 00000 n 0000247057 00000 n 0000247208 00000 n 0000247359 00000 n 0000247510 00000 n 0000247661 00000 n 0000247812 00000 n 0000247963 00000 n 0000248114 00000 n 0000248265 00000 n 0000248415 00000 n 0000248566 00000 n 0000248716 00000 n 0000248867 00000 n 0000249018 00000 n 0000249169 00000 n 0000249320 00000 n 0000249471 00000 n 0000249622 00000 n 0000249773 00000 n 0000249923 00000 n 0000250074 00000 n 0000250225 00000 n 0000250376 00000 n 0000250526 00000 n 0000250675 00000 n 0000250826 00000 n 0000250977 00000 n 0000251128 00000 n 0000251279 00000 n 0000251430 00000 n 0000251581 00000 n 0000251732 00000 n 0000251882 00000 n 0000252033 00000 n 0000252184 00000 n 0000252335 00000 n 0000252486 00000 n 0000252637 00000 n 0000252788 00000 n 0000252939 00000 n 0000253090 00000 n 0000253241 00000 n 0000253392 00000 n 0000253543 00000 n 0000253694 00000 n 0000253845 00000 n 0000253996 00000 n 0000254147 00000 n 0000254298 00000 n 0000254449 00000 n 0000254600 00000 n 0000254750 00000 n 0000254900 00000 n 0000255049 00000 n 0000257349 00000 n 0000255255 00000 n 0000245643 00000 n 0000244291 00000 n 0000255196 00000 n 0000446736 00000 n 0000257500 00000 n 0000257651 00000 n 0000257802 00000 n 0000257953 00000 n 0000258104 00000 n 0000258255 00000 n 0000258406 00000 n 0000258556 00000 n 0000258705 00000 n 0000258854 00000 n 0000259004 00000 n 0000259155 00000 n 0000259306 00000 n 0000259457 00000 n 0000259608 00000 n 0000259759 00000 n 0000259910 00000 n 0000260061 00000 n 0000260212 00000 n 0000260363 00000 n 0000260514 00000 n 0000260665 00000 n 0000260816 00000 n 0000260967 00000 n 0000261118 00000 n 0000261268 00000 n 0000261417 00000 n 0000261568 00000 n 0000261719 00000 n 0000261870 00000 n 0000262021 00000 n 0000262172 00000 n 0000262323 00000 n 0000262474 00000 n 0000262625 00000 n 0000262776 00000 n 0000262927 00000 n 0000263078 00000 n 0000263229 00000 n 0000263380 00000 n 0000263531 00000 n 0000263682 00000 n 0000263833 00000 n 0000263984 00000 n 0000264135 00000 n 0000264286 00000 n 0000264437 00000 n 0000264588 00000 n 0000264739 00000 n 0000264889 00000 n 0000265038 00000 n 0000265187 00000 n 0000267748 00000 n 0000265394 00000 n 0000256744 00000 n 0000255355 00000 n 0000265335 00000 n 0000267899 00000 n 0000268050 00000 n 0000268201 00000 n 0000268352 00000 n 0000268503 00000 n 0000268654 00000 n 0000268805 00000 n 0000268955 00000 n 0000269106 00000 n 0000269257 00000 n 0000269408 00000 n 0000269559 00000 n 0000269710 00000 n 0000269861 00000 n 0000270012 00000 n 0000270163 00000 n 0000270313 00000 n 0000270464 00000 n 0000270615 00000 n 0000270766 00000 n 0000270917 00000 n 0000271068 00000 n 0000271217 00000 n 0000271366 00000 n 0000271517 00000 n 0000271668 00000 n 0000271819 00000 n 0000271970 00000 n 0000272120 00000 n 0000272270 00000 n 0000272421 00000 n 0000272572 00000 n 0000272723 00000 n 0000272874 00000 n 0000273025 00000 n 0000273176 00000 n 0000273327 00000 n 0000273477 00000 n 0000273628 00000 n 0000273779 00000 n 0000273928 00000 n 0000274078 00000 n 0000274229 00000 n 0000274380 00000 n 0000274531 00000 n 0000274682 00000 n 0000274833 00000 n 0000274984 00000 n 0000275135 00000 n 0000275286 00000 n 0000275437 00000 n 0000275588 00000 n 0000275739 00000 n 0000275890 00000 n 0000276041 00000 n 0000276191 00000 n 0000276341 00000 n 0000276490 00000 n 0000278942 00000 n 0000276698 00000 n 0000267089 00000 n 0000265494 00000 n 0000276639 00000 n 0000279093 00000 n 0000279244 00000 n 0000279395 00000 n 0000279546 00000 n 0000279697 00000 n 0000279847 00000 n 0000279997 00000 n 0000280148 00000 n 0000280299 00000 n 0000280450 00000 n 0000280601 00000 n 0000280750 00000 n 0000280900 00000 n 0000281051 00000 n 0000281202 00000 n 0000281353 00000 n 0000281504 00000 n 0000281654 00000 n 0000281805 00000 n 0000281956 00000 n 0000282107 00000 n 0000282258 00000 n 0000282408 00000 n 0000282557 00000 n 0000282706 00000 n 0000282856 00000 n 0000283007 00000 n 0000283158 00000 n 0000283309 00000 n 0000283460 00000 n 0000283611 00000 n 0000283762 00000 n 0000283913 00000 n 0000284064 00000 n 0000284215 00000 n 0000284366 00000 n 0000284517 00000 n 0000284668 00000 n 0000284819 00000 n 0000284968 00000 n 0000285119 00000 n 0000285269 00000 n 0000285419 00000 n 0000285569 00000 n 0000285718 00000 n 0000285869 00000 n 0000286019 00000 n 0000286170 00000 n 0000286321 00000 n 0000286471 00000 n 0000286622 00000 n 0000286773 00000 n 0000286924 00000 n 0000287075 00000 n 0000287226 00000 n 0000287377 00000 n 0000287527 00000 n 0000287677 00000 n 0000287827 00000 n 0000287978 00000 n 0000288128 00000 n 0000290839 00000 n 0000288336 00000 n 0000278256 00000 n 0000276798 00000 n 0000288277 00000 n 0000290990 00000 n 0000291141 00000 n 0000291292 00000 n 0000291443 00000 n 0000291594 00000 n 0000291745 00000 n 0000291896 00000 n 0000292047 00000 n 0000292198 00000 n 0000292349 00000 n 0000292500 00000 n 0000292650 00000 n 0000292799 00000 n 0000292950 00000 n 0000293101 00000 n 0000293250 00000 n 0000293401 00000 n 0000293552 00000 n 0000293703 00000 n 0000293854 00000 n 0000294005 00000 n 0000294155 00000 n 0000294305 00000 n 0000294456 00000 n 0000294607 00000 n 0000294758 00000 n 0000294909 00000 n 0000295060 00000 n 0000295210 00000 n 0000295359 00000 n 0000295510 00000 n 0000295661 00000 n 0000295812 00000 n 0000295963 00000 n 0000296113 00000 n 0000296263 00000 n 0000296414 00000 n 0000296565 00000 n 0000296716 00000 n 0000296867 00000 n 0000297018 00000 n 0000297169 00000 n 0000297319 00000 n 0000297469 00000 n 0000297618 00000 n 0000297769 00000 n 0000297920 00000 n 0000298071 00000 n 0000298222 00000 n 0000298373 00000 n 0000298524 00000 n 0000298675 00000 n 0000298826 00000 n 0000298976 00000 n 0000299127 00000 n 0000301673 00000 n 0000299336 00000 n 0000290207 00000 n 0000288436 00000 n 0000299277 00000 n 0000301824 00000 n 0000301974 00000 n 0000302125 00000 n 0000302276 00000 n 0000302427 00000 n 0000302578 00000 n 0000302729 00000 n 0000302880 00000 n 0000303031 00000 n 0000303182 00000 n 0000303333 00000 n 0000303484 00000 n 0000303635 00000 n 0000303786 00000 n 0000303937 00000 n 0000304088 00000 n 0000304239 00000 n 0000304390 00000 n 0000304541 00000 n 0000304692 00000 n 0000304841 00000 n 0000304991 00000 n 0000305141 00000 n 0000305291 00000 n 0000305442 00000 n 0000305593 00000 n 0000305744 00000 n 0000305894 00000 n 0000306044 00000 n 0000306193 00000 n 0000306343 00000 n 0000306492 00000 n 0000306642 00000 n 0000306793 00000 n 0000306944 00000 n 0000307095 00000 n 0000307246 00000 n 0000307397 00000 n 0000307548 00000 n 0000307699 00000 n 0000307850 00000 n 0000308001 00000 n 0000308152 00000 n 0000308303 00000 n 0000308454 00000 n 0000308605 00000 n 0000308756 00000 n 0000308907 00000 n 0000309058 00000 n 0000309209 00000 n 0000309360 00000 n 0000309511 00000 n 0000309662 00000 n 0000309813 00000 n 0000309964 00000 n 0000310114 00000 n 0000310263 00000 n 0000311196 00000 n 0000310471 00000 n 0000301023 00000 n 0000299436 00000 n 0000310412 00000 n 0000311347 00000 n 0000311498 00000 n 0000311708 00000 n 0000311041 00000 n 0000310571 00000 n 0000311649 00000 n 0000446861 00000 n 0000442734 00000 n 0000311794 00000 n 0000312042 00000 n 0000312374 00000 n 0000312766 00000 n 0000313238 00000 n 0000313630 00000 n 0000314104 00000 n 0000314681 00000 n 0000314981 00000 n 0000315419 00000 n 0000331228 00000 n 0000331613 00000 n 0000350617 00000 n 0000351221 00000 n 0000368194 00000 n 0000368733 00000 n 0000380697 00000 n 0000381104 00000 n 0000387028 00000 n 0000387291 00000 n 0000393827 00000 n 0000394127 00000 n 0000407022 00000 n 0000407373 00000 n 0000427214 00000 n 0000427819 00000 n 0000442315 00000 n 0000446941 00000 n 0000447061 00000 n 0000447181 00000 n 0000447305 00000 n 0000447388 00000 n 0000450104 00000 n 0000450283 00000 n 0000450454 00000 n 0000450624 00000 n 0000450795 00000 n 0000450965 00000 n 0000451136 00000 n 0000451306 00000 n 0000451477 00000 n 0000451647 00000 n 0000451818 00000 n 0000451987 00000 n 0000452156 00000 n 0000452327 00000 n 0000452497 00000 n 0000452673 00000 n 0000452848 00000 n 0000453025 00000 n 0000453215 00000 n 0000453424 00000 n 0000453634 00000 n 0000453845 00000 n 0000454055 00000 n 0000454264 00000 n 0000454475 00000 n 0000454685 00000 n 0000454896 00000 n 0000455106 00000 n 0000455317 00000 n 0000455527 00000 n 0000455738 00000 n 0000455947 00000 n 0000456156 00000 n 0000456367 00000 n 0000456577 00000 n 0000456788 00000 n 0000456996 00000 n 0000457207 00000 n 0000457417 00000 n 0000457626 00000 n 0000457837 00000 n 0000458048 00000 n 0000458259 00000 n 0000458469 00000 n 0000458680 00000 n 0000458889 00000 n 0000459099 00000 n 0000459310 00000 n 0000459520 00000 n 0000459731 00000 n 0000459941 00000 n 0000460152 00000 n 0000460361 00000 n 0000460570 00000 n 0000460781 00000 n 0000460991 00000 n 0000461202 00000 n 0000461412 00000 n 0000461623 00000 n 0000461832 00000 n 0000462041 00000 n 0000462252 00000 n 0000462462 00000 n 0000462673 00000 n 0000462883 00000 n 0000463094 00000 n 0000463303 00000 n 0000463512 00000 n 0000463723 00000 n 0000463935 00000 n 0000464152 00000 n 0000464367 00000 n 0000464584 00000 n 0000464799 00000 n 0000465016 00000 n 0000465223 00000 n 0000465426 00000 n 0000465629 00000 n 0000465832 00000 n 0000466035 00000 n 0000466238 00000 n 0000466440 00000 n 0000466643 00000 n 0000466845 00000 n 0000467046 00000 n 0000467249 00000 n 0000467452 00000 n 0000467657 00000 n 0000467865 00000 n 0000468108 00000 n 0000468351 00000 n 0000468600 00000 n 0000468849 00000 n 0000469098 00000 n 0000469341 00000 n 0000469592 00000 n 0000469849 00000 n 0000470106 00000 n 0000470358 00000 n 0000470552 00000 n 0000470670 00000 n 0000470785 00000 n 0000470907 00000 n 0000471032 00000 n 0000471158 00000 n 0000471283 00000 n 0000471409 00000 n 0000471535 00000 n 0000471661 00000 n 0000471786 00000 n 0000471912 00000 n 0000472038 00000 n 0000472163 00000 n 0000472287 00000 n 0000472416 00000 n 0000472551 00000 n 0000472661 00000 n 0000472784 00000 n 0000472910 00000 n 0000473032 00000 n 0000473133 00000 n 0000473173 00000 n 0000473373 00000 n trailer << /Size 1764 /Root 1762 0 R /Info 1763 0 R /ID [<8BE4FB60377A00CC1FE69E2D23F43DD5> <8BE4FB60377A00CC1FE69E2D23F43DD5>] >> startxref 473762 %%EOF pymodbus/doc/INSTALL0000644000175500017550000000312212607272152014252 0ustar debacledebacleRequirements ------------- * Python 2.3 or later. * Python Twisted * Pyserial On Windows pywin32 is recommended (this is built in to ActivePython, so no need to reinstall if you use it instead of standard Python): http://sourceforge.net/project/showfiles.php?group_id=78018 The Windows IOCP reactor requires pywin32 build 205 or later. Installation ------------- To install the package from pypi, use either easy_install or pip:: pip install -U pymodbus easy_install -U pymodbus As with other Python packages, the standard way of installing from source is (as root or administrator):: python setup.py install Running Tests -------------- The tests can be run with the built in unittest module, however, it is much easier to run with the nose package. With that installed, you can use either of the following:: python setup.py test nosetests Building Documentation ---------------------- The documentation is written in restructured text using the sphinx module. Building it is as simple as:: python setup build_sphinx The API documents can be generated using one of four programs: * epydoc * pydoc * pydoctor * doxygen To bulid these, simply run the following command and the available packages will sipmly be built:: python setup.py build_apidocs Quality Tests ---------------------- There are a number of quality tests that can be run against the code base aside from unit tests:: python setup.py scan_2to3 # run a python3 compatability test python setup.py pep8 # run a pop8 standards test python setup.py lint # run a lint test pymodbus/doc/LICENSE0000644000175500017550000000256112607272152014234 0ustar debacledebacleCopyright (c) 2011 Galen Collins All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pymodbus/doc/sphinx/0000755000175500017550000000000012607272152014534 5ustar debacledebaclepymodbus/doc/sphinx/examples/0000755000175500017550000000000012607272152016352 5ustar debacledebaclepymodbus/doc/sphinx/examples/bottle-frontend.rst0000644000175500017550000000131112607272152022206 0ustar debacledebacle================================================== Bottle Web Frontend Example ================================================== -------------------------------------------------- Summary -------------------------------------------------- This is a simple example of adding a live REST api on top of a running pymodbus server. This uses the bottle microframework to achieve this. The example can be hosted under twisted as well as the bottle internal server and can furthermore be run behind gunicorn, cherrypi, etc wsgi containers. -------------------------------------------------- Main Program -------------------------------------------------- .. literalinclude:: ../../../examples/gui/web/frontend.py pymodbus/doc/sphinx/examples/redis-datastore.rst0000644000175500017550000000030112607272152022170 0ustar debacledebacle================================================== Redis Datastore Example ================================================== .. literalinclude:: ../../../examples/contrib/redis-datastore.py pymodbus/doc/sphinx/examples/synchronous-server.rst0000644000175500017550000000030612607272152023001 0ustar debacledebacle================================================== Synchronous Server Example ================================================== .. literalinclude:: ../../../examples/common/synchronous-server.py pymodbus/doc/sphinx/examples/database-datastore.rst0000644000175500017550000000030712607272152022634 0ustar debacledebacle================================================== Database Datastore Example ================================================== .. literalinclude:: ../../../examples/contrib/database-datastore.py pymodbus/doc/sphinx/examples/wx-frontend.rst0000644000175500017550000000112012607272152021351 0ustar debacledebacle================================================== WX Frontend Example ================================================== Main Program -------------------------------------------------- This is an example simulator that is written using the python wx bindings. Although it currently does not have a frontend for modifying the context values, it does allow one to expose N virtual modbus devices to a network which is useful for testing data center monitoring tools. .. note:: The virtual networking code will only work on linux .. literalinclude:: ../../../examples/gui/wx/simulator.py pymodbus/doc/sphinx/examples/gtk-frontend.rst0000644000175500017550000000150512607272152021507 0ustar debacledebacle================================================== Glade/GTK Frontend Example ================================================== Main Program -------------------------------------------------- This is an example simulator that is written using the pygtk bindings. Although it currently does not have a frontend for modifying the context values, it does allow one to expose N virtual modbus devices to a network which is useful for testing data center monitoring tools. .. note:: The virtual networking code will only work on linux .. literalinclude:: ../../../examples/gui/gtk/simulator.py :language: python Glade Layout File -------------------------------------------------- The following is the glade layout file that is used by this script: .. literalinclude:: ../../../examples/gui/gtk/simulator.glade :language: xml pymodbus/doc/sphinx/examples/asynchronous-server.rst0000644000175500017550000000031012607272152023135 0ustar debacledebacle================================================== Asynchronous Server Example ================================================== .. literalinclude:: ../../../examples/common/asynchronous-server.py pymodbus/doc/sphinx/examples/serial-forwarder.rst0000644000175500017550000000030712607272152022354 0ustar debacledebacle================================================== Synchronous Serial Forwarder ================================================== .. literalinclude:: ../../../examples/contrib/serial-forwarder.py pymodbus/doc/sphinx/examples/modbus-payload.rst0000644000175500017550000000032012607272152022017 0ustar debacledebacle================================================== Modbus Payload Building/Decoding Example ================================================== .. literalinclude:: ../../../examples/common/modbus-payload.py pymodbus/doc/sphinx/examples/custom-datablock.rst0000644000175500017550000000030212607272152022333 0ustar debacledebacle================================================== Custom Datablock Example ================================================== .. literalinclude:: ../../../examples/common/custom-datablock.py pymodbus/doc/sphinx/examples/libmodbus-client.rst0000644000175500017550000000030212607272152022333 0ustar debacledebacle================================================== Libmodbus Client Facade ================================================== .. literalinclude:: ../../../examples/contrib/libmodbus-client.py pymodbus/doc/sphinx/examples/modbus-scraper.rst0000644000175500017550000000027712607272152022040 0ustar debacledebacle================================================== Modbus Scraper Example ================================================== .. literalinclude:: ../../../examples/contrib/modbus-scraper.py pymodbus/doc/sphinx/examples/message-generator.rst0000644000175500017550000000147412607272152022522 0ustar debacledebacle================================================== Modbus Message Generator Example ================================================== This is an example of a utility that will build examples of modbus messages in all the available formats in the pymodbus package. -------------------------------------------------- Program Source -------------------------------------------------- .. literalinclude:: ../../../examples/contrib/message-generator.py -------------------------------------------------- Example Request Messages -------------------------------------------------- .. literalinclude:: ../../../examples/contrib/tx-messages -------------------------------------------------- Example Response Messages -------------------------------------------------- .. literalinclude:: ../../../examples/contrib/rx-messages pymodbus/doc/sphinx/examples/remote-server-context.rst0000644000175500017550000000031412607272152023363 0ustar debacledebacle================================================== Remote Single Server Context ================================================== .. literalinclude:: ../../../examples/contrib/remote_server_context.py pymodbus/doc/sphinx/examples/index.rst0000644000175500017550000000204612607272152020215 0ustar debacledebacle Pymodbus Library Examples ==================================== *What follows is a collection of examples using the pymodbus library in various ways* Example Library Code -------------------------------------------------- .. toctree:: :maxdepth: 2 asynchronous-client asynchronous-server asynchronous-processor custom-message modbus-logging modbus-payload synchronous-client synchronous-client-ext synchronous-server performance updating-server callback-server changing-framers thread-safe-datastore Custom Pymodbus Code -------------------------------------------------- .. toctree:: :maxdepth: 2 redis-datastore database-datastore bcd-payload modicon-payload message-generator message-parser serial-forwarder modbus-scraper modbus-simulator concurrent-client libmodbus-client remote-server-context Example Frontend Code -------------------------------------------------- .. toctree:: :maxdepth: 2 gtk-frontend tk-frontend wx-frontend bottle-frontend pymodbus/doc/sphinx/examples/modicon-payload.rst0000644000175500017550000000030112607272152022155 0ustar debacledebacle================================================== Modicon Encoded Example ================================================== .. literalinclude:: ../../../examples/contrib/modicon-payload.py pymodbus/doc/sphinx/examples/thread-safe-datastore.rst0000644000175500017550000000031512607272152023252 0ustar debacledebacle================================================== Thread Safe Datastore Example ================================================== .. literalinclude:: ../../../examples/contrib/thread_safe_datastore.py pymodbus/doc/sphinx/examples/updating-server.rst0000644000175500017550000000030012607272152022214 0ustar debacledebacle================================================== Updating Server Example ================================================== .. literalinclude:: ../../../examples/common/updating-server.py pymodbus/doc/sphinx/examples/synchronous-client.rst0000644000175500017550000000157012607272152022755 0ustar debacledebacle================================================== Synchronous Client Example ================================================== It should be noted that each request will block waiting for the result. If asynchronous behaviour is required, please use the asynchronous client implementations. The synchronous client, works against TCP, UDP, serial ASCII, and serial RTU devices. The synchronous client exposes the most popular methods of the modbus protocol, however, if you want to execute other methods against the device, simple create a request instance and pass it to the execute method. Below an synchronous tcp client is demonstrated running against a reference server. If you do not have a device to test with, feel free to run a pymodbus server instance or start the reference tester in the tools directory. .. literalinclude:: ../../../examples/common/synchronous-client.py pymodbus/doc/sphinx/examples/modbus-simulator.rst0000644000175500017550000000030212607272152022405 0ustar debacledebacle================================================== Modbus Simulator Example ================================================== .. literalinclude:: ../../../examples/contrib/modbus-simulator.py pymodbus/doc/sphinx/examples/tk-frontend.rst0000644000175500017550000000111712607272152021337 0ustar debacledebacle================================================== TK Frontend Example ================================================== Main Program -------------------------------------------------- This is an example simulator that is written using the native tk toolkit. Although it currently does not have a frontend for modifying the context values, it does allow one to expose N virtual modbus devices to a network which is useful for testing data center monitoring tools. .. note:: The virtual networking code will only work on linux .. literalinclude:: ../../../examples/gui/tk/simulator.py pymodbus/doc/sphinx/examples/performance.rst0000644000175500017550000000071012607272152021403 0ustar debacledebacle================================================== Synchronous Client Performance Check ================================================== Below is a quick example of how to test the performance of a tcp modbus device using the synchronous tcp client. If you do not have a device to test with, feel free to run a pymodbus server instance or start the reference tester in the tools directory. .. literalinclude:: ../../../examples/common/performance.py pymodbus/doc/sphinx/examples/synchronous-client-ext.rst0000644000175500017550000000032312607272152023546 0ustar debacledebacle================================================== Synchronous Client Extended Example ================================================== .. literalinclude:: ../../../examples/common/synchronous-client-ext.py pymodbus/doc/sphinx/examples/asynchronous-processor.rst0000644000175500017550000000113412607272152023653 0ustar debacledebacle================================================== Asynchronous Processor Example ================================================== Below is a simplified asynchronous client skeleton that was submitted by a user of the library. It can be used as a guide for implementing more complex pollers or state machines. Feel free to test it against whatever device you currently have available. If you do not have a device to test with, feel free to run a pymodbus server instance or start the reference tester in the tools directory. .. literalinclude:: ../../../examples/common/asynchronous-processor.py pymodbus/doc/sphinx/examples/callback-server.rst0000644000175500017550000000030012607272152022135 0ustar debacledebacle================================================== Callback Server Example ================================================== .. literalinclude:: ../../../examples/common/callback-server.py pymodbus/doc/sphinx/examples/modbus-logging.rst0000644000175500017550000000027612607272152022026 0ustar debacledebacle================================================== Modbus Logging Example ================================================== .. literalinclude:: ../../../examples/common/modbus-logging.py pymodbus/doc/sphinx/examples/message-parser.rst0000644000175500017550000000435412607272152022030 0ustar debacledebacle================================================== Modbus Message Parsing Example ================================================== This is an example of a parser to decode raw messages to a readable description. It will attempt to decode a message to the request and response version of a message if possible. Here is an example output:: $./message-parser.py -b -m 000112340006ff076d ================================================================================ Decoding Message 000112340006ff076d ================================================================================ ServerDecoder -------------------------------------------------------------------------------- name = ReadExceptionStatusRequest check = 0x0 unit_id = 0xff transaction_id = 0x1 protocol_id = 0x1234 documentation = This function code is used to read the contents of eight Exception Status outputs in a remote device. The function provides a simple method for accessing this information, because the Exception Output references are known (no output reference is needed in the function). ClientDecoder -------------------------------------------------------------------------------- name = ReadExceptionStatusResponse check = 0x0 status = 0x6d unit_id = 0xff transaction_id = 0x1 protocol_id = 0x1234 documentation = The normal response contains the status of the eight Exception Status outputs. The outputs are packed into one data byte, with one bit per output. The status of the lowest output reference is contained in the least significant bit of the byte. The contents of the eight Exception Status outputs are device specific. -------------------------------------------------- Program Source -------------------------------------------------- .. literalinclude:: ../../../examples/contrib/message-parser.py -------------------------------------------------- Example Messages -------------------------------------------------- See the documentation for the message generator for a collection of messages that can be parsed by this utility. pymodbus/doc/sphinx/examples/concurrent-client.rst0000644000175500017550000000031412607272152022540 0ustar debacledebacle================================================== Modbus Concurrent Client Example ================================================== .. literalinclude:: ../../../examples/contrib/concurrent-client.py pymodbus/doc/sphinx/examples/changing-framers.rst0000644000175500017550000000030212607272152022312 0ustar debacledebacle================================================== Changing Default Framers ================================================== .. literalinclude:: ../../../examples/common/changing-framers.py pymodbus/doc/sphinx/examples/bcd-payload.rst0000644000175500017550000000030212607272152021256 0ustar debacledebacle================================================== Binary Coded Decimal Example ================================================== .. literalinclude:: ../../../examples/contrib/bcd-payload.py pymodbus/doc/sphinx/examples/asynchronous-client.rst0000644000175500017550000000126712607272152023121 0ustar debacledebacle================================================== Asynchronous Client Example ================================================== The asynchronous client functions in the same way as the synchronous client, however, the asynchronous client uses twisted to return deferreds for the response result. Just like the synchronous version, it works against TCP, UDP, serial ASCII, and serial RTU devices. Below an asynchronous tcp client is demonstrated running against a reference server. If you do not have a device to test with, feel free to run a pymodbus server instance or start the reference tester in the tools directory. .. literalinclude:: ../../../examples/common/asynchronous-client.py pymodbus/doc/sphinx/examples/custom-message.rst0000644000175500017550000000027612607272152022045 0ustar debacledebacle================================================== Custom Message Example ================================================== .. literalinclude:: ../../../examples/common/custom-message.py pymodbus/doc/sphinx/library/0000755000175500017550000000000012607272152016200 5ustar debacledebaclepymodbus/doc/sphinx/library/bit-write-message.rst0000644000175500017550000000112312607272152022257 0ustar debacledebacle:mod:`bit_write_message` --- Bit Write Modbus Messages ============================================================ .. module:: bit_write_message :synopsis: Bit Write Modbus Messages .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.bit_write_message .. autoclass:: WriteSingleCoilRequest :members: .. autoclass:: WriteSingleCoilResponse :members: .. autoclass:: WriteMultipleCoilsRequest :members: .. autoclass:: WriteMultipleCoilsResponse :members: pymodbus/doc/sphinx/library/constants.rst0000644000175500017550000000112412607272152020744 0ustar debacledebacle:mod:`constants` --- Modbus Default Values ============================================================ .. module:: constants :synopsis: Modbus Default Values .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.constants .. autoclass:: Defaults :members: .. autoclass:: ModbusStatus :members: .. autoclass:: Endian :members: .. autoclass:: ModbusPlusOperation :members: .. autoclass:: DeviceInformation :members: .. autoclass:: MoreData :members: pymodbus/doc/sphinx/library/utilities.rst0000644000175500017550000000120612607272152020744 0ustar debacledebacle:mod:`utilities` --- Extra Modbus Helpers ========================================== .. module:: utilities :synopsis: Extra Modbus Helpers .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.utilities .. autofunction:: default .. autofunction:: dict_property .. autofunction:: pack_bitstring .. autofunction:: unpack_bitstring .. autofunction:: __generate_crc16_table .. autofunction:: computeCRC .. autofunction:: checkCRC .. autofunction:: computeLRC .. autofunction:: checkLRC .. autofunction:: rtuFrameSize pymodbus/doc/sphinx/library/pymodbus.rst0000644000175500017550000000035012607272152020572 0ustar debacledebacle:mod:`pymodbus` --- Pymodbus Library ============================================================ .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins .. automodule:: pymodbus pymodbus/doc/sphinx/library/pdu.rst0000644000175500017550000000111412607272152017517 0ustar debacledebacle:mod:`pdu` --- Base Structures ============================================================ .. module:: pdu :synopsis: Base Structures .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.pdu .. autoclass:: ModbusPDU :members: .. autoclass:: ModbusRequest :members: .. autoclass:: ModbusResponse :members: .. autoclass:: ModbusExceptions :members: .. autoclass:: ExceptionResponse :members: .. autoclass:: IllegalFunctionRequest :members: pymodbus/doc/sphinx/library/async-client.rst0000644000175500017550000000072012607272152021322 0ustar debacledebacle:mod:`client.async` --- Twisted Async Modbus Client ==================================================== .. module:: client.async :synopsis: Twisted Asynchronous Modbus Client .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------ .. automodule:: pymodbus.client.async .. autoclass:: ModbusClientProtocol :members: .. autoclass:: ModbusClientFactory :members: pymodbus/doc/sphinx/library/sync-client.rst0000644000175500017550000000114312607272152021161 0ustar debacledebacle:mod:`client.sync` --- Twisted Synchronous Modbus Client ========================================================= .. module:: client.sync :synopsis: Twisted Synchronous Modbus Client .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------ .. automodule:: pymodbus.client.sync .. autoclass:: ModbusTransactionManager :members: .. autoclass:: BaseModbusClient :members: .. autoclass:: ModbusTcpClient :members: .. autoclass:: ModbusUdpClient :members: .. autoclass:: ModbusSerialClient :members: pymodbus/doc/sphinx/library/other-message.rst0000644000175500017550000000125512607272152021500 0ustar debacledebacle:mod:`other_message` --- Other Modbus Messages ============================================================ .. module:: other_message :synopsis: Other Modbus Messages .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.other_message .. autoclass:: ReadExceptionStatusRequest :members: .. autoclass:: ReadExceptionStatusResponse :members: .. autoclass:: GetCommEventCounterRequest :members: .. autoclass:: GetCommEventCounterResponse :members: .. autoclass:: ReportSlaveIdRequest :members: .. autoclass:: ReportSlaveIdResponse :members: pymodbus/doc/sphinx/library/events.rst0000644000175500017550000000110612607272152020234 0ustar debacledebacle:mod:`events` --- Events Used in PyModbus ============================================================ .. module:: events :synopsis: Events Used in PyModbus .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.events .. autoclass:: ModbusEvent :members: .. autoclass:: RemoteReceiveEvent :members: .. autoclass:: RemoteSendEvent :members: .. autoclass:: EnteredListenModeEvent :members: .. autoclass:: CommunicationRestartEvent :members: pymodbus/doc/sphinx/library/bit-read-message.rst0000644000175500017550000000124512607272152022045 0ustar debacledebacle:mod:`bit_read_message` --- Bit Read Modbus Messages ============================================================ .. module:: bit_read_message :synopsis: Bit Read Modbus Messages .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.bit_read_message .. autoclass:: ReadBitsRequestBase :members: .. autoclass:: ReadBitsResponseBase :members: .. autoclass:: ReadCoilsRequest :members: .. autoclass:: ReadCoilsResponse :members: .. autoclass:: ReadDiscreteInputsRequest :members: .. autoclass:: ReadDiscreteInputsResponse :members: pymodbus/doc/sphinx/library/file-message.rst0000644000175500017550000000127612607272152021301 0ustar debacledebacle:mod:`file_message` --- File Modbus Messages ============================================================ .. module:: file_message :synopsis: File Modbus Messages .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.file_message .. autoclass:: FileRecord :members: .. autoclass:: ReadFileRecordRequest :members: .. autoclass:: ReadFileRecordResponse :members: .. autoclass:: WriteFileRecordRequest :members: .. autoclass:: WriteFileRecordResponse :members: .. autoclass:: ReadFifoQueueRequest :members: .. autoclass:: ReadFifoQueueResponse :members: pymodbus/doc/sphinx/library/datastore/0000755000175500017550000000000012607272152020166 5ustar debacledebaclepymodbus/doc/sphinx/library/datastore/index.rst0000644000175500017550000000033312607272152022026 0ustar debacledebacle Server Datastores and Contexts ==================================== *The following are the API documentation strings taken from the sourcecode* .. toctree:: :maxdepth: 2 store.rst context.rst remote.rst pymodbus/doc/sphinx/library/datastore/store.rst0000644000175500017550000000102012607272152022045 0ustar debacledebacle:mod:`store` --- Datastore for Modbus Server Context ============================================================ .. module:: store :synopsis: Datastore for Modbus Server Context .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.datastore.store .. autoclass:: BaseModbusDataBlock :members: .. autoclass:: ModbusSequentialDataBlock :members: .. autoclass:: ModbusSparseDataBlock :members: pymodbus/doc/sphinx/library/datastore/remote.rst0000644000175500017550000000061112607272152022211 0ustar debacledebacle:mod:`remote` --- Remote Slave Context ============================================================ .. module:: remote :synopsis: Remote Slave Context .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.datastore.remote .. autoclass:: RemoteSlaveContext :members: pymodbus/doc/sphinx/library/datastore/context.rst0000644000175500017550000000070212607272152022403 0ustar debacledebacle:mod:`context` --- Modbus Server Contexts ============================================================ .. module:: context :synopsis: Modbus Server Contexts .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.datastore.context .. autoclass:: ModbusSlaveContext :members: .. autoclass:: ModbusServerContext :members: pymodbus/doc/sphinx/library/client-common.rst0000644000175500017550000000062712607272152021503 0ustar debacledebacle:mod:`client.common` --- Twisted Async Modbus Client ==================================================== .. module:: client.common :synopsis: Modbus common client clode .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------ .. automodule:: pymodbus.client.common .. autoclass:: ModbusClientMixin :members: pymodbus/doc/sphinx/library/index.rst0000644000175500017550000000123112607272152020036 0ustar debacledebacle Pymodbus Library API Documentation ==================================== *The following are the API documentation strings taken from the sourcecode* .. toctree:: :maxdepth: 2 bit-read-message.rst bit-write-message.rst client-common.rst sync-client.rst async-client.rst constants.rst datastore/index.rst diag-message.rst device.rst factory.rst interfaces.rst exceptions.rst other-message.rst mei-message.rst file-message.rst events.rst payload.rst pdu.rst pymodbus.rst register-read-message.rst register-write-message.rst sync-server.rst async-server.rst transaction.rst utilities.rst pymodbus/doc/sphinx/library/sync-server.rst0000644000175500017550000000152212607272152021212 0ustar debacledebacle:mod:`server.sync` --- Twisted Synchronous Modbus Server ============================================================ .. module:: server.sync :synopsis: Twisted Synchronous Modbus Server .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.server.sync .. autoclass:: ModbusBaseRequestHandler :members: .. autoclass:: ModbusSingleRequestHandler :members: .. autoclass:: ModbusConnectedRequestHandler :members: .. autoclass:: ModbusDisconnectedRequestHandler :members: .. autoclass:: ModbusTcpServer :members: .. autoclass:: ModbusUdpServer :members: .. autoclass:: ModbusSerialServer :members: .. autofunction:: StartTcpServer .. autofunction:: StartUdpServer .. autofunction:: StartSerialServer pymodbus/doc/sphinx/library/register-read-message.rst0000644000175500017550000000151712607272152023115 0ustar debacledebacle:mod:`register_read_message` --- Register Read Messages ============================================================ .. module:: register_read_message :synopsis: Register Read Messages .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.register_read_message .. autoclass:: ReadRegistersRequestBase :members: .. autoclass:: ReadRegistersResponseBase :members: .. autoclass:: ReadHoldingRegistersRequest :members: .. autoclass:: ReadHoldingRegistersResponse :members: .. autoclass:: ReadInputRegistersRequest :members: .. autoclass:: ReadInputRegistersResponse :members: .. autoclass:: ReadWriteMultipleRegistersRequest :members: .. autoclass:: ReadWriteMultipleRegistersResponse :members: pymodbus/doc/sphinx/library/transaction.rst0000644000175500017550000000124212607272152021256 0ustar debacledebacle:mod:`transaction` --- Transaction Controllers for Pymodbus ============================================================ .. module:: transaction :synopsis: Transaction controllers for pymodbus .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.transaction .. autoclass:: DictTransactionManager :members: .. autoclass:: FifoTransactionManager :members: .. autoclass:: ModbusSocketFramer :members: .. autoclass:: ModbusRtuFramer :members: .. autoclass:: ModbusAsciiFramer :members: .. autoclass:: ModbusBinaryFramer :members: pymodbus/doc/sphinx/library/diag-message.rst0000644000175500017550000000511012607272152021255 0ustar debacledebacle:mod:`diag_message` --- Diagnostic Modbus Messages ============================================================ .. module:: diag_message :synopsis: Diagnostic Modbus Messages .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.diag_message .. autoclass:: DiagnosticStatusRequest :members: .. autoclass:: DiagnosticStatusResponse :members: .. autoclass:: DiagnosticStatusSimpleRequest :members: .. autoclass:: DiagnosticStatusSimpleResponse :members: .. autoclass:: ReturnQueryDataRequest :members: .. autoclass:: ReturnQueryDataResponse :members: .. autoclass:: RestartCommunicationsOptionRequest :members: .. autoclass:: RestartCommunicationsOptionResponse :members: .. autoclass:: ReturnDiagnosticRegisterRequest :members: .. autoclass:: ReturnDiagnosticRegisterResponse :members: .. autoclass:: ChangeAsciiInputDelimiterRequest :members: .. autoclass:: ChangeAsciiInputDelimiterResponse :members: .. autoclass:: ForceListenOnlyModeRequest :members: .. autoclass:: ForceListenOnlyModeResponse :members: .. autoclass:: ClearCountersRequest :members: .. autoclass:: ClearCountersResponse :members: .. autoclass:: ReturnBusMessageCountRequest :members: .. autoclass:: ReturnBusMessageCountResponse :members: .. autoclass:: ReturnBusCommunicationErrorCountRequest :members: .. autoclass:: ReturnBusCommunicationErrorCountResponse :members: .. autoclass:: ReturnBusExceptionErrorCountRequest :members: .. autoclass:: ReturnBusExceptionErrorCountResponse :members: .. autoclass:: ReturnSlaveMessageCountRequest :members: .. autoclass:: ReturnSlaveMessageCountResponse :members: .. autoclass:: ReturnSlaveNoResponseCountRequest :members: .. autoclass:: ReturnSlaveNoReponseCountResponse :members: .. autoclass:: ReturnSlaveNAKCountRequest :members: .. autoclass:: ReturnSlaveNAKCountResponse :members: .. autoclass:: ReturnSlaveBusyCountRequest :members: .. autoclass:: ReturnSlaveBusyCountResponse :members: .. autoclass:: ReturnSlaveBusCharacterOverrunCountRequest :members: .. autoclass:: ReturnSlaveBusCharacterOverrunCountResponse :members: .. autoclass:: ReturnIopOverrunCountRequest :members: .. autoclass:: ReturnIopOverrunCountResponse :members: .. autoclass:: ClearOverrunCountRequest :members: .. autoclass:: ClearOverrunCountResponse :members: .. autoclass:: GetClearModbusPlusRequest :members: .. autoclass:: GetClearModbusPlusResponse :members: pymodbus/doc/sphinx/library/register-write-message.rst0000644000175500017550000000115612607272152023333 0ustar debacledebacle:mod:`register_write_message` --- Register Write Messages ============================================================ .. module:: register_write_message :synopsis: Register Write Messages .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.register_write_message .. autoclass:: WriteSingleRegisterRequest :members: .. autoclass:: WriteSingleRegisterResponse :members: .. autoclass:: WriteMultipleRegistersRequest :members: .. autoclass:: WriteMultipleRegistersResponse :members: pymodbus/doc/sphinx/library/payload.rst0000644000175500017550000000067612607272152020374 0ustar debacledebacle:mod:`payload` --- Modbus Payload Utilities ============================================================ .. module:: payload :synopsis: Modbus Payload Utilities .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.payload .. autoclass:: BinaryPayloadBuilder :members: .. autoclass:: BinaryPayloadDecoder :members: pymodbus/doc/sphinx/library/async-server.rst0000644000175500017550000000116612607272152021357 0ustar debacledebacle:mod:`server.async` --- Twisted Asynchronous Modbus Server ============================================================ .. module:: server.async :synopsis: Twisted Asynchronous Modbus Server .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.server.async .. autoclass:: ModbusTcpProtocol :members: .. autoclass:: ModbusUdpProtocol :members: .. autoclass:: ModbusServerFactory :members: .. autofunction:: StartTcpServer .. autofunction:: StartUdpServer .. autofunction:: StartSerialServer pymodbus/doc/sphinx/library/interfaces.rst0000644000175500017550000000106112607272152021053 0ustar debacledebacle:mod:`interfaces` --- System Interfaces ============================================================ .. module:: interfaces :synopsis: System Interfaces .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.interfaces .. autoclass:: Singleton :members: .. autoclass:: IModbusDecoder :members: .. autoclass:: IModbusFramer :members: .. autoclass:: IModbusSlaveContext :members: .. autoclass:: IPayloadBuilder :members: pymodbus/doc/sphinx/library/factory.rst0000644000175500017550000000066212607272152020405 0ustar debacledebacle:mod:`factory` --- Request/Response Decoders ============================================================ .. module:: factory :synopsis: Request/Response Decoders .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.factory .. autoclass:: ServerDecoder :members: .. autoclass:: ClientDecoder :members: pymodbus/doc/sphinx/library/device.rst0000644000175500017550000000114012607272152020165 0ustar debacledebacle:mod:`device` --- Modbus Device Representation ============================================================ .. module:: device :synopsis: Modbus Device Representation .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.device .. autoclass:: ModbusAccessControl :members: .. autoclass:: ModbusPlusStatistics :members: .. autoclass:: ModbusDeviceIdentification :members: .. autoclass:: DeviceInformationFactory :members: .. autoclass:: ModbusControlBlock :members: pymodbus/doc/sphinx/library/exceptions.rst0000644000175500017550000000105312607272152021112 0ustar debacledebacle:mod:`exceptions` --- Exceptions Used in PyModbus ============================================================ .. module:: exceptions :synopsis: Exceptions Used in PyModbus .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.exceptions .. autoclass:: ModbusException :members: .. autoclass:: ModbusIOException :members: .. autoclass:: ParameterException :members: .. autoclass:: NotImplementedException :members: pymodbus/doc/sphinx/library/mei-message.rst0000644000175500017550000000072112607272152021126 0ustar debacledebacle:mod:`mei_message` --- MEI Modbus Messages ============================================================ .. module:: mei_message :synopsis: MEI Modbus Messages .. moduleauthor:: Galen Collins .. sectionauthor:: Galen Collins API Documentation ------------------- .. automodule:: pymodbus.mei_message .. autoclass:: ReadDeviceInformationRequest :members: .. autoclass:: ReadDeviceInformationResponse :members: pymodbus/doc/sphinx/index.rst0000644000175500017550000000072712607272152016403 0ustar debacledebacle.. PyModbus documentation master file, created by sphinx-quickstart on Tue Apr 14 19:11:16 2009. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to PyModbus's documentation! ==================================== Contents: .. toctree:: :maxdepth: 2 examples/index.rst library/index.rst Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` pymodbus/doc/sphinx/Makefile0000644000175500017550000000563712607272152016207 0ustar debacledebacle# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf build/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html @echo @echo "Build finished. The HTML pages are in build/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) build/dirhtml @echo @echo "Build finished. The HTML pages are in build/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) build/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in build/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) build/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in build/qthelp, like this:" @echo "# qcollectiongenerator build/qthelp/PyModbus.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile build/qthelp/PyModbus.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex @echo @echo "Build finished; the LaTeX files are in build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes @echo @echo "The overview file is in build/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in build/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) build/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in build/doctest/output.txt." pymodbus/doc/sphinx/static/0000755000175500017550000000000012607272152016023 5ustar debacledebaclepymodbus/doc/sphinx/static/README0000644000175500017550000000004512607272152016702 0ustar debacledebacleinclude any html static content here pymodbus/doc/sphinx/conf.py0000644000175500017550000001461412607272152016041 0ustar debacledebacle# -*- coding: utf-8 -*- # # PyModbus documentation build configuration file, created by # sphinx-quickstart on Tue Apr 14 19:11:16 2009. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Pymodbus' copyright = u'2009, Galen Collins' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.0' # The full version, including alpha/beta/rc tags. release = '1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ['build'] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. show_authors = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for extensions --------------------------------------------------- autodoc_default_flags = ['members', 'inherited-members', 'show-inheritance'] autoclass_content = 'both' # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'Pymodbus' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'Pymodbus.tex', ur'Pymodbus Documentation', ur'Galen Collins', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True