def parseNodeNum(self, cmd): # Make sure the command even has enough arguments for there to be a # node number argument present! if not len(cmd.cmdArgs) >= 1: logger.error( "CommandHandler.parseNodeNum(): I expected this command to have at least one argument, a node number. It doesn't." ) return None # Try parsing the first argument as a node number. If this fails, # log an error and use the invalid node number '-1'. try: cmd.nodeNum = int(cmd.cmdArgs[0]) except ValueError: logger.error( "CommandHandler.parseNodeNum(): I expected the first argument to this command to be a node number, an integer. It isn't. Using -1 instead." ) cmd.nodeNum = -1 # Invalid value. return # No point in setting the component or skipping the arg. # Change the component name in this thread's logging context to the # name of the node that sent this command. When we finish processing # the command later, we can switch the component back to "server" logmaster.setComponent("node#%d" % cmd.nodeNum) # Now that we've parsed the node number into its own attribute, # it doesn't need to be in the arg list any more. Strip it off arg list. cmd.cmdArgs = cmd.cmdArgs[1:] return cmd.nodeNum
def process(this, msg: communicator.Message): # If we're not already in the CommandHandler worker thread, then # do the work in that thread, in the background. if threading.current_thread( ) != this: # Ensure we're in worker thread. this(lambda: this.process(msg)) return self = this # Our thready self is this very object that we are operating on. # logger.debug("CommandHandler.process(): Processing the message: [%s]..." % msg.data.strip()) try: cmd = Command(msg) # Parse message into command/argument words. except EmptyCommand: logger.info( "CommandHandler.process(): Ignoring empty command [%s]." % msg.data) return # For the moment, we are assuming that all commands are originating # from sensor nodes, and that the first argument to every command # is the node's ID (0,1,2,3). However, this is really an unnecessary # convention for all but the POWERED_ON message, because all subsequent # messages from a given node will be received on a Connection that is # already associated with that node. So, consider changing this. It # would require some extensive rewriting though. Also, even if we don't # do that, really, we should check for the node number only if this is a # command that isn't coming from the user (operator of server app), as # opposed to coming from a remote node. Should Messages be marked # with their source? Or should we just be more flexible in our parsing? # Some commands may not make sense to associate with nodes at all, e.g. # a server shutdown command. Need to think this through properly, and # figure out what's really the right way to handle things. nodenum = self.parseNodeNum( cmd) # Extract node number from command line. #\_ Also updates component in log context. # Now that we know the node number that this command claims to be coming # from, we take this at face value, and assume that the entire connection # on which we received that message is the main connection associated with # that node number, and change the connection window's title accordingly. # Please observe that this is a very brittle thing to do, because it only # really makes sense for node-specific commands received over a MainServer # connection. See the comment above the previous line of code. So, for # example, if the user tries to type a command on the console, the following # line will at present cause the command-handler thread to raise an exception # and exit, effectively crippling the server. if hasattr( msg.conn, 'term' ): # Is the connection this message came from even associated with a terminal widget? msg.conn.term.set_title("Node #%d Main Server Connection #%d" % (nodenum, msg.conn.cid)) # Finally, we are ready to try dispatching the command for execution. try: # Note to self, the "msg" above, a Communicator message, # does not have exactly the same fields as the original # Message class objects defined in this file. This may # cause problems. Need to fix them. - Obsolete comment? self.dispatch_command(cmd) except Exception as e: logger.error( "CommandHandler.process(): Caught an exception [%s] while dispatching command [%s]. Ignoring." % (str(e), cmd.cmdString)) finally: # Most command handlers will temporarily change the "component" # field in this thread's logging context to the name of whatever # node sent the command, to facilitate debugging. Here, we change # it back to "server" to avoid confusion when debugging the command # parse-and-dispatch process. logmaster.setComponent("server")
def process(this, msg: communicator.Message): # If we're not already in the CommandHandler worker thread, then # do the work in that thread, in the background. if threading.current_thread() != this: # Ensure we're in worker thread. this(lambda: this.process(msg)) return self = this # Our thready self is this very object that we are operating on. # logger.debug("CommandHandler.process(): Processing the message: [%s]..." % msg.data.strip()) try: cmd = Command(msg) # Parse message into command/argument words. except EmptyCommand: logger.info("CommandHandler.process(): Ignoring empty command [%s]." % msg.data) return # For the moment, we are assuming that all commands are originating # from sensor nodes, and that the first argument to every command # is the node's ID (0,1,2,3). However, this is really an unnecessary # convention for all but the POWERED_ON message, because all subsequent # messages from a given node will be received on a Connection that is # already associated with that node. So, consider changing this. It # would require some extensive rewriting though. Also, even if we don't # do that, really, we should check for the node number only if this is a # command that isn't coming from the user (operator of server app), as # opposed to coming from a remote node. Should Messages be marked # with their source? Or should we just be more flexible in our parsing? # Some commands may not make sense to associate with nodes at all, e.g. # a server shutdown command. Need to think this through properly, and # figure out what's really the right way to handle things. nodenum = self.parseNodeNum(cmd) # Extract node number from command line. # \_ Also updates component in log context. # Now that we know the node number that this command claims to be coming # from, we take this at face value, and assume that the entire connection # on which we received that message is the main connection associated with # that node number, and change the connection window's title accordingly. # Please observe that this is a very brittle thing to do, because it only # really makes sense for node-specific commands received over a MainServer # connection. See the comment above the previous line of code. So, for # example, if the user tries to type a command on the console, the following # line will at present cause the command-handler thread to raise an exception # and exit, effectively crippling the server. if hasattr( msg.conn, "term" ): # Is the connection this message came from even associated with a terminal widget? msg.conn.term.set_title("Node #%d Main Server Connection #%d" % (nodenum, msg.conn.cid)) # Finally, we are ready to try dispatching the command for execution. try: # Note to self, the "msg" above, a Communicator message, # does not have exactly the same fields as the original # Message class objects defined in this file. This may # cause problems. Need to fix them. - Obsolete comment? self.dispatch_command(cmd) except Exception as e: logger.error( "CommandHandler.process(): Caught an exception [%s] while dispatching command [%s]. Ignoring." % (str(e), cmd.cmdString) ) finally: # Most command handlers will temporarily change the "component" # field in this thread's logging context to the name of whatever # node sent the command, to facilitate debugging. Here, we change # it back to "server" to avoid confusion when debugging the command # parse-and-dispatch process. logmaster.setComponent("server")