Example #1
0
def test_release_source_empty(mock_source):
    """Test releasing source.

    Args:
        mock_source: mock FrameIngestorSource.
    """
    frame_ingestor = FrameIngestor()
    with pytest.raises(RuntimeError):
        frame_ingestor.release_source()
Example #2
0
def test_get_frame(mock_source):
    """Test getting a frame.

    Args:
        mock_source: mock FrameIngestorSource.
    """
    frame_ingestor = FrameIngestor(mock_source)
    frame_ingestor.get_frame()

    mock_source.get_frame.assert_called_once()
Example #3
0
def test_register_source(mock_source):
    """Test registering initial source.

    Args:
        mock_source: mock FrameIngestorSource.
    """
    frame_ingestor = FrameIngestor()
    frame_ingestor.register_source(mock_source)
    assert frame_ingestor._source == mock_source
    mock_source.start.assert_called_once()
Example #4
0
def test_release_source(mock_source):
    """Test releasing source.

    Args:
        mock_source: mock FrameIngestorSource.
    """
    frame_ingestor = FrameIngestor(mock_source)
    frame_ingestor.release_source()

    assert not frame_ingestor._source
    mock_source.stop.assert_called_once()
Example #5
0
def test_register_source_swap(mock_source, mock_source2):
    """Test swapping initial source.

    Args:
        mock_source: mock FrameIngestorSource.
        mock_source2: mock FrameIngestorSource.
    """
    frame_ingestor = FrameIngestor(mock_source)
    frame_ingestor.register_source(mock_source2)

    assert frame_ingestor._source == mock_source2
    mock_source.stop.assert_called_once()
    mock_source2.start.assert_called_once()
Example #6
0
def test_init_with_source(mock_source):
    """Test initializing FrameIngestor with a source.

    Args:
        mock_source: mock FrameIngestorSource.
    """
    frame_ingestor = FrameIngestor(mock_source)
    assert frame_ingestor._source == mock_source
    mock_source.start.assert_called_once()
Example #7
0
def test_with_statement(mock_source):
    """Test releasing source.

    Args:
        mock_source: mock FrameIngestorSource.
    """
    with FrameIngestor(mock_source) as frame_ingestor:
        frame_ingestor.get_frame()
    mock_source.stop.assert_called_once()
Example #8
0
    def __init__(self):
        """Initialize signal handlers, API client and run."""
        self._should_close = False
        self._frame_ingestor = FrameIngestor()
        self._batcher = DataBatcher()

        # Add signal handlers
        signal.signal(signal.SIGINT, self._handle_signals)
        signal.signal(signal.SIGTERM, self._handle_signals)

        # Extra configuration
        self._uses_file = False
        self._display_frame = read_int_from_env('DISPLAY_FRAME', 0) > 0
        self._max_frame_width = read_int_from_env(
            'MAX_FRAME_WIDTH', DEFAULT_FRAME_WIDTH,
        )

        # Start client
        self._start()
Example #9
0
def test_with_statement_exception(mock_source):
    """Test passing exceptions from with statement.

    Args:
        mock_source: mock FrameIngestorSource.
    """
    mock_source.get_frame.side_effect = RuntimeError('error')
    with pytest.raises(RuntimeError):
        with FrameIngestor(mock_source) as frame_ingestor:
            frame_ingestor.get_frame()
    mock_source.stop.assert_called_once()
Example #10
0
class Main(object):
    """Handle application lifecycle."""

    def __init__(self):
        """Initialize signal handlers, API client and run."""
        self._should_close = False
        self._frame_ingestor = FrameIngestor()
        self._batcher = DataBatcher()

        # Add signal handlers
        signal.signal(signal.SIGINT, self._handle_signals)
        signal.signal(signal.SIGTERM, self._handle_signals)

        # Extra configuration
        self._uses_file = False
        self._display_frame = read_int_from_env('DISPLAY_FRAME', 0) > 0
        self._max_frame_width = read_int_from_env(
            'MAX_FRAME_WIDTH', DEFAULT_FRAME_WIDTH,
        )

        # Start client
        self._start()

    def _init_counter(self):
        """Set up people counter."""
        try:
            configuration = load_counter_configuration()
        except RuntimeError as ex:
            msg = 'Could not load counter settings: {0}'.format(ex)
            logging.error(msg)
            self._should_close = True
            self._close()
            return
        logging.info('Setting up counter...')
        self._counter = PeopleCounter(configuration)

    def _init_archiver(self, start_time: datetime):
        """Set up archiver and save initial message.

        Args:
            start_time: monitoring start timestamp
        """
        archives_dir = os.getenv('ARCHIVES_DIR', None)
        if not archives_dir:
            logging.error('Missing ARCHIVES_DIR')
            self._should_close = True
            return

        start_time_string = start_time.strftime('%Y-%m-%d_%H_%M_%S')
        archive_path = '{0}/{1}.csv'.format(archives_dir, start_time_string)

        logging.info('Opening archive...')

        try:
            self._archiver = CsvArchiver(archive_path)
        except RuntimeError as ex1:
            msg = 'Could not setup archive: {0}'.format(ex1)
            logging.error(msg)
            self._archiver = None
            self._should_close = True

        try:
            self._archiver.init()
        except RuntimeError as ex2:
            msg = 'Could not init archive: {0}'.format(ex2)
            logging.error(msg)
            self._archiver = None
            self._should_close = True

        # Save initial event
        self._archiver.append_event(
            self._last_batch_time, EventType.monitoring_started,
        )

    def _init_api_connector(self, start_time: datetime):
        """Set up uplink and send initial message.

        Args:
            start_time: monitoring start timestamp
        """
        try:
            configuration = load_api_configuration()
        except RuntimeError as ex:
            msg = 'Could not load API settings: {0}'.format(ex)
            logging.error(msg)
            self._should_close = True
            self._close()
            return
        camera_pk = configuration.camera_pk
        logging.info('Setting up uplink...')
        self._api_connector = ApiConnector(
            camera_pk, configuration, start_time,
        )

    def _start(self):
        """Start the system."""
        self._start_monitoring()

        while not self._should_close:
            # Execute loop
            self._execute_loop()

        self._close()

    def _start_monitoring(self):
        """Start monitoring coroutines."""
        # Save start time
        start_time = datetime.now().astimezone()
        self._last_batch_time = start_time
        logging.info('System starts')

        # Start monitoring
        self._init_counter()
        self._init_archiver(start_time)
        self._init_api_connector(start_time)
        self._init_ingestion_stream()

    def _execute_loop(self):
        """Execute main program loop."""
        # Get camera frame
        frame = self._frame_ingestor.get_frame()
        if self._uses_file and frame is None:
            self._should_close = True
            return

        frame = resize(frame, width=self._max_frame_width)

        if self._display_frame:
            cv2.imshow('Frame', frame)
            cv2.waitKey(1)

        # Update counter
        current_time = datetime.now().astimezone()
        self._counter.update(frame, current_time)
        self._batcher.entered(self._counter.get_entering_list())
        self._batcher.left(self._counter.get_leaving_list())

        # Run batching
        delta = current_time - self._last_batch_time
        if delta.total_seconds() > BATCH_SECONDS:
            self._last_batch_time = current_time

            # Log data
            batch = self._batcher.batch()
            logging.debug('People in: {0}'.format(len(batch.entering)))
            logging.debug('People out: {0}'.format(len(batch.leaving)))

            # Add to archiver
            self._archiver.append(batch)
            self._archiver.flush()

            # Send to API endpoint
            if not self._api_connector.send(batch):
                logging.warn('Could not upload events')

    def _close(self):
        """Close the system."""
        # Save start time
        shutdown_time = datetime.now().astimezone()
        logging.info('System shutting down...')

        # Finish ingestion stream
        if self._display_frame:
            cv2.destroyAllWindows()
        if self._frame_ingestor.has_source():
            logging.info('Closing ingestor source...')
            self._frame_ingestor.release_source()

        # Finish archiver
        if self._archiver:
            logging.info('Closing archive...')
            self._archiver.append_event(
                shutdown_time, EventType.monitoring_ended,
            )
            self._archiver.finalize()

        # Finish monitoring
        if self._api_connector:
            logging.info('Closing uplink...')
            self._api_connector.close(shutdown_time)

    def _init_ingestion_stream(self):
        """Init ingestion stream."""
        logging.info('Opening camera stream...')

        stream_type = read_int_from_env('FRAME_SOURCE')
        if stream_type is None:
            self._should_close = True
            return

        if stream_type == 0:  # File
            self._uses_file = True
            path = os.getenv('SOURCE_FILE_PATH', None)
            source = FileIngestorSource(path)
        elif stream_type == 1:  # Webcam
            stream_num = read_int_from_env('WEBCAM_STREAM_NUM')
            if stream_num is None:
                self._should_close = True
                return
            source = WebcamIngestorSource(stream_num)

        try:
            self._frame_ingestor.register_source(source)
        except RuntimeError as ex:
            msg = 'Could not register_source: {0}'.format(ex)
            logging.error(msg)
            self._should_close = True

    def _handle_signals(self, _signum, _frame):
        """Handle interruption events.

        Args:
            _signum: Signal number
            _frame: Current stack frame
        """
        self._should_close = True
Example #11
0
def test_register_source_invalid():
    """Test preventing sources not matching type."""
    frame_ingestor = FrameIngestor()
    invalid_arg = 'string'
    with pytest.raises(TypeError):
        frame_ingestor.register_source(invalid_arg)
Example #12
0
def test_register_source_empty():
    """Test preventing empty sources."""
    frame_ingestor = FrameIngestor()
    with pytest.raises(TypeError):
        frame_ingestor.register_source()
Example #13
0
def test_init_no_source():
    """Test initializing FrameIngestor without providing source."""
    frame_ingestor = FrameIngestor()
    assert not frame_ingestor._source
Example #14
0
def test_get_frame_no_source():
    """Test exception when getting frame and no source."""
    frame_ingestor = FrameIngestor()
    with pytest.raises(RuntimeError):
        frame_ingestor.get_frame()