def load_vn_from_module(self, module, checked=False, **kwargs): """Load virtual node from an imported module. Instantiates a virtual node from a class defined in provided module. Such module must have been already imported and should contain only one virtual node class that is a child of 'nodes.BaseVirtualNode'. That last feature can be checked passing an argument 'checked' as False. Args: module: Module containing virtual node's definition. checked (boolean): A flag indicating whether module's content (only one BaseVirtualNode's child class) has been checked or not. """ if not checked: if self.is_vn_ill_defined(module.__name__): notice( self, "Provided module '" + module.__name__ + ".py' was ill defined and could not be loaded.") return for name in dir(module): cls = getattr(module, name) if inspect.isclass(cls): if "BaseVirtualNode" in str(cls.__mro__[-2]): self.vn_class = cls else: notice(self, "Error - Virtual node class ill defined.") return self.set_node(self.vn_class, **kwargs)
def wait_for_new_port(self, filter_term=None, time_limit=10): """Wait for a new port to be created. This function waits for the user to connect an interface device and returns its new serial port. Args: filter_term (str): Optional serial port filter, depends on interface type and operating system. time_limit (float): Maximum number of seconds to wait for a new port to be created. Returns: A list of new serial ports. False, otherwise. """ timer_count = 0 old_ports = scan_serial_ports(filter_term) num_old_ports = len(old_ports) while True: time.sleep(0.25) timer_count += 0.25 if timer_count > time_limit: notice(self.owner, 'TIMEOUT while trying to acquire a new port.') return False new_ports = scan_serial_ports(filter_term) num_new_ports = len(new_ports) if num_new_ports < num_old_ports: # A device has been unplugged, update info old_ports = list(new_ports) num_old_ports = num_new_ports elif num_new_ports > num_old_ports: # Returns all ports that just appeared return list(set(new_ports) - set(old_ports))
def acquire_port_and_connect(self, interface_type): """Acquire a serial port and connect to it. Tries to detect a serial port based on the provided interface type. If no serial port is detected, it waits some seconds (10 by default) for a new one to be created. Args: interface_type (str): Type of interface. Returns: Call to connect function if a port was detected. Exits, otherwise. """ serial_filter = self.get_serial_filter_terms(interface_type) available_ports = get_available_serial_ports(serial_filter) if len(available_ports) == 1: # Connects to the only available port that matches the filter. self.portName = available_ports[0] return self.connect() else: notice( self, "Trying to acquire serial port. You have 10 seconds to " "plug an interface in.") new_ports = self.wait_for_new_port(serial_filter) if new_ports: if len(new_ports) > 1: notice( self.owner, "Could not acquire. Multiple ports plugged in " "simultaneously.") return else: self.portName = new_ports[0] return self.connect()
def transmit(self, data): """Add data to transmit queue. As a thread handles data transmission, it continuously checks the transmit queue and sends data when added by this function. String data is converted to a list by default. """ if self.isConnected: self.transmitQueue.put(data) else: notice(self, 'Serial interface is not connected.')
def __getattr__(self, attribute): """Forward any unsupported call to the shell onto the node.""" if self.node is not None: # Shell contains a node. if hasattr(self.node, attribute): # node contains requested attribute return getattr(self.node, attribute) else: notice(self, "Node does not have requested attribute.") raise AttributeError(attribute) else: notice(self, "Node has not been initialized.") raise AttributeError(attribute)
def publish_settings(self): init_message = '' if self.name: init_message += "Name: " + self.name + '\n' if self.interface: init_message += "Provided interface: True" + '\n' else: init_message += "Provided interface: False" + '\n' if self.persistence: init_message += "Persistence file: " + self.persistence.filename + '\n' else: init_message += "Persistence file: False" + '\n' notice(self, init_message)
def load_vn_from_url(self, url, **kwargs): """Load virtual node from a url. This functions gets the text from a provided URL, imports it as a module and loads the defined virtual node. Args: url (str): URL directing to virtual node's definition. """ resp = requests.get(url) notice(self, resp.text) vn_imported_module = self.import_vn_module(resp.text) self.load_vn_from_module(vn_imported_module, checked=True, **kwargs)
def serialize(self, packet): """Convert packet into a string for transmission over a serial port. Uses ASCII characters. Returns: Packet as string if it was a list or a string. False, otherwise. """ if type(packet) == list: return ''.join([chr(byte) for byte in packet]) elif type(packet) == str: return packet else: notice(self, "Error: Packet must be either a list or a string.") return False
def set_node(self, vn_class, **kwargs): """Sets contained or linked node. Creates an instance of selected virtual node class and executes its 'init()' method. Owner, name and interface from this shell are passed to contained node. Args: vn_class (Class): Virtual node class to be instantiated. kwargs: Arguments to be passed onto the virtual node's initialization. """ self.node = vn_class(self.owner, **kwargs) notice(self, "Node assigned to '" + self.name + "' node shell.") self.node.shell = self self.node.name = self.name self.node.interface = self.interface self.node.init(**self.node.initKwargs)
def init_after_set(self): """Initialize after setting. Begins connection procedure. First, it tries to connect to provided port name; if there is no port name, it waits for a new port to be created based on a provided interface type. """ if self.portName: self.connect(self.portName) elif self.interfaceType: # if an interface type is provided, auto-acquire self.acquire_port_and_connect(self.interfaceType) else: notice( self, "Serial interface could not be initialized. A port name or an " "interface type are required.")
def __getattr__(self, attribute): """Forward attribute calls to the contained interface. Args: attribute: Contained interface's attribute to be called. Returns: Contained interfaces's attribute. """ if self.contained_interface is not None: if hasattr(self.contained_interface, attribute): return getattr(self.contained_interface, attribute) else: notice(self, "Interface does not have requested attribute.") raise AttributeError(attribute) else: notice(self, "Interface shell is empty.") raise AttributeError(attribute)
def run(self): """Define code to be ran by the transmit thread. Gets a packet from transmit queue, converts it into a string and transmits it. """ while True: transmit_state, transmit_packet = self.get_transmit_packet() if transmit_state: if self.port: self.port.write( self.serialize(transmit_packet).encode('utf-8')) else: notice( self, "Cannot transmit. No serial port " "initialized.") time.sleep(0.0005) pass
def is_vn_ill_defined(self, vn_module): """Check whether a virtual node is ill defined or not. Makes sure that selected virtual node contains one and only one user-defined virtual machine class, child of 'nodes.BaseVirtualNode' class. Args: vn_module: Virtual node module to be analyzed. Returns: True when selected module contains none or more than a unique virtual node. False otherwise. """ num_of_vn_cls = 0 for name, class_data in sorted(pyclbr.readmodule(vn_module).items(), key=lambda x: x[1].lineno): supers_list = [] if hasattr(class_data, 'super'): class_super = class_data.super[0] if hasattr(class_super, 'name'): while class_super != 'object': supers_list.append(class_super.name) class_super = class_super.super[0] supers_list.append('object') else: supers_list.append(class_super) if any('BaseVirtualNode' in super_ for super_ in supers_list): num_of_vn_cls += 1 if num_of_vn_cls == 0: notice(self, "Error: No virtual node defined.") return True elif num_of_vn_cls > 1: notice( self, "Error: More than a unique virtual node defined in a " "single file.") return True return False
def connect(self, port_name=None): """Connect to serial port. Connects to specified port name. Tries to connect to port assigned on instantiation if no port is specified as argument. Args: port_name (str): Name of serial port. """ if port_name: port = port_name elif self.portName: port = self.portName else: notice(self, 'No port name provided.') return try: self.port = serial.Serial(port, self.baudRate, timeout=self.timeOut) except serial.SerialException: notice(self, "Error when opening serial port " + str(port)) return self.port.flushInput() self.port.flushOutput() notice(self, "Port " + str(port) + " connected successfully.") # Some serial ports need some time between opening and transmission time.sleep(2) self.isConnected = True self.start_transmitter()
def get_serial_filter_terms(self, interface_type=None): """Get filter terms for serial ports. According to the operating system, selects a type of filter and the filter term. For example, in the case of Arduino, the type is 'manufacturer' and the filter term is 'Arduino'. This implementation allows the inclusion of new interfaces. Args: interface_type (str): Type of interface, 'ftdi' for FTDI devices, 'Arduino' for Arduino and 'generic serial' for others. Returns: A list of filter type and filter term respectively, if found on the dictionary. False, otherwise. Note: Currently, the type of filter should be one attribute of 'serial.tools.list_ports.ListPortInfo' class, supported by your operating system (Windows, Linux os MacOS). Note: The use of interface type 'genericSerial' should be avoided because many devices could share the provided filter term. """ ftdi_terms = { 'Windows': ['manufacturer', 'FTDI'], 'Linux': ['manufacturer', 'FTDI'], 'Darwin': ['manufacturer', 'FTDI'] } arduino_terms = { 'Windows': ['manufacturer', 'Arduino'], 'Linux': ['manufacturer', 'Arduino'], 'Darwin': ['manufacturer', 'Arduino'] } generic_serial_terms = { 'Windows': ['device', 'COM'], 'Linux': ['device', 'tty'], 'Darwin': ['device', 'tty.'] } terms_dict = { 'ftdi': ftdi_terms, 'arduino': arduino_terms, 'genericSerial': generic_serial_terms } op_sys = platform.system() # nominally detects the system if interface_type in terms_dict: if op_sys in terms_dict[interface_type]: return terms_dict[interface_type][op_sys] else: notice( self, "Operating system support not found for interface type '" + interface_type + "'") return False else: notice( self, "Interface support not found for interface type '" + interface_type + "'") return False
def do(self): notice(self, "It does work.")