def sendingString(self): if self.type is None: # invalid message return '' try: data = self.sendingDataString(self.data, self.type) datalen = len(data) if is_python3(): name = self.name.encode('utf-8') data = data.encode('utf-8') else: name = str(self.name) header = struct.pack(self.packedHeaderDataFormat, self.magic, self.vers, self.size, self.sn, self.sec, self.usec, self.cmd, self.type, self.rows, self.cols, datalen, self.err, self.flags, name) except: import traceback log.log(2, "specmessage error: %s" % traceback.format_exc()) return header + data
def handle_read(self): try: received = self.recv(32768) self.received_strings.append(received) if is_python3(): s = b''.join(self.received_strings) sbuffer = memoryview(s) else: s = ''.join(self.received_strings) sbuffer = buffer(s) consumedBytes = 0 offset = 0 received_messages = [] while offset < len(sbuffer): if self.message is None: try: self.message = SpecMessage.message( version=self.client_version, order=self.client_order) except: import traceback log.log(2, traceback.format_exc()) consumedBytes = self.message.readFromStream(sbuffer[offset:]) if consumedBytes == 0: break offset += consumedBytes if self.message.isComplete(): # dispatch incoming message if self.message.cmd == SpecMessage.HELLO: self.client_order = self.message.packedHeaderDataFormat[ 0] self.client_version = self.message.vers self.clientName = self.message.name self.send_hello_reply(self.message.sn, str(self.server.name)) else: received_messages.append(self.message) self.message = None self.received_strings = [s[offset:]] for message in received_messages: if not self.dispatch_message(message): self.send_error( message.sn, '', 'unsupported command type : %d' % message.cmd) except: import traceback log.log(3, "SpecServer read error. %s" % traceback.format_exc()) return
def readFromStream(self, streamBuf): """Read buffer from stream and try to create a message from it Arguments: streamBuf - string buffer of the last bytes received from Spec Return value : the number of consumed bytes """ consumedBytes = 0 try: while self.bytesToRead > 0 and len(streamBuf[consumedBytes:]) >= self.bytesToRead: if self.readheader: self.readheader = False self.type, self.bytesToRead = self.readHeader(streamBuf[:self.headerLength]) consumedBytes = self.headerLength else: rawdata = streamBuf[consumedBytes:consumedBytes+self.bytesToRead] consumedBytes += self.bytesToRead self.bytesToRead = 0 self.data = self.readData(rawdata, self.type) except BaseException as e: import traceback log.log(2, "error reading message from stream %s" % str(e)) log.log(2, traceback.format_exc()) return consumedBytes
def dispatch_message(self, message): try: if message.cmd == SpecMessage.CHAN_READ: # temporary code (workaround for a Spec client bug) self.get_and_reply(reply_id=message.sn, channame=message.name) elif message.cmd == SpecMessage.CHAN_SEND: self.set_and_reply(reply_id=message.sn, channame=message.name, value=message.data) elif message.cmd in (SpecMessage.CMD_WITH_RETURN, SpecMessage.FUNC_WITH_RETURN): self.run_and_reply(reply_id=message.sn, cmd=message.data) elif message.cmd == SpecMessage.FUNC_WITH_RETURN: self.run_and_reply(reply_id=message.sn, cmd=message.data) elif message.cmd == SpecMessage.CMD: # in this case we allow for multiple commands separated by colon cmdstr = message.data cmds = cmdstr.split(";") for cmd in cmds: self.run_and_reply(reply_id=message.sn, cmd=cmd) elif message.cmd == SpecMessage.REGISTER: if message.name == 'update': log.log(3, "update channel registered !") self.updateRegistered = True else: return False return True except: import traceback log.log(2, traceback.format_exc()) return False
def put(self, event): """Put an event into the queue.""" receiversList = event.receivers EventQueue.queue.mutex.acquire() try: showstatus() was_empty = not EventQueue.queue._qsize() for r in receiversList: if not was_empty: if r.dispatchMode == UPDATEVALUE: for i in range(len(EventQueue.queue.queue)): _r, args = EventQueue.queue.queue[i] if r == _r: del EventQueue.queue.queue[i] break EventQueue.queue._put((r, event.args)) except: import traceback log.log(DEBUG, "could not add event to queue %s" % traceback.format_exc()) finally: EventQueue.queue.mutex.release()
def _update(self): asyncore.loop(timeout=1, count=1) if time.time() - self.last_log_print > self.log_period: log.log(4, "command server is running") self.last_log_print = time.time()
def rawtodictonary(rawstring): """Transform a list as coming from a SPEC associative array to a dictonary - 2dim arrays are transformed top dict with dict entries. In SPEC the key contains \x1c""" if is_python3(): rawstring = bytes(rawstring).decode('utf-8') # from memoryview #raw = bytes(rawstring).decode('utf-8').split(NULL)[:-2] #else: raw = rawstring.split(NULL)[:-2] log.log(2, "converting to dict raw data %s" % raw) data = {} for i in range(0,len(raw) - 1,2): key,val = raw[i], raw[i+1] keyel = key.split("\x1c") if len(keyel) == 1: if key in data: data[key][None] = val else: data[key]=val else: #if keyel[0] in data and type(data[keyel[0]])!=types.DictType: if keyel[0] in data and not isinstance(data[keyel[0]], dict): data[keyel[0]]={ None: data[keyel[0]] } try: data[keyel[0]][keyel[1]] = val except TypeError: data[keyel[0]] = {keyel[1] : val} except KeyError: data[keyel[0]] = {keyel[1] : val} return data
def handle_close(self): self.close() try: self.server.clients.remove(self) except: log.log( 2, "removing client from spec server. but it is gone already") pass
def emit(sender, signal, arguments=()): try: ev = Event(sender, signal, arguments) eventsToDispatch.put(ev) except: log.log(DEBUG, "failed adding event") import traceback log.log(DEBUG, traceback.format_exc())
def set_update_time(self, update_time): self.update_time = update_time is None and \ self.default_update_time or update_time self.update_time /= 1000.0 # in seconds # minimum 10 milliseconds update cycle time if self.update_time < 0.01: log.log(2, "update time of %s secs too short. using 0.01") self.update_time = 0.01
def specConnected(self): """Emit the 'connected' signal when the remote Spec version is connected.""" old_state = self.state self.state = CONNECTED if old_state != CONNECTED: log.log( 1, 'Connected to %s:%s' % (self.host, (self.scanport and self.scanname) or self.port)) SpecEventsDispatcher.emit(self, 'connected', ())
def spec_disconnected(self): """Emit the 'disconnected' signal when the remote Spec version is disconnected.""" # SpecEventsDispatcher.dispatch() old_state = self.state self.state = DISCONNECTED if old_state == CONNECTED: log.log( 1, 'disconnected from %s:%s' % (self.host, self.specname or self.port)) SpecEventsDispatcher.emit(self, 'disconnected', ())
def update(self, data, error, error_code): """Emit the 'replyFromSpec' signal.""" self.data = data self.error = error self.error_code = error_code self.pending = False log.log(2, "emitting replyArrived") SpecEventsDispatcher.emit(self, 'replyArrived', (self, ))
def run(self): if self.is_running(): return # start a thread for automatic update self.updater = spec_updater.spec_updater(method=spec_updater.THREAD, update_func=self._update) log.log(2, "starting spec server with name: %s" % self.name) self.updater.start()
def set_name(self, name): if self.name is not None and name != self.name: new_name = True else: new_name = False if not self.allow_name_change and new_name: if self.name is not None and name != self.name: return if name is None: log.log(2, "cannot start SpecServer without a name.") return self.name = name if new_name: log.log(2, "server name has been changed.") return # start only once and when a name is provided # a name change does not change the port if isinstance(self.name, str): # choose a port number from PORT range host = "" for p in range(MIN_PORT, MAX_PORT): self.server_address = (host, p) try: self.bind(self.server_address) self.bind_ok = True break except: # already used. continue continue else: log.log(2, "ALL server addresses are taken. Cannot start") else: # it is a port number. use that one self.server_address = (self.host, self.name) try: self.bind(self.server_address) self.bind_ok = True except: log.log( 2, "Cannot start server on port %s. Already used?" % self.name) if self.bind_ok: log.log(2, "spec server listening on %s" % str(self.server_address)) self.listen(5)
def set_and_reply(self, reply_id, channame, value): try: ret = self.server.set_value(channame, value) except Exception: import traceback log.log(2, traceback.format_exc()) if ret is None: self.send_error(reply_id, '', 'cannot set channel ' + channame) else: self.send_reply(reply_id, '', ret)
def _update(self, timeout=0.01): try: self.check_connection() if asyncore.socket_map: asyncore.loop(timeout=0.01, count=1) if self.thread_update: self.update_events() except Exception as e: import traceback log.log(2, traceback.format_exc())
def __init__(self, sender, signal, arguments): self.receivers = [] senderId = id(sender) signal = str(signal) self.args = arguments log.log( DEBUG, " creating event for signal %s - senderId is %s" % (signal, senderId)) try: self.receivers = connections[senderId][signal] except: pass
def __init__(self, host=None, name=None, allow_name_change=True, handler=SpecHandler): asyncore.dispatcher.__init__(self) self.handler_class = handler self.updater = None self.last_print = time.time() if host is None: self.host = "localhost" else: self.host = host # only needed to select a particular ip in the computer # self.last_log_print = time.time() self.log_period = 10 self.name = None self.allow_name_change = allow_name_change self.clients = [] self.commands = { "?": [ self.get_help, ], "command_list": [ self.get_command_list, ], "channel_list": [ self.get_channel_list, ], } self.channels = {} self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() if name is not None: self.set_name(name) else: # delay socket creation log.log( 2, "SpecServer created without a name. set a name to start it")
def handle_write(self): # # send all the messages from the queue # while len(self.sendq) > 0: self.outputStrings.append(self.sendq.pop().sendingString()) try: outputBuffer = b''.join(self.outputStrings) sent = self.send(outputBuffer) self.outputStrings = [outputBuffer[sent:]] except: import traceback log.log(3, "error writing message: %s", traceback.format_exc())
def _cleanupConnections(senderId, signal): """Delete any empty signals for sender. Delete sender if empty.""" receivers = connections[senderId][signal] log.log(DEBUG, " number of receivers is %d" % len(receivers)) if len(receivers) == 0: # no more receivers log.log(DEBUG, " - deleting connection for %s" % senderId) signals = connections[senderId] del signals[signal] if len(signals) == 0: # no more signals _removeSender(senderId)
def executeCommand(self, command): if self._conn.server_version < 3: conn_cmd = 'send_msg_cmd_with_return' else: if isinstance(command,str): conn_cmd = self._conn.send_msg_cmd_with_return else: conn_cmd = self._conn.send_msg_func_with_return reply_id = conn_cmd(command) if self.synchronous: log.log(2, "synchronous command waiting for reply") self.wait() return self.retvalue else: return reply_id
def handle_write(self): """Handle 'write' events on socket Send all the messages from the queue. """ log.log(DEBUG, "writing to socket") while len(self.sendq) > 0: self.outputStrings.append(self.sendq.pop().sendingString()) if is_python3(): outputBuffer = b''.join(self.outputStrings) else: outputBuffer = ''.join(self.outputStrings) log.log(DEBUG, "SpecConnection - writing data out") sent = self.send(outputBuffer) self.outputStrings = [outputBuffer[sent:]]
def connect(sender, signal, slot, dispatchMode=UPDATEVALUE): if sender is None or signal is None: return if not callable(slot): return senderId = id(sender) signal = str(signal) signals = {} log.log( DEBUG, "connecting (%s) %s to %s - %s" % (str(sender), senderId, signal, signals)) if senderId in connections: signals = connections[senderId] else: connections[senderId] = signals def remove(object, senderId=senderId): _removeSender(senderId) try: weakSender = weakref.ref(sender, remove) senders[senderId] = weakSender except: pass receivers = [] if signal in signals: receivers = signals[signal] else: signals[signal] = receivers weakReceiver = callableObjectRef(slot) for r in receivers: if r.weakReceiver == weakReceiver: r.dispatchMode = dispatchMode return receivers.append(Receiver(weakReceiver, dispatchMode))
def replyArrived(self, reply): log.log(2, "reply arrived for command") self.reply_pending = False self.retvalue = reply.get_data() if reply.error: if callable(self.__error_callback): try: self.__error_callback(reply.error) except: log.exception("Error while calling error callback (command=%s,spec version=%s)", self.command, self.specapp) self.__error_callback = None else: if callable(self.__callback): try: self.__callback(reply.data) except: log.exception("Error while calling reply callback (command=%s,spec version=%s)", self.command, self.specapp) self.__callback = None
def emit(sender, signal, arguments=()): try: ev = Event(sender, signal, arguments) log.log( DEBUG, "adding event with signal \"%s\" to the queue %s (%s)" % (signal, ev, id(eventsToDispatch))) eventsToDispatch.put(ev) log.log(DEBUG, "is queue empty0 %s" % eventsToDispatch.empty()) except: log.log(DEBUG, "failed adding event") import traceback log.log(DEBUG, traceback.format_exc())
def _removeReceiver(weakReceiver): """Remove receiver from connections""" #log.log(DEBUG, "cleaning up connections, because receiver is removed %s" % str(weakReceiver)) return for senderId in list(connections.keys()): for signal in list(connections[senderId].keys()): receivers = connections[senderId][signal] for r in receivers: log.log( DEBUG, "cleaning up connections, because receiver is removed %s" % str(r)) receivers.remove(r) break log.log( DEBUG, "cleaning up connections, because receiver is removed for signal %s" % str(signal)) _cleanupConnections(senderId, signal)
def dispatch(max_time_in_s=1): t0 = time.time() while True: try: if eventsToDispatch.empty(): break log.log(DEBUG, "is queueue empty %s" % eventsToDispatch.empty()) receiver, args = eventsToDispatch.get() except queue.Empty: log.log(2, "uhmmm") break except: log.log(1, "other exception while dispatching events") import traceback log.log(1, traceback.format_exc()) else: log.log(DEBUG, "got a new event to dispatch with args %s" % str(args)) receiver(args) if max_time_in_s < 0: continue elif (time.time() - t0) >= max_time_in_s: break
def send_msg_cmd_with_return(self, cmd, caller=None): """Send a command message to the remote Spec server, and return the reply id. Arguments: cmd -- command string, i.e. '1+1' """ if not self.is_connected(): raise SpecClientNotConnectedError if not caller: try: caller = sys._getframe(1).f_locals['self'] except KeyError: log.log(2, "caller not identified") caller = None log.log(2, "executing command. reply to be informed to %s" % str(caller)) reply, msg = SpecMessage.msg_cmd_with_return( cmd, version=self.server_version) reply_id = self.__send_msg_with_reply(reply, msg, receiver_obj=caller) return reply_id
def check_connection(self): """Establish a connection to Spec If the connection is already established, do nothing. Otherwise, create a socket object and try to connect. If we are in port scanning mode, try to connect using a port defined in the range from MIN_PORT to MAX_PORT """ if not self.socket_connected: if self.scanports: if self.port is None or self.port > MAX_PORT: self.port = MIN_PORT else: self.port += 1 while not self.scanports or self.port < MAX_PORT: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(0.2) try: if s.connect_ex((self.host, self.port)) == 0: self.set_socket(s) self.handle_connect() break except socket.error: pass # exception could be 'host not found' for example, we ignore it if self.scanports: self.port += 1 else: break elif self.state == WAITINGFORHELLO: if (time.time() - self.waiting_hello_started) > WAIT_HELLO_TIMEOUT: log.log( 2, "socket connected but no response to hello message.forget this socket" ) self.handle_close()