class TorrentPeerProcessor(TorrentManager): def __init__(self, torrent): TorrentManager.__init__(self, torrent, "Peer processor") self.running = False self.process_thread = CustomThread(self.process, "Peer processor") def start(self): self.running = True self.process_thread.start() def stop(self): self.running = False self.process_thread.join() super().stop() def process(self): while self.running: start_time = current_time() peers_to_process = self.torrent.peer_manager.connected_peers Timing().start_timing("peer_processing") for peer in peers_to_process: peer.metadata_manager.update() peer.download_manager.update_timeout() Timing().stop_timing("peer_processing") spend_time = current_time() - start_time time.sleep(0.1 - (spend_time / 1000))
class PresenceManager(metaclass=Singleton): def __init__(self): self.check_thread = CustomThread(self.check_presence, "Presence checker") self.running = False self.check_interval = 5 self.device_gone_interval = 240 self.on_coming_home = None self.on_leaving_home = None self.anyone_home = True self.pi = sys.platform == "linux" or sys.platform == "linux2" self.device_states = [ DeviceState("Mobiel Jan", "192.168.2.51", self.device_gone_interval), DeviceState("Mobiel Melissa", "192.168.2.50", self.device_gone_interval), ] def start(self): self.running = True self.check_thread.start() def stop(self): self.running = False self.check_thread.join() def check_presence(self): if not self.pi: return while self.running: for device in self.device_states: result = subprocess.call('sudo arping -q -c1 -W 1 ' + device.ip + ' > /dev/null', shell=True) device.set_device_state(result == 0) if self.anyone_home and len( [x for x in self.device_states if x.home_state]) == 0: # left self.anyone_home = False Logger().write(LogVerbosity.Info, "Everybody left home") if self.on_leaving_home is not None: self.on_leaving_home() elif not self.anyone_home and len( [x for x in self.device_states if x.home_state]) > 0: # returned self.anyone_home = True Logger().write(LogVerbosity.Info, "Someone came home") if self.on_coming_home is not None: self.on_coming_home() time.sleep(self.check_interval)
class TorrentNetworkManager(TorrentManager): def __init__(self, torrent): super().__init__(torrent, "network") self.running = True self.torrent = torrent self.speed_log = 0 self.average_download_counter = AverageCounter(self, 3) self.thread = None self._event_id_stopped = EventManager.register_event(EventType.TorrentStopped, self.unregister) def unregister(self): EventManager.deregister_event(self._event_id_stopped) def start(self): Logger().write(LogVerbosity.Info, "Starting network manager") self.thread = CustomThread(self.execute, "Network IO") self.thread.start() def execute(self): while self.running: Timing().start_timing("IO") # Select in/outputs input_peers = self.torrent.peer_manager.get_peers_for_reading() received_messages = [] for peer in input_peers: messages_size, messages = peer.connection_manager.handle_read() if messages_size > 0: self.average_download_counter.add_value(messages_size) time = current_time() received_messages += [(peer, x, time) for x in messages] if len(received_messages) != 0: self.torrent.message_processor.process_messages(received_messages) for peer in self.torrent.peer_manager.connected_peers: peer.download_manager.update_requests() output_peers = self.torrent.peer_manager.get_peers_for_writing() for peer in output_peers: peer.connection_manager.handle_write() Timing().stop_timing("IO") sleep(0.005) def stop(self): self.running = False self.thread.join() super().stop()
class Engine(LogObject): def __init__(self, name, tick_time=1000, parent=None): super().__init__(parent, "Engine:" + name) self.name = name self.tick_time = tick_time self.last_tick = 0 self.running = False self.thread = None self.work_items = [] self.current_item = None self.start_time = 0 self.timing = dict() self.own_time = TimingObject(self.name) # Log props self.current_item_log = "" def runner(self): while self.running: sleep(0.01) if not self.running: break self.last_tick = current_time() self.tick() def start(self): self.running = True self.thread = CustomThread(self.runner, self.name) self.thread.start() def add_work_item(self, name, interval, work_item, initial_invoke=True): self.work_items.append(EngineWorkItem(self, name, interval, work_item, initial_invoke)) def stop(self): self.running = False self.thread.join() def tick(self): tick_time = current_time() cur_list = list(self.work_items) for i in range(len(cur_list)): work_item = cur_list[i] if work_item.last_run_time + work_item.interval < tick_time: Timing().start_timing("Engine item " + work_item.name) self.current_item = work_item self.current_item_log = work_item.name self.start_time = current_time() if not self.running: return result = work_item.action() self.current_item = None self.current_item_log = "" test_time = current_time() work_item.last_run_time = test_time work_item.last_run_duration = test_time - self.start_time if work_item.interval == -1: self.work_items.remove(work_item) elif not result: self.work_items.remove(work_item) if work_item.name not in self.timing: self.timing[work_item.name] = TimingObject(work_item.name) self.timing[work_item.name].add_time(current_time() - self.start_time) Timing().stop_timing("Engine item " + work_item.name) sleep(0) self.own_time.add_time(current_time() - tick_time)
class Peer(TorrentManager): @property def bitfield(self): # Only create a new bitfield when we actually have metadata for it if self.__bitfield is None and not self.torrent.is_preparing: self.__bitfield = Bitfield(self, self.torrent.data_manager.total_pieces) return self.__bitfield @property def state(self): return self._state @state.setter def state(self, value): if self._state != value: old = self._state self._state = value if self.torrent.peer_manager is not None: self.torrent.peer_manager.update_peer(self, old, value) if value == PeerState.Started: self.peer_state_task = CustomThread( self.check_peer_state, "Check peer state " + str(self.id)) self.peer_state_task.start() def __init__(self, id, torrent, uri, source): super().__init__(torrent, "Peer " + str(id)) self.id = id self.torrent = torrent self.uri = uri self._state = PeerState.Initial self.source = source self.connection_manager = None self.download_manager = None self.metadata_manager = None self.extension_manager = None self.__bitfield = None self.communication_state = PeerCommunicationState(self) self.counter = None self.peer_speed = PeerSpeed.Low self.allowed_fast_pieces = [] self.round_trips = 0 self.round_trip_total = 0 self.round_trip_average = 0 self.max_blocks_log = 0 self.speed_log = 0 self.peer_start_task = None self.peer_state_task = None self.peer_stop_task = None self.stop_reason = "Torrent stopping" self.protocol_logger = ProtocolLogger(self) def adjust_round_trip_time(self, time): self.round_trips += 1 self.round_trip_total += time self.round_trip_average = self.round_trip_total / self.round_trips def start(self): Logger().write(LogVerbosity.All, str(self.id) + ' Starting peer') self.state = PeerState.Starting self.connection_manager = PeerConnectionManager(self, self.uri) self.download_manager = PeerDownloadManager(self) self.metadata_manager = PeerMetaDataManager(self) self.extension_manager = PeerExtensionManager(self) self.counter = AverageCounter(self, 3) self.peer_start_task = CustomThread(self.connection_manager.start, "Start peer " + str(self.id)) self.peer_start_task.start() Logger().write(LogVerbosity.Debug, str(self.id) + ' Peer started') def update(self): self.metadata_manager.update() self.download_manager.update() def check_peer_state(self): zero_speed_checks = 0 while self.state == PeerState.Started: if current_time() - self.connection_manager.connected_on < 20000: time.sleep(1) continue if not self.torrent.peer_manager.should_stop_peers(): time.sleep(1) continue if self.counter.total == 0: Logger().write(LogVerbosity.Info, str(self.id) + " stopping not downloading peer") self.stop_async( "Not downloading" ) # Stop since we haven't downloaded anything since connecting return if self.counter.value == 0: zero_speed_checks += 1 if zero_speed_checks >= 10: Logger().write( LogVerbosity.Info, str(self.id) + " stopping currently not downloading peer") self.stop_async( "Not recently downloading" ) # Stop since we haven't downloaded anything in the last 10 seconds return else: zero_speed_checks = 0 time.sleep(1) @staticmethod def add_connected_peer_stat(source): if source == PeerSource.DHT: Stats().add('peers_source_dht_connected', 1) elif source == PeerSource.HttpTracker: Stats().add('peers_source_http_tracker_connected', 1) elif source == PeerSource.UdpTracker: Stats().add('peers_source_udp_tracker_connected', 1) elif source == PeerSource.PeerExchange: Stats().add('peers_source_exchange_connected', 1) def stop_async(self, reason): if self.state != PeerState.Started and self.state != PeerState.Starting: return self.stop_reason = reason self.state = PeerState.Stopping self.peer_stop_task = CustomThread(self.stop, "Peer stopper " + str(self.id), []) self.peer_stop_task.start() def stop(self): if self.state == PeerState.Stopped: return self.state = PeerState.Stopping Logger().write(LogVerbosity.All, str(self.id) + ' Peer stopping') if self.peer_state_task is not None: self.peer_state_task.join() if self.state == PeerState.Stopped: return self.connection_manager.disconnect() self.download_manager.stop() self.extension_manager.stop() self.metadata_manager.stop() self.state = PeerState.Stopped self.peer_start_task.join() self.protocol_logger = None super().stop() self.finish() Logger().write(LogVerbosity.Debug, str(self.id) + ' Peer stopped: ' + self.stop_reason)
class RuleManager(metaclass=Singleton): conditions = { 1: IsBetweenTimeCondition, 2: IsPassingTimeCondition, 3: IsHomeCondition, 4: OnLeavingHomeCondition, 5: OnComingHomeCondition } actions = { 1: ToggleTradfriGroupAction, 2: SetTemperatureAction, 3: ToggleTvAction, 4: PlayRadioAction } def __init__(self): self.running = False self.current_rules = [] self.check_thread = CustomThread(self.check_rules, "Rule checker") self.load_rules() enabled = Database().get_stat("rules_enabled") self.enabled = bool(enabled) def start(self): if Settings.get_bool("slave"): return self.running = True self.check_thread.start() def stop(self): self.running = False self.check_thread.join() def set_enabled(self, enabled): self.enabled = enabled Database().update_stat("rules_enabled", enabled) def check_rules(self): while self.running: Logger().write(LogVerbosity.All, "Checking rules") for rule in self.current_rules: if rule.check(): Logger().write( LogVerbosity.Info, "Executing rule " + rule.name + ": " + rule.description) if self.enabled: try: rule.execute() except Exception as e: Logger().write_error(e, "Rule error") Database().update_rule(rule) time.sleep(10) def update_rule(self, rule_id, active, name, actions, conditions): if rule_id == -1: rule = Rule(-1, name, current_time(), True, 0) self.current_rules.append(rule) else: rule = [x for x in self.current_rules if x.id == rule_id][0] rule.name = name rule.active = active rule.actions = [] for action in actions: rule.add_action(-1, action[0], [x for x in action[1:] if x is not None]) rule.conditions = [] for condition in conditions: rule.add_condition(-1, condition[0], [x for x in condition[1:] if x is not None]) rule.last_execution = 0 Database().save_rule(rule) def remove_rule(self, rule_id): self.current_rules = [x for x in self.current_rules if x.id != rule_id] Database().remove_rule(rule_id) def get_rule(self, rule_id): return [x for x in self.current_rules if x.id == rule_id][0] def get_rules(self): return self.enabled, self.current_rules def get_actions_and_conditions(self): actions = [ ActionModel(id, action.name, action.description, action.parameter_descriptions) for id, action in self.actions.items() ] conditions = [ ActionModel(id, condition.name, condition.description, condition.parameter_descriptions) for id, condition in self.conditions.items() ] return actions, conditions def load_rules(self): db_rules = Database().get_rules() self.current_rules = self.parse_rules(db_rules) def parse_rules(self, rules): result = [] for r in rules: rule = Rule(r.id, r.name, r.created, r.active, r.last_execution) for link in r.links: if link.rule_link_type == "Condition": rule.add_condition(link.id, link.link_type, link.parameters) else: rule.add_action(link.id, link.link_type, link.parameters) result.append(rule) return result @staticmethod def update_check_time(hour, minute): result = datetime.now() if result.hour > hour or (result.hour == hour and result.minute >= minute): result += timedelta(days=1) result = result.replace(hour=hour, minute=minute, second=0, microsecond=0) return result
class PeerConnectionManager(LogObject): @property def ready_for_sending(self): return len(self.to_send_bytes) > 0 and current_time() - self.last_send > 10 @property def ready_for_reading(self): return len(self.buffer) >= self._next_message_length def __init__(self, peer, uri): super().__init__(peer, "connection") self.peer = peer self.uri = uri self.to_send_bytes = bytearray() self.last_send = 0 self.connected_on = 0 self._last_communication = 0 self._peer_timeout = Settings.get_int("peer_timeout") self._connection_timeout = Settings.get_int("connection_timeout") / 1000 self.connection = TcpClient(uri.hostname, uri.port, self._connection_timeout) self.buffer = bytearray() self.buffer_in_size = 0 self.buffer_out_size = 0 self._next_message_length = 1 self._buffer_position = 0 self._receive_state = ReceiveState.ReceiveLength self._receive_buffer_size = 32768 self.in_thread = None self.reading_handshake = True def start(self): self.connected_on = 0 Logger().write(LogVerbosity.All, str(self.peer.id) + ' connecting to ' + str(self.uri.netloc)) Stats().add('peers_connect_try', 1) if not self.connection.connect(): Stats().add('peers_connect_failed', 1) if self.peer is not None: Logger().write(LogVerbosity.All, str(self.peer.id) + ' could not connect to ' + str(self.uri.netloc)) self.peer.stop_async("Can't connect") return if self.peer.state is not PeerState.Starting: return self.connected_on = current_time() self.peer.add_connected_peer_stat(self.peer.source) Stats().add('peers_connect_success', 1) Logger().write(LogVerbosity.Debug, str(self.peer.id) + ' connected to ' + str(self.uri.netloc)) self.peer.state = PeerState.Started self.in_thread = CustomThread(self.socket_reader, "Peer " + str(self.peer.id) + " input") self.in_thread.start() def socket_reader(self): while self.peer.state == PeerState.Started: if len(self.buffer) > 1000000: time.sleep(0.01) continue data = self.connection.receive_available(self._receive_buffer_size) if data is None or len(data) == 0: self.peer.stop_async("Reading error") break self._last_communication = current_time() self.buffer += data self.buffer_in_size = len(self.buffer) time.sleep(0.001) def handle_read(self): received_messages = [] messages_size = 0 while True: buffer_size = len(self.buffer) if buffer_size - self._buffer_position < self._next_message_length: # cant read another complete message break # form messages from the buffer: if self._receive_state == ReceiveState.ReceiveLength: if self.reading_handshake: offset, id_length = read_byte_as_int(self.buffer, 0) msg_length = id_length + 49 # Handshake sends the length of the id, the rest of the handshake takes 49 bytes if msg_length > len(self.buffer): break self.reading_handshake = False else: offset, msg_length = read_integer(self.buffer, self._buffer_position) self._buffer_position += 4 self._next_message_length = msg_length self._receive_state = ReceiveState.ReceiveMessage if self._next_message_length < 0 or self._next_message_length > 17000: Logger().write(LogVerbosity.Info, "Invalid next message length: " + str(self._next_message_length)) self.peer.stop_async("Invalid next msg length") break else: total_data = self._buffer_position + self._next_message_length message = bytes(self.buffer[self._buffer_position: total_data]) self._buffer_position = total_data self._next_message_length = 4 self._receive_state = ReceiveState.ReceiveLength messages_size += len(message) received_messages.append(message) self.buffer = self.buffer[self._buffer_position:] self._buffer_position = 0 self.buffer_in_size = len(self.buffer) return messages_size, received_messages def handle_write(self): success = self.connection.send(self.to_send_bytes) self.to_send_bytes.clear() self.buffer_out_size = 0 self.last_send = current_time() self._last_communication = current_time() if not success: self.peer.stop_async("Write error") def send(self, data): self.to_send_bytes.extend(data) self.buffer_out_size = len(self.to_send_bytes) def disconnect(self): if self.connected_on != 0: Logger().write(LogVerbosity.Debug, str(self.peer.id) + ' disconnected') self.connection.disconnect() self.to_send_bytes.clear() if self.in_thread is not None: self.in_thread.join() self.buffer.clear() self.peer = None