def test_logix_remote_pylogix( count=100 ): """Performance of pylogix executing an operation a number of times on a socket connected Logix simulator, within the same Python interpreter (ie. all on a single CPU thread). Only connects on the standard port. """ #logging.getLogger().setLevel( logging.NORMAL ) enip.lookup_reset() # Flush out any existing CIP Objects for a fresh start svraddr = ('localhost', 44828) kwargs = { 'argv': [ #'-v', #'--log', '/tmp/pylogix.log', #'--profile', '/tmp/plogix.prof', '--address', '%s:%d' % svraddr, 'SCADA=INT[1000]' ], 'server': { 'control': cpppo.apidict( enip.timeout, { 'done': False } ), }, } logixthread_kwargs = { 'count': count, 'svraddr': svraddr, 'kwargs': kwargs } log.normal( "test_logix_remote_pylogix w/ server.control in object %s", id( kwargs['server']['control'] )) # This is sort of "inside-out". This thread will run logix_remote, which will signal the # enip_main (via the kwargs.server...) to shut down. However, to do line-based performance # measurement, we need to be running enip.main in the "Main" thread... logixthread = threading.Thread( target=logix_remote_pylogix, kwargs=logixthread_kwargs ) logixthread.daemon = True logixthread.start() try: enip_main( **kwargs ) finally: kwargs['server']['control'].done = True # Signal the server to terminate logixthread.join() log.normal( "Shutdown of server complete" )
def main(**kwds): """Set up a *Logix w/ a 16-channel HART Interface card, eg. 1756-IF8H""" enip.config_files += [__file__.replace('.py', '.cfg')] HART(name="HART Channels", instance_id=0) # Class Object for i in range(16): HART(name="HART Channel %d" % i, instance_id=i + 1) # Establish Identity and TCPIP objects w/ some custom data for the test, from a config file return enip_main(argv=sys.argv[1:])
def main(**kwds): """Set up PowerFlex/20-COMM-E objects (enip.main will set up other Logix-like objects)""" enip.config_files += [__file__.replace('.py', '.cfg')] DPI_Parameters(name="DPI_Parameters", instance_id=0) # Class Object DPI_Parameters(name="DPI_Parameters", instance_id=DPI_Parameters.OUTPUT_FREQ) DPI_Parameters(name="DPI_Parameters", instance_id=DPI_Parameters.MTR_VEL_FDBK) DPI_Parameters(name="DPI_Parameters", instance_id=DPI_Parameters.OUTPUT_CURRENT) DPI_Parameters(name="DPI_Parameters", instance_id=DPI_Parameters.DC_BUS_VOLTS) DPI_Parameters(name="DPI_Parameters", instance_id=DPI_Parameters.ELAPSED_KWH) DPI_Parameters(name="DPI_Parameters", instance_id=DPI_Parameters.ACCEL_TIME_1) DPI_Parameters(name="DPI_Parameters", instance_id=DPI_Parameters.SPEED_UNITS) # Establish Identity and TCPIP objects w/ some custom data for the test, from a config file return enip_main(argv=sys.argv[1:], UCMM_class=UCMM_no_route_path)
def url(self, **kwds): """Produce a url by joining the class' URI and OPTs with any keyword parameters""" return self.URI + "?" + urlencode(dict(self.OPT, **kwds)) def __getitem__(self, key): """Obtain the temperature of the city's matching our Attribute's name, convert it to an appropriate type; return a value appropriate to the request.""" try: # eg. "http://api.openweathermap.org/...?...&q=City Name" data = urlopen(self.url(q=self.name)).read() if type(data) is not str: # Python3 urlopen.read returns bytes data = data.decode('utf-8') weather = json.loads(data) assert weather.get( 'cod' ) == 200 and 'main' in weather, \ weather.get( 'message', "Unknown error obtaining weather data" ) cast = float if isinstance(self.parser, REAL) else int temperature = cast(weather['main']['temp']) except Exception as exc: logging.warning("Couldn't get temperature for %s via %r: %s", self.name, self.url(q=self.name), exc) raise return [temperature ] if self._validate_key(key) is slice else temperature def __setitem__(self, key, value): raise Exception("Changing the weather isn't that easy...") sys.exit(enip_main(attribute_class=Attribute_weather))
def __setitem__( self, key, value ): try: # vvvv -- Process an EtherNet/IP CIP Write [Tag [Fragmented]]. # # We'll just store the value, and output the write request (and the value written) to # our time-series history file. # super( Attribute_historize, self ).__setitem__( key, value ) self.__logger.write( { 'write': value }, serial=(self.name, ( key.indices( len( self ))[0] if isinstance( key, slice ) else key, key.indices( len( self ))[1]-1 if isinstance( key, slice ) else key, ))) # ^^^^ except Exception as exc: # vvvv -- Process an EtherNet/IP CIP Write [Tag [Fragmented]] Exception. # # Something went wrong with the Write request processing. Log something intelligent and # re-raise the exception, to return a failure to the EtherNet/IP client. # self.__logger.comment( "%s: PLC I/O Write Tag %20s[%5s-%-5s] Exception: %s" % ( history.timestamp(), self.name, key.indices( len( self ))[0] if isinstance( key, slice ) else key, key.indices( len( self ))[1]-1 if isinstance( key, slice ) else key, exc )) # ^^^^ raise sys.exit( enip_main( attribute_class=Attribute_historize ))
def main(argv=None, idle_service=None, **kwds): """Run a cpppo.server.enip.main simulating a bunch of Tags, with the initial data specified in the config file matching the name of the simulator (eg. simulator_example.cfg). Append your own configuration file name to the enip.config_files list, or put a [Simulator] block in one of the other Cpppo configuration files. We'll traverse any keys specified in the configuration, create each of them as Attributes (at any specified @<class>/<instance>/<attribute> CIP address), and populate the enip.tags dictionary with them. Any number of tags can be specified with their type, optional CIP address and/or array length, and optionaly initial value(s) data. We'll add any [Simulator] tags to the supplies sys.argv data, and let the enip.main parse all the tag names and type/address/size. Then, we'll initialize the attributes afterward everything starts up, by looking up the tag names in enip.tags, and assigning the initial values. [Simulator] Something Floating = REAL = 1.0 Many Reals = REAL[100] = 0.1, 0.2, 0.3 INTS = INT[10] # ten INTs starting off at zero value Specific CIP Address@123/4/5 = DINT = 999 """ if argv is None: argv = sys.argv[1:] # Remember any tags we find w/ optional values CSV. We won't know the type 'til after the # Attribute is created by enip.main, so just store the raw CSV for now. Use the Object class' # config_loader, and only look for [Simulator] entries (don't use DEFAULT). Iterate thru all # the entries, adding an entry to kwds for each. values = {} # All collected Tags w/ a CSV of initial values # Load config_files early (with case sensitivity); enip.main will re-do it, case-insensitively optionxform = Object.config_loader.optionxform Object.config_loader.optionxform = str # Case-sensitive Object.config_loader.read(config_files) if 'Simulator' in Object.config_loader: for nam, typ in Object.config_loader['Simulator'].items(): val = None if '=' in typ: # Optional value(s) typ, val = typ.split('=', 1) typ, val = typ.strip(), val.strip() argv += ['='.join((nam, typ))] # eg. Tag@123/4/5=REAL[100] if val: # A non-empty initial value was provided; strip off any optional CIP address and # save the initial values provided. if '@' in nam: nam = nam[:nam.index('@')] values[nam] = val Object.config_loader.optionxform = optionxform Object.config_loader.clear() def idle_init(): """First time thru, set up any initia values; subsequently, perform original idle_service.""" if idle_init.complete: if idle_service: idle_service() return idle_init.complete = True for nam in values: # Got initial value(s) for this one. val_list = [] try: val_list, = csv.reader([values[nam]], quotechar='"', delimiter=',', quoting=csv.QUOTE_ALL, skipinitialspace=True) ent = dict.__getitem__( tags, nam) # may be 'Tag.SubTag'; avoid dotdict '.' resolution typ = ent.attribute.parser.__class__.__name__ # eg. 'REAL' _, _, cast = client.CIP_TYPES[typ] ent.attribute[0:len( val_list )] \ = [ cast( v ) for v in val_list ] except Exception as exc: print("Failed to set %s[0:%d] = %r" % (nam, len(val_list), val_list)) raise idle_init.complete = False # Establish Identity, TCPIP, etc. objects, and any custom [Simulator] tags from the config file(s). return enip_main(argv=argv, idle_service=idle_init, **kwds)