'''
Created on Sep 19, 2020
@author: esdev
'''
import time
import zmq
import logging
import importlib
from itertools import count
from slacm.exceptions import LoadError
from slacm.timer import TimerPort
from slacm.pub import PublisherPort
from slacm.sub import SubscriberPort
from slacm.req import RequestPort
from slacm.rep import ReplyPort
from slacm.qry import QueryPort
from slacm.ans import AnswerPort
from slacm.component import Component,ComponentThread
[docs]
class Instance(object):
'''
Class to represent a component instance
'''
_modules = {}
@property
def modules(self):
'''
Dictionary to maintain the loaded modules.
'''
return self._modules
@modules.setter
def modules(self,val):
self._modules = val
_portTypes = {
"PubPort" : PublisherPort,
"SubPort" : SubscriberPort,
"ReqPort" : RequestPort,
"RepPort" : ReplyPort,
"QryPort" : QueryPort,
"AnsPort" : AnswerPort,
"TimPort" : TimerPort
}
def __init__(self, parent, model):
'''
Consruct for an instance. Loands the module for the component, constructs the
component, and its ports.
:param parent: parent actor
:param model: instance model
'''
self.logger = logging.getLogger(__name__)
self.parent = parent
self.name = model.name
self.type = model.type
self.context = parent.childContext
self.disco = self.parent.get_disco()
self.netInfo = self.parent.get_netInfo()
self.typeName = self.type.name
self.params = self.parent.get_comp_params(self.name)
self.args = self.params if self.params else {}
self.qualName = '%s.%s.%s' % (self.parent.name,self.name,self.typeName)
self.logger.info('Instance.__init__(%s)',self.qualName)
self.load()
self.class_ = getattr(self.module_, self.typeName)
self.class_.OWNER = self # Trick to set the OWNER of the component
self.component = self.class_(**self.args) # Run the component constructor
self.class_.OWNER = None
self.thread = None
self.ports = {}
self.port_index = count(0)
for port in self.type.ports:
_class = self._portTypes[port.__class__.__name__]
self.ports[port.name] = _class(self,port.name,port)
setattr(self.component,port.name,self.ports[port.name])
[docs]
def get_next_index(self):
'''
Returns the next port index
'''
return next(self.port_index)
[docs]
def getActor(self):
'''
Returns the parent actor object
'''
return self.parent
[docs]
def get_netInfo(self):
'''
Returns the network information object
'''
return self.netInfo
[docs]
def is_local(self,message):
'''
Returns True if the message is 'host local' for the parent actor.
'''
return self.parent.is_local(message)
[docs]
def load(self):
'''
Load the component implementation code, or retrieve it from the cache.
'''
if self.typeName not in self.modules:
try:
self.module_ = importlib.import_module(self.typeName)
self.modules[self.typeName] = self.module_
except Exception as e:
raise LoadError (f"{type(e)}: {e}" % (type(e),e)) from e
else:
self.module_ = self.modules[self.typeName]
[docs]
def sendCommand(self,arg):
'''
Send a command to the component thread
'''
self.command.send_pyobj(arg)
[docs]
def recvResp(self):
'''
Receive response from the component thread
'''
return self.command.recv_pyobj()
[docs]
def setup(self):
'''
Execute the 'setup' phase of component initialization. Create command socket,
launch component thread, and instruct it to execute the 'setup'.
'''
self.logger.info('Instance.setup(%s: %s)',self.name,self.type.name)
self.context = self.parent.childContext
self.command = self.context.socket(zmq.PAIR)
self.command.bind("inproc://part_" + self.name + '_control')
self.thread = ComponentThread(self)
self.thread.daemon = True
self.thread.start()
time.sleep(0.001)
self.sendCommand(Component.SETUP)
_ack = self.recvResp()
[docs]
def finalize(self):
'''
Executhe the 'finalize' phase of component initialization by instructing
the component thread.
'''
self.logger.info('Instance.finalize(%s: %s)',self.name,self.type.name)
self.sendCommand(Component.FINALIZE)
_ack = self.recvResp()
[docs]
def start(self):
'''
Instruct the component thread to run user code.
'''
self.logger.info('Instance.start(%s: %s)',self.name,self.type.name)
self.sendCommand(Component.START)
_ack = self.recvResp()
[docs]
def stop(self):
'''
Instruct the component thread to stop running user code and terminate.
'''
self.logger.info('Instance.stop(%s: %s)',self.name,self.type.name)
self.sendCommand(Component.STOP)
# _ack = self.recvResp()