class Entry(object): """ Represents an Entry in the NetworkTable. """ NEXT_ID = 0 def __init__(self, name, value, manager, auto_id=True): self.MANAGER = manager self.name = name # The entries actual value, should be accessed as Entry().value. self._value = value self.type = get_type(value) # The next ID if it's a server, otherwise an UNDEFINED_ID. if self.MANAGER.is_server and auto_id: self.id = Entry.NEXT_ID Entry.NEXT_ID += 1 else: self.id = UNDEFINED_ID # An indicator of whether or not this entry needs to be sent # over the network. self.dirty = False self.sequence_number = SequenceNumber() # A socket not to send it over. self.ignore = None def copy(self): "Create a shallow copy of the entry." copy = Entry(self.name, self.value, self.MANAGER, False) copy.__dict__ = self.__dict__ return copy def _get_value(self): "Getter for value." return self._value def _set_value(self, value): "Setter for value, auto dirties and queques the entry." self._value = value self.sequence_number.increment() if not(self.dirty): self.MANAGER.add_dirty_entry(self) if self.id != UNDEFINED_ID: self.dirty = True self.ignore = None # With a property calling Entry().value calls the getter and # Entry().value = val calls the setter. value = property(_get_value, _set_value)
def __init__(self, name, value, auto_id=True): self.name = name # The entries actual value, should be accessed as Entry().value. self._value = value self.type = get_type(value) # The next ID if it's a server, otherwise an UNDEFINED_ID. if IS_SERVER and auto_id: self.id = Entry.NEXT_ID Entry.NEXT_ID += 1 else: self.id = UNDEFINED_ID # An indicator of whether or not this entry needs to be sent # over the network. self.dirty = False self.sequence_number = SequenceNumber() # A socket not to send it over. self.ignore = None
def __init__(self, **options): self._set_options(**options) self._seq_number = SequenceNumber(*self._sequenceRange)
class Protocol(object): _seq_number = None _default_opts = {} def _set_options(self, **options): opts = self._default_opts.copy() opts.update(options) # FIXME: This method needs revision self._commandRange = options.get('commandRange', (0x20, 0x7f)) self._sequenceRange = options.get('sequenceRange', (0x20, 0x7f)) def __init__(self, **options): self._set_options(**options) self._seq_number = SequenceNumber(*self._sequenceRange) def build_message(self, command, *params): """ Build a message from a command and parameters with STX, ETX and BCC marks and next Sequence Number. Ready for send as request to the device. """ seq = self._seq_number.next() return self.build_message_with_seq(command, seq, *params) def build_message_with_seq(self, command, seq, *params): """ Build a message from a command, sequence number and parameters see build_message() for more detail. This method is suitable when we need to set an explicit sequence number like when we are implementing an emulation device. """ if isinstance(seq, int): seq = chr(seq) if not self._check_sequence_range(seq): raise OutOfRangeError("seq %r out of valid range (%x, %x)" % \ (seq, self._sequenceRange[0], self._sequenceRange[1])) if not self._check_command_range(seq): raise OutOfRangeError("Command %r out of valid range (%x, %x)" % \ (command, self._commandRange[0], self._commandRange[1])) params = self.build_params(params) msg = symbols.STX + seq + command + params + symbols.ETX bcc = self._makeBCC(msg) return msg + bcc def parse_message(self, message, check_sequence_number=True): if not self._checkBCC(message): received = message[-4:] expected = self._makeBCC(message[:-4]) raise BadBlockCheckCharacterError("received: %r, expected: %r in message: %r " % \ (received, expected, message)) # Cut off bcc message = orig_message = message[:-4] head, stx, message = message.partition(symbols.STX) if head: raise MalformedMessageError("%r isn't the first character in %r" % (stx, orig_message)) message, etx, tail = message.rpartition(symbols.ETX) if tail: raise MalformedMessageError("%r isn't the last character in %r" % (etx, orig_message)) seq_no = message[0] if check_sequence_number: if seq_no != repr(self._seq_number): raise ValueError("Inconsistent sequence number %r, we are waiting for %r" % \ (ord(seq_no), ord(repr(self._seq_number)))) seq_no = '' command = message[1] if not self._check_command_range(command): raise ValueError("Command %r out of valid range" % command) params = self.parse_params(message[2:]) if seq_no: return [ord(seq_no), command] + params else: return [command] + params def build_params(self, params): "Build a stream with parameters separated by self.FS (FieldSeparator)" if not params: return "" return symbols.FS + symbols.FS.join(params) def parse_params(self, s): "Return a list of parameters" if not s: return [] orig_s = s head, fs, s = s.partition(symbols.FS) if head: raise MalformedMessageError("%r isn't the first character in parameters substring %r" % \ (fs, orig_s)) return s.split(symbols.FS) def _makeBCC(self, s): "Return Block Check Character of message" return "%.4X" % sum([ord(c) for c in s]) def _checkBCC(self, message): "Return True if Block Check Character its OK, False otherwise" return self._makeBCC(message[:-4]) == message[-4:].upper() def _check_command_range(self, command): assert len(command) == 1, "Command isn't a character" return self._commandRange[0] < ord(command) < self._commandRange[1] def _check_sequence_range(self, sequence): assert len(sequence) == 1, "Sequence isn't a character" return self._sequenceRange[0] < ord(sequence) < self._sequenceRange[1]