class ModbusTCPDevice(Component): MODBUS_PORT = 502 DEFAULT_INTERVAL = 60 * 15 PREFIX = 'XXXX' def __init__(self, ip): super(ModbusTCPDevice, self).__init__() self.sample_timer = None self.ip = ip self.sn = ip self.registers = [] self.conn = None self._set_channel() self.queued_time = time.time() self.time_d = 0 self.sample_pending = False self.asap = False def set_asap_sampling(self, flag): if self.sample_timer is None: return False self.sample_timer.persist = not flag self.asap = flag if flag: self.fire(sample(), self) else: self.sample_timer.reset(self.sample_timer.interval) def _set_channel(self): self.channel = self.__class__.__name__ + str(id(self)) def started(self, *args): self.conn = ModbusClient(host=self.ip[0], port=self.ip[1], auto_open=True) self.fire(sample(), self) self.sample_timer = Timer(self.DEFAULT_INTERVAL + random.uniform(0, 1), sample(), self, persist=True).register(self) return def update_interval(self, interval, stray=1): if self.sample_timer is not None: self.sample_timer.reset(interval + random.uniform(0, stray)) return True return False def sample_success(self, addr, regs): tab = prettytable.PrettyTable() # tab.field_names = [str(x) for x in xrange(0xf)] num_rows = (len(regs) / 0x10) + 1 for x in xrange(0x10): column = [stringify_reg([_]) for _ in regs[x::0x10]] column += [''] * (num_rows - len(column)) tab.add_column(str(x), column) logging.debug("Sample from %s address %s:\n%s", addr, self.sn, tab.get_string()) def sample(self): if self.sample_pending: logging.warn("%s already has a sample pending.", self.get_dev_id()) else: logging.debug("%s Spawning sample task", self.sn) self.queued_time = time.time() self.sample_pending = True self.fire(task(self._sample), "sample_worker") def _sample(self): logging.debug("Sampling from %s", self.sn) for t in self.registers: try: regs = self.conn.read_holding_registers(t[0], t[1]) except Exception as e: logging.error(e) else: if regs is None: logging.warn("%s sent empty sample", self.get_dev_id()) else: self.fire(sample_success(t[0], regs), self) self.time_d = time.time() - self.queued_time logging.debug("Sampling %s took %s", self.get_dev_id(), self.time_d) self.sample_pending = False if self.asap: logging.debug("%s firing ASAP sample", self.get_dev_id()) self.fire(sample(), self) if self.time_d > self.sample_timer.interval: logging.warn("Sampling %s took %s! (> %s)", self.get_dev_id(), self.time_d, self.sample_timer.interval) return def get_dev_id(self): return "{}-{}".format(self.PREFIX, self.sn)