Beispiel #1
0
async def test_to_list_operator(observable: Observable, event_loop):
    result_box = []

    disposable = observable.pipe(to_list()).subscribe(result_box.append,
                                                      loop=event_loop)

    await disposable
Beispiel #2
0
 def add_file(self, file):
     if not exists(file):
         raise FileNotFoundError("The file {0} does not exist".format(file))
     elif file not in self.__files:
         self.__files[file] = Observable()
         # self.__add_watch(file) # TODO: Test case for if this code was there
     else:
         raise FileExistsError("The file {0} is already added".format(file))
Beispiel #3
0
async def test_piping(observable_12: Observable):
    """
    ProxySubject instance by default just passes everything it gets further
    so the result should be the same as without the pipe
    """
    result_box = []

    disposable = observable_12.pipe(ProxySubject()).subscribe(
        result_box.append)
    await disposable

    assert result_box == [1, 2]
Beispiel #4
0
class __FailSafe(Observer):
    __wait_time = None  # Time to wait before triggering fail safe
    __time_last_updated = None

    __thread = None
    __thread_running = False

    observe_fail_safe = Observable()

    def __init__(self):
        self.__wait_time = 80

    def update(self, *args, **kwargs):
        self.__time_last_updated = perf_counter()

    def start(self):
        if not self.__thread or not self.__thread.is_alive():
            self.__thread_running = True
            self.__thread = Thread(target=self.__run,
                                   name="Thread FailSafe.__run")
            self.__thread.start()

    def stop(self):
        self.__thread_running = False

    def wait(self):
        self.__thread.join()

    def __run(self):
        while self.__thread_running:
            if self.__time_last_updated and perf_counter(
            ) > self.__time_last_updated + self.__wait_time:
                self.observe_fail_safe.update_observers()
                self.__time_last_updated = None

                # TODO: Make it only print on debug
                logger.info(
                    "Runs fail_safe because there hasn't been a pulse in {0}s".
                    format(self.__wait_time))

            sleep(1.0)

    def get_wait_time(self):
        return self.__wait_time

    def set_wait_time(self, wait_time):
        self.__wait_time = wait_time
class Client(network_api.NetworkAPI):
    observe_kW = Observable()

    __is_connected = False
    __delay_between_tries = 1.0
    __max_connection_tries = 10

    # TODO: Make this non-hardcoded
    __relays = [False, False, False, False]
    __kilowatt = (0.0, 0.0)

    __update_queue = Queue()
    __running = False
    __Thread = None

    def __init__(self, family, address):
        self.__family = family
        self.__address = address

    def start(self):
        super().start(self.__family, self.__address)

    def _setup(self):
        count = 0
        while not self.__is_connected and count < self.__max_connection_tries:
            try:
                count += 1
                self._socket.connect(self._address)

                self.__is_connected = True
                self.observe_start.register(self.__connected)

                return True
            except (ConnectionRefusedError, FileNotFoundError):
                print("Connecting fail {0} out of {1} tries".format(count, self.__max_connection_tries))
                sleep(self.__delay_between_tries)
            except gaierror as e:
                print("Connecting fail {0} out of {1} tries. It seems that the hostname is wrong".format(count, self.__max_connection_tries))
                sleep(self.__delay_between_tries)

        return False

    def __connected(self):
        self.ask_for_kW_history()
        self.ask_for_relays()

    def __create_package(self, command, status, data):
        self._send({'COMMAND': command, 'STATUS': status, 'DATA': data}, self._socket)

    def ask_for_kW_history(self):
        pass

    def ask_for_relays(self):
        self.__create_package(network_api.COM_RELAY,
                              network_api.STA_RELOAD,
                              None)

    def _receive(self, package, conn):
        if isinstance(package, dict):
            if package['COMMAND'] == network_api.COM_KILOWATT:
                self.__kilowatt = package['DATA']
                self.observe_kW.update_observers(package)
            elif package['COMMAND'] == network_api.COM_RELAY:
                self.__recv_relay(package, conn)

    def __recv_relay(self, package, conn):
        if package['STATUS'] == network_api.STA_UPDATE:
            for relay in self.__relays:
                for package_relay in package['DATA']:
                    if relay[0] == package_relay[0]:
                        relay = package_relay

            self.observe_kW.update_observers(package)
        elif package['STATUS'] == network_api.STA_RELOAD:
            self.__relays = package['DATA']
            self.observe_kW.update_observers(package)

    def is_connected(self):
        return self.__is_connected

    def get_relays(self):
        return self.__relays
class __StateMachine(Observer):
    __start = None
    __end = None
    __current_state = None

    observe_change = Observable()

    def __init__(self):
        self.__start = StateZero()
        pass

    def update(self, *args, **kwargs):
        logger.debug("{0:.3f} kW ({1:.2f}s)".format(args[0], args[1]))

        if self.is_started():
            self.next(args[0], args[1])
        else:
            # TODO: Find a better solution
            self.start()

    def add_relay(self, new_relay):
        if not self.__start.next:
            new_relay.prev = self.__start
            self.__start.next = new_relay
            self.__end = new_relay
            new_relay.setup()
        else:
            current_state = self.__start.next
            while not new_relay.prev:
                # Updates existing relay
                if current_state and current_state.get_relay_number(
                ) == new_relay.get_relay_number():
                    # TODO: fix black python magic. Mads (vismanden)
                    # TODO: siger man kan gøre det med en list istedet for link list.
                    # TODO: og det svære må jeg akende at han har ret
                    new_relay.copy_to(current_state)
                    break
                # Checks that "current_state" is lower then "new_state"
                elif current_state.get_relay_number(
                ) < new_relay.get_relay_number():
                    # Checks that "current_state" is the highest number relay that is lower then "new_relay"
                    if current_state.next and (
                            current_state.next.get_relay_number() <
                            new_relay.get_relay_number()
                            or current_state.next.get_relay_number()
                            == new_relay.get_relay_number()):
                        current_state = current_state.next

                    # Add the new relay
                    else:
                        new_relay.prev = current_state
                        new_relay.next = current_state.next
                        current_state.next = new_relay
                        if not new_relay.next:
                            self.__end = new_relay
                        new_relay.setup()

                # check if "current_state" number is bigger then "new_relay"
                elif current_state.get_relay_number(
                ) > new_relay.get_relay_number():
                    # check if this is the last relay and if it is adds the "new_relay" before this one
                    if not current_state.next:
                        tmp = current_state.prev
                        tmp.next = new_relay
                        new_relay.prev = tmp
                        new_relay.next = current_state
                        current_state.prev = new_relay
                        new_relay.setup()
                    else:
                        current_state = current_state.next

    def start(self):
        if not self.is_started():
            if self.__start.next:
                self.__current_state = self.__start.run()

    def next(self, kW, time_interval):
        if self.__current_state:
            temp = self.__current_state
            self.__current_state = self.__current_state.run(kW)
            # TODO: Make a test case that makes sure that both self.__current_state and temp is check for the attr. 'is_relay_on'
            if not temp == self.__current_state:
                if hasattr(self.__current_state, 'is_relay_on') and hasattr(
                        temp, 'is_relay_on'):
                    self.observe_change.update_observers(
                        (temp.get_relay_number(), temp.is_relay_on()),
                        (self.__current_state.get_relay_number(),
                         self.__current_state.is_relay_on()))
                elif hasattr(self.__current_state, 'is_relay_on'):
                    self.observe_change.update_observers(
                        (self.__current_state.get_relay_number(),
                         self.__current_state.is_relay_on()))
                elif hasattr(temp, 'is_relay_on'):
                    self.observe_change.update_observers(
                        (temp.get_relay_number(), temp.is_relay_on()))
        else:
            raise IndexError("You have to call start first")

    def stop(self):
        if self.__current_state:
            while not self.__start == self.__current_state:
                self.__current_state = self.__current_state.force_state(_OFF)
            self.__current_state = None

    def is_started(self):
        if self.__current_state:
            return True
        else:
            return False

    def get_relay_current_status(self):
        return self.__current_state.get_relay_number(
        ), self.__current_state.is_relay_on()

    def get_relay_full_status(self):
        r = []
        current = self.__start.next
        while current:
            r.append((current.get_relay_number(), current.is_relay_on()))
            current = current.next
        return r
Beispiel #7
0
class Config(Observer):
    class Relay(object):
        __slots__ = [
            "kilo_watt", "switch_on", "switch_off", "gpio_pin", "relay_number"
        ]

    observable = Observable()

    __file = None
    __config = ConfigParser()

    __relay = []

    # TODO: Create a config file for this
    __gpio_pins = {"1": 10, "2": 9, "3": 11, "4": 22}

    def __init__(self, filename):
        self.__file = filename

    def update(self, *args, **kwargs):
        self.load()

    def load(self):
        self.__config.clear()

        # Raise FileNotFoundError if file is missing
        open(self.__file, "r")

        logger.info("Config file loading...")
        self.__config.read(self.__file)

        tmp_relay = []
        for section in self.__config.keys():
            # TODO: Rewrite hardcoding
            match = re.search("Relay[1-4]", section)
            if match:
                r = self.__check_relay(section)
                if r:
                    tmp_relay.append(r)

        if not tmp_relay:
            logger.info("There is not defined any relays")
            raise ImportError(
                "There was no relays specified in the config file '{0}'".
                format(self.__file))

        self.__relay = tmp_relay
        logger.info("Config file Loaded")

        self.observable.update_observers(self.__relay)
        logger.info("Notified observers")

    def __check_relay(self, section):
        tmp = self.Relay()
        try:
            tmp.kilo_watt = self.__to_float(section, "watt")
            tmp.switch_on = self.__to_float(section, "koble_ind")
            tmp.switch_off = self.__to_float(section, "koble_ud")
            tmp.gpio_pin = self.__gpio_pins[section[5:]]
            tmp.relay_number = int(section[5:])
        except ValueError as msg:
            logger.error(msg)
            return None
        return tmp

    def __to_float(self, section, key):
        try:
            value = self.__config[section][key]
        except KeyError:
            raise ValueError("The key '{1}' is missing section '{0}'".format(
                section, key))

        if not value:
            raise ValueError(
                "The key '{1}', in section '{0}' is undefined".format(
                    section, key))
        return float(value.replace(",", "."))

    def save(self):
        logger.info("Save config file")

        self.__config["Relay1"] = {}
        self.__config["Relay1"]["watt"] = "9.6"
        self.__config["Relay1"]["koble_ind"] = "10"
        self.__config["Relay1"]["koble_ud"] = "90"
        self.__config["Relay2"] = {}
        self.__config["Relay2"]["watt"] = "9.6"
        self.__config["Relay2"]["koble_ind"] = "10"
        self.__config["Relay2"]["koble_ud"] = "60"
        self.__config["Relay3"] = {}
        self.__config["Relay3"]["watt"] = "9.7"
        self.__config["Relay3"]["koble_ind"] = "10"
        self.__config["Relay3"]["koble_ud"] = "60"
        self.__config["Relay4"] = {}
        self.__config["Relay4"]["watt"] = "9.7"
        self.__config["Relay4"]["koble_ind"] = "10"
        self.__config["Relay4"]["koble_ud"] = "60"

        with open(self.__file, "w", newline="\r\n") as configfile:
            self.__config.write(configfile)

    def get_relays(self):
        return self.__relay
    def setUp(self):
        self.observable = Observable()

        self.observer1 = AnObserver()
        self.observer2 = AnObserver()
        self.observer3 = AnObserver()
class TestObserve(unittest.TestCase):

    def setUp(self):
        self.observable = Observable()

        self.observer1 = AnObserver()
        self.observer2 = AnObserver()
        self.observer3 = AnObserver()

    def test_remove_all(self):
        self.observable.register(self.observer1)
        self.observable.register(self.observer2)
        self.observable.register(self.observer3)

        self.observable.unregister_all()

        # Should be zero registered
        self.assertEqual(self.observable.count(), 0)

    def test_register(self):
        self.observable.register(self.observer1)
        self.observable.register(self.observer2)
        self.observable.register(self.observer3)

        # Should be three registered
        self.assertEqual(self.observable.count(), 3)
        self.observable.unregister_all()

    def test_unregister(self):
        self.observable.register(self.observer1)
        self.observable.register(self.observer2)
        self.observable.register(self.observer3)

        self.observable.unregister(self.observer2)
        self.observable.unregister(self.observer3)

        # Should be one registered
        self.assertEqual(self.observable.count(), 1)
        self.observable.unregister_all()

    def test_update_observers(self):
        self.observable.register(self.observer1)

        self.observable.update_observers('abc', msg='123')
        self.assertEqual(self.observer1.args[0], 'abc')
        self.assertEqual(self.observer1.kwargs['msg'], '123')
        self.observable.unregister_all()

    def test_type_handler(self):
        self.assertIsNone(self.observable.register(self.observer1))
        self.assertIsNone(self.observable.register(self.observer1.update))
        self.assertIsNone(self.observable.register(AnObserver()))

        error_msg = 'This is not a class-object, method or function'

        with self.assertRaisesRegex(TypeError, error_msg):
            self.observable.register(self.observer1.update())
        with self.assertRaisesRegex(TypeError, error_msg):
            self.observable.register(an_function())
        with self.assertRaisesRegex(TypeError, error_msg):
            self.observable.register(None)

        with self.assertRaisesRegex(TypeError, error_msg):
            self.observable.register(AnClass())
        with self.assertRaisesRegex(TypeError, error_msg):
            self.observable.register(AnClass)
        with self.assertRaisesRegex(TypeError, error_msg):
            self.observable.register(AnObserver)

        with self.assertRaisesRegex(TypeError, error_msg):
            self.observable.register(AnClass.update)
Beispiel #10
0
class __Watt:
    observable_pulse = Observable()
    observable_kW_update = Observable()

    __pulse = deque([])

    __kW = 0.0
    __seconds = 0

    __max_time = 60
    __max_pulses = 5
    # TODO: create a variable to hold the value for "debounce_timeout_ms"

    __pin_interrupts = None

    def start(self, pin_interrupts):
        if self.__pin_interrupts:
            GPIO.del_interrupt_callback(self.__pin_interrupts)
        self.__pin_interrupts = pin_interrupts

        GPIO.add_interrupt_callback(self.__pin_interrupts, self.__add_pulse, edge='rising', debounce_timeout_ms=100)
        GPIO.wait_for_interrupts(threaded=True)

    def stop(self):
        GPIO.stop_waiting_for_interrupts()

    def wait(self):
        # TODO: Make A join
        time.sleep(1.0)

    def __add_pulse(self, gpio_id, value):
        # TODO: How to handle float overflow (time.perf_counter)
        # TODO: Mads (vismanden) siger at det aldrig vil ske
        self.__pulse.append(time.perf_counter())

        self.observable_pulse.update_observers()

        seconds = self.__interval()
        if len(self.__pulse) >= self.__max_pulses:
            self.__calc_kW(seconds)

            while len(self.__pulse) >= self.__max_pulses:
                self.__pulse.popleft()
        elif seconds >= self.__max_time:
            self.__calc_kW(seconds)

            while self.__interval() >= self.__max_time:
                self.__pulse.popleft()
        else:
            logger.debug("An Interrupt happened ({0:.2f}s)".format(seconds))

    def __interval(self):
        return self.__pulse[-1] - self.__pulse[0]

    def __calc_kW(self, seconds):
        self.__kW = ((len(self.__pulse)) / seconds) / 0.036
        self.__seconds = 0
        self.observable_kW_update.update_observers(self.__kW, seconds)

    def get_kW_and_time(self):
        return self.__kW, self.__seconds
Beispiel #11
0
    def create(on_subscribe: typing.Callable[[Observer], Disposable]):
        from lib.observable import Observable

        return Observable(on_subscribe=on_subscribe)
Beispiel #12
0
class __ServerAPI(network_api.NetworkAPI):
    class Connection:
        logged_in = None
        connection_time = None

        def __init__(self, connection_time):
            self.connection_time = connection_time

    observe_kW = Observable()

    # TODO: Make this non-hardcoded
    __relays = [(1, False), (2, False), (3, False), (4, False)]
    __kilowatt = (0.0, 0.0)

    __connections = {}
    __update_queue = Queue()

    __selector = None

    def start(self, family=AF_UNIX, address='/tmp/relay.sock'):
        # TODO: Already exist exception
        if family == AF_UNIX and exists('/tmp/relay.sock'):
            remove('/tmp/relay.sock')

        super().start(family, address)

    def _setup(self):
        self._socket.bind(self._address)
        self._socket.listen(5)
        return True

    def __accept(self, sock: socket):
        conn, addr = sock.accept()
        logger.debug('accepted: {0}, from: {1}'.format(conn, addr))
        conn.setblocking(False)
        self.__selector.register(conn, EVENT_READ)
        self.__connections[conn] = self.Connection(time.time())
        self.__connected(conn)

    def _selector_handler(self, selector):
        self.__selector = selector
        return self.__accept

    def _exception_handler(self, conn, exception):
        if isinstance(exception, EOFError):
            # TODO: Log "Lost connection to server"
            self.__close_conn(conn)
        elif isinstance(exception, BlockingIOError):
            # TODO: Log "Lost connection to server"
            self.__close_conn(conn)
        elif isinstance(exception, OSError):
            # TODO: Log "Lost connection to server"
            self.__close_conn(conn)
        else:
            raise NotImplementedError(
                'There is not implemented a exception handler for {0}'.format(
                    exception.__class__.__name__))

    def __close_all_conn(self):
        for conn in self.__connections:
            conn.send('Bye, Bye...'.encode('utf-8'))
            self.__close_conn(conn)

    def __close_conn(self, conn):
        self.__selector.unregister(conn)
        del self.__connections[conn]
        conn.close()

    def _teardown(self):
        self.__close_all_conn()
        self.__running = False
        if self._family == AF_UNIX and exists(self._address):
            remove(self._address)

    def get_relays(self):
        return self.__relays

    def __create_package(self, command, status, data, conn=None):
        if conn:
            self._send({
                'COMMAND': command,
                'STATUS': status,
                'DATA': data
            }, conn)
        else:
            for conn in self.__connections:
                self._send({
                    'COMMAND': command,
                    'STATUS': status,
                    'DATA': data
                }, conn)

    def __connected(self, conn):
        def ask_for_kW_history(conn):
            pass

        def ask_for_relays(conn):
            self.__create_package(network_api.COM_RELAY,
                                  network_api.STA_RELOAD, None, conn)

        ask_for_kW_history(conn)
        ask_for_relays(conn)

    def _receive(self, package, conn):
        def __recv_relay(package, conn):
            if package['STATUS'] == network_api.STA_UPDATE:
                for relay in self.__relays:
                    for package_relay in package['DATA']:
                        if relay[0] == package_relay[0]:
                            relay = package_relay

                package[
                    'DATA'] = self.__relays  # Sends all the states of all the relays
                self.observe_kW.update_observers(package)
            elif package['STATUS'] == network_api.STA_RELOAD:
                self.__relays = package['DATA']
                self.observe_kW.update_observers(package)

        if isinstance(package, dict):
            if self.__connections[conn].logged_in:
                if package['COMMAND'] == network_api.COM_KILOWATT:
                    self.__kilowatt = package['DATA']
                    self.observe_kW.update_observers(package)
                elif package['COMMAND'] == network_api.COM_RELAY:
                    __recv_relay(package, conn)
            else:
                if package['COMMAND'] == network_api.COM_LOGIN:
                    # TODO: Add support for cookies
                    if package['STATUS'] == network_api.STA_USER:
                        # TODO: Fix Hardcoding
                        with open('user.txt') as u:
                            with open('pass.txt') as p:
                                if package['DATA'][0] == u.read(
                                ) and package['DATA'][1] == p.read():
                                    self.__connections[conn].logged_in = 1337
                                    self.__connected(conn)
                else:
                    if self.__connections[conn].connection_time > time.time(
                    ) + 60:
                        logger.debug(
                            'Closed connection to {0}, because the client had not logged in within a 60s'
                            .format(conn.family))
                        self.__close_conn(conn)
        else:
            logger.debug('Closed connection to {0}'.format(conn.family))
            self.__close_conn(conn)
Beispiel #13
0
class NetworkAPI:
    __metaclass__ = ABCMeta

    observe_start = Observable()
    observe_stop = Observable()

    __send_queue = Queue()
    __send_running = False
    __send_thread = None

    __recv_queue = Queue()
    __recv_running = False
    __recv_thread = None

    __selector = None
    __selector_queue = Queue()
    __selector_running = False
    __selector_thread = None
    __selector_lock = Lock()

    __reconnect_running = False

    _family = None
    _address = None
    _socket = None

    def __init_socket(self, family, address):
        self._family = family
        if family == AF_UNIX:
            if isinstance(address, str):
                self._address = address
            else:
                raise TypeError('Then AF_UNIX, address must be a path (String)')
        elif family == AF_INET:
            if len(address) == 2:
                self._address = address
            else:
                raise TypeError('Then AF_INET, address must be a tuple (IP/HOST, PORT)')
        else:
            raise TypeError('family is either AF_UNIT or AF_INET')

        self._socket = socket(family=self._family, type=SOCK_STREAM)
        # self._socket.setblocking(False)

    def start(self, family, address):
        if not self.__reconnect_running:
            self.__init_socket(family, address)

            if self._setup():
                self.__selector_setup()
                self.__recv_setup()
                self.__send_setup()
                self.__reconnect_running = True
                self.observe_start.update_observers()

    def __selector_setup(self):
        self.__selector = SyncSelector()
        callobj = self._selector_handler(self.__selector)
        self.__selector.register(self._socket, EVENT_READ, callobj)
        self.__selector_running = True
        self.__selector_thread = Thread(name='NetworkAPI.__selector_run()', target=self.__selector_run)
        self.__selector_thread.start()

    @abstractmethod
    def _selector_handler(self, selector):
        pass

    def __selector_run(self):
        while self.__selector_running:
            events = self.__selector.select(timeout=1)
            try:
                for key, mask in events:
                    if key.data is not None:
                        key.data(key.fileobj)
                    else:
                        data_raw = key.fileobj.recv(4096)  # key.fileobj is a object of the class Socket
                        if data_raw is not None:
                            package = pickle.loads(data_raw)
                            self.__recv_queue.put((package, key.fileobj))

            except BaseException as e:
                self._exception_handler(key.fileobj, e)

    def __selector_teardown(self):
        self.__selector_running = False
        self.__selector.unregister(self._socket)
        self.__selector.close()

    def __recv_setup(self):
        self.__recv_running = True
        self.__recv_thread = Thread(name="NetworkAPI.__recv_run()", target=self.__recv_run)
        self.__recv_thread.start()

    def __recv_run(self):
        while self.__recv_running:
            try:
                package, conn = self.__recv_queue.get(True, 0.1)
                self._receive(package, conn)
            except Empty:
                pass

    def __recv_teardown(self):
        self.__recv_running = False

    def __send_setup(self):
        self.__send_running = True
        self.__send_thread = Thread(name="NetworkAPI.__send_run()", target=self.__send_run)
        self.__send_thread.start()

    def __send_run(self):
        while self.__send_running:
            try:
                package, conn = self.__send_queue.get(True, 0.1)
                data_raw = pickle.dumps(package)
                conn.send(data_raw)

                # TODO: Find out if sleep is still necessary
                sleep(0.01)
            except Empty:
                pass
            except BrokenPipeError as e:
                conn.close()
            except OSError as e:
                conn.close()

    def _send(self, package, conn):
        self.__send_queue.put((package, conn))

    def __send_teardown(self):
        self.__send_running = False

    def stop(self):
        if self.__reconnect_running:
            self.__selector_teardown()
            self.__recv_teardown()
            self.__send_teardown()

            self._socket.close()
            self.__reconnect_running = False
            self._teardown()
            self.observe_stop.update_observers()

    def wait(self):
        self.__selector_thread.join()
        self.__recv_thread.join()
        self.__send_thread.join()

    @abstractmethod
    def _setup(self):
        pass

    @abstractmethod
    def _teardown(self):
        pass

    @abstractmethod
    def _receive(self, package, conn):
        pass

    @abstractmethod
    def _exception_handler(self, conn, exception):
        raise NotImplementedError('The _exception_handler method is not defined in the inherited class')