Exemplo n.º 1
0
def run_async(args):
    logger.info("Starting up!")
    loop = asyncio.get_event_loop()

    logger.debug("Setting up Demo Satellite")
    demo_sat = DemoSat(name="Space Oddity")

    logger.debug("Setting up MajorTom")
    gateway = GatewayAPI(host=args.majortomhost,
                         gateway_token=args.gatewaytoken,
                         basic_auth=args.basicauth,
                         command_callback=demo_sat.command_callback,
                         cancel_callback=demo_sat.cancel_callback,
                         http=args.http)

    logger.debug("Connecting to MajorTom")
    asyncio.ensure_future(gateway.connect_with_retries())

    logger.debug("Sending Command Definitions")
    asyncio.ensure_future(
        gateway.update_command_definitions(system=demo_sat.name,
                                           definitions=demo_sat.definitions))

    logger.debug("Starting Event Loop")
    loop.run_forever()
Exemplo n.º 2
0
def run_sync(args):
    logger.debug("Starting Event Loop")
    loop = asyncio.get_event_loop()

    # Gateways are the link between the generic interfaces of Major Tom and the specifics of your
    # satellite(s) and groundstation(s). They can be designed to handle one or more satellites and
    # one or more groundstations.

    logger.debug("Setting up Gateway")
    gateway = Gateway()

    # Gateways use a websocket API, and we have a library to make the interface easier.
    # We instantiate the API, making sure to specify both sides of the connection:
    #  - The Major Tom side, which requires a host and authentication
    #  - The Gateway side, which specifies all the callbacks to be used when Major Tom communicates with this Gateway
    logger.debug("Setting up websocket connection")
    websocket_connection = GatewayAPI(
        host=args.majortomhost,
        gateway_token=args.gatewaytoken,
        basic_auth=args.basicauth,
        http=args.http,
        command_callback=gateway.command_callback,
        error_callback=gateway.error_callback,
        rate_limit_callback=gateway.rate_limit_callback,
        cancel_callback=gateway.cancel_callback,
        transit_callback=gateway.transit_callback,
        received_blob_callback=gateway.received_blob_callback,
    )

    # It is useful to have a reference to the websocket api within your Gateway
    gateway.api = websocket_connection

    # Connect to MT
    asyncio.ensure_future(websocket_connection.connect_with_retries())

    # To make it easier to interact with this Gateway, we are going to configure a bunch of commands for a satellite
    # called "Example FlatSat". Please see the associated json file to see the list of commands.
    logger.debug(
        "Setting up Example Flatsat satellite and associated commands")
    with open('satellite/example_commands.json', 'r') as f:
        command_defs = json.loads(f.read())
    asyncio.ensure_future(
        websocket_connection.update_command_definitions(
            system="Example FlatSat", definitions=command_defs["definitions"]))

    try:
        loop.run_forever()
    except KeyboardInterrupt:
        loop.run_until_complete(loop.shutdown_asyncgens())
        loop.close()
async def test_sync_callbacks_can_be_run_async():
    def cb(command, *args, **kwargs):
        time.sleep(1)
        logging.debug(f"Finished command {command.id}")

    gw = GatewayAPI("host", "gateway_token", command_callback=cb)

    async def ticker():
        for i in range(10):
            yield json.dumps({
                "type": "command",
                "command": {
                    "id": i,
                    "type": "get_batt",
                    "system": "ISS",
                    "fields": []
                }
            })
            await asyncio.sleep(0.1)

    logging.info("start await")
    async for message in ticker():
        await gw.handle_message(message)
    logging.info("end await")

    # The sync commands should finish before this is done
    # If they don't, a bunch of errors will (hopefully) be generated.
    await asyncio.sleep(3)
Exemplo n.º 4
0
async def test_calls_command_callback(callback_mock):
    gw = GatewayAPI("host", "gateway_token", command_callback=callback_mock)

    await gw.handle_message(MESSAGE)
    await asyncio.sleep(1)

    # Make sure that the command callback was called with the command and Gateway
    callback_mock.assert_called_once_with(TypeMatcher(Command),
                                          TypeMatcher(GatewayAPI))
Exemplo n.º 5
0
async def test_fails_when_no_command_callback(monkeypatch):
    gw = GatewayAPI("host", "gateway_token")

    # Monkey-patch fail command, which should be called when a command callback doesn't exist
    mock_fail_command = AsyncMock()
    monkeypatch.setattr(gw, "fail_command", mock_fail_command)

    await gw.handle_message(MESSAGE)
    await asyncio.sleep(1)

    assert mock_fail_command.called
async def test_calls_transit_callback(callback_mock):
    gw = GatewayAPI("host", "gateway_token", transit_callback=callback_mock)
    # ToDo: Update this example message
    message = {
        "type": "transit",
    }

    res = await gw.handle_message(json.dumps(message))

    # The transit callback is given the raw message
    callback_mock.assert_called_once_with(message)
async def test_error_propagation_on_SYNC_command_callback(caplog):
    def cb(*args, **kwargs):
        # You should NOT see 'Task exception was never retrieved'
        raise RuntimeError("This exception message should be visible.")

    gw = GatewayAPI("host", "gateway_token", command_callback=cb)

    await gw.handle_message(MESSAGE)
    await asyncio.sleep(1)  # Give time for the callback to run

    assert 'This exception message should be visible' in caplog.text
async def test_calls_SYNC_command_callback():
    result = {"worked": False}

    def cb(*args, **kwargs):
        result["worked"] = True

    gw = GatewayAPI("host", "gateway_token", command_callback=cb)

    await gw.handle_message(MESSAGE)
    await asyncio.sleep(1)

    assert result["worked"]
async def test_calls_error_callback(callback_mock):
    gw = GatewayAPI("host", "gateway_token", error_callback=callback_mock)
    message = {
        "type": "error",
        "error":
        "This Gateway's token has been rotated. Please use the new one.",
        "disconnect": True
    }

    res = await gw.handle_message(json.dumps(message))

    # The error callback is given the raw message
    callback_mock.assert_called_once_with(message)
Exemplo n.º 10
0
async def test_calls_command_callback_v2():
    ''' This test uses an inline async coroutine instead of a mock. '''
    result = {"worked": False}

    async def cb(*args, **kwargs):
        result["worked"] = True

    gw = GatewayAPI("host", "gateway_token", command_callback=cb)

    await gw.handle_message(MESSAGE)
    await asyncio.sleep(1)

    assert result["worked"]
Exemplo n.º 11
0
async def test_initializes_mission_name_from_hello_message(callback_mock):
    gw = GatewayAPI("host", "gateway_token")
    message = json.dumps({
        "type": "hello",
        "hello": {
            "mission": "MISSION NAME"
        },
    })

    await gw.handle_message(message)
    await asyncio.sleep(1)

    assert gw.mission_name == "MISSION NAME"
async def test_calls_transit_callback():
    result = {"worked": False}

    def cb(*args, **kwargs):
        result["worked"] = True

    gw = GatewayAPI("host", "gateway_token", transit_callback=cb)
    # ToDo: Update this example message
    message = {
        "type": "transit",
    }

    await gw.handle_message(json.dumps(message))
    await asyncio.sleep(1)
async def test_calls_cancel_callback(callback_mock):
    gw = GatewayAPI("host", "gateway_token", cancel_callback=callback_mock)
    message = json.dumps({
        "type": "cancel",
        "timestamp": 1528391020767,
        "command": {
            "id": 20
        }
    })

    res = await gw.handle_message(message)

    # The cancel callback is called with the command id and the gateway
    callback_mock.assert_called_once_with(20, TypeMatcher(GatewayAPI))
async def test_calls_SYNC_error_callback():
    result = {"worked": False}

    def cb(*args, **kwargs):
        result["worked"] = True

    gw = GatewayAPI("host", "gateway_token", error_callback=cb)
    message = {
        "type": "error",
        "error":
        "This Gateway's token has been rotated. Please use the new one.",
        "disconnect": True
    }

    await gw.handle_message(json.dumps(message))
    await asyncio.sleep(1)
async def test_calls_command_callback(callback_mock):
    gw = GatewayAPI("host", "gateway_token", command_callback=callback_mock)
    message = json.dumps({
        "type": "command",
        "command": {
            "id": 4,
            "type": "get_battery",
            "system": "ISS",
            "fields": []
        }
    })

    res = await gw.handle_message(message)

    # Make sure that the command callback was called with the command and Gateway
    callback_mock.assert_called_once_with(TypeMatcher(Command),
                                          TypeMatcher(GatewayAPI))
async def test_calls_rate_limit_callback(callback_mock):
    gw = GatewayAPI("host", "gateway_token", rate_limit_callback=callback_mock)
    message = {
        "type": "rate_limit",
        "rate_limit": {
            "rate":
            60,
            "retry_after":
            0.5,
            "error":
            "Rate limit exceeded. Please limit request rate to a burst of 20 and an average of 60/second.",
        }
    }

    res = await gw.handle_message(json.dumps(message))

    # The rate limit callback is given the raw message
    callback_mock.assert_called_once_with(message)
async def test_calls_SYNC_cancel_callback():
    result = {"worked": False}

    def cb(*args, **kwargs):
        result["worked"] = True

    gw = GatewayAPI("host", "gateway_token", cancel_callback=cb)
    message = json.dumps({
        "type": "cancel",
        "timestamp": 1528391020767,
        "command": {
            "id": 20
        }
    })

    await gw.handle_message(message)
    await asyncio.sleep(1)

    assert result["worked"]
async def test_fails_when_no_command_callback(monkeypatch):
    gw = GatewayAPI("host", "gateway_token")

    # Monkey-patch fail command, which should be called when a command callback doesn't exist
    mock_fail_command = AsyncMock()
    monkeypatch.setattr(gw, "fail_command", mock_fail_command)

    message = json.dumps({
        "type": "command",
        "command": {
            "id": 4,
            "type": "get_battery",
            "system": "ISS",
            "fields": []
        }
    })
    res = await gw.handle_message(message)

    assert (None == res)
    assert mock_fail_command.called
async def test_calls_received_blob_callback(callback_mock):
    gw = GatewayAPI("host",
                    "gateway_token",
                    received_blob_callback=callback_mock)
    blob = b"I am a blob"
    message = json.dumps({
        "type": "received_blob",
        "blob": base64.b64encode(blob).decode("utf-8"),
        "context": {
            "norad_id": 12345
        },
        "metadata": {
            "gsn_timestamp": 1234567890,
            "majortom_timestamp": 1234567890
        }
    })

    res = await gw.handle_message(message)

    # The received_blob callback is given the decoded blob, the context, and the gateway
    callback_mock.assert_called_once_with(blob, ANY, TypeMatcher(GatewayAPI))
async def test_calls_SYNC_received_blob_callback():
    result = {"worked": False}

    def cb(*args, **kwargs):
        result["worked"] = True

    gw = GatewayAPI("host", "gateway_token", received_blob_callback=cb)
    blob = b"I am a blob"
    message = json.dumps({
        "type": "received_blob",
        "blob": base64.b64encode(blob).decode("utf-8"),
        "context": {
            "norad_id": 12345
        },
        "metadata": {
            "gsn_timestamp": 1234567890,
            "majortom_timestamp": 1234567890
        }
    })

    await gw.handle_message(message)
    await asyncio.sleep(1)
async def test_calls_SYNC_rate_limit_callback():
    result = {"worked": False}

    def cb(*args, **kwargs):
        result["worked"] = True

    gw = GatewayAPI("host", "gateway_token", rate_limit_callback=cb)
    message = {
        "type": "rate_limit",
        "rate_limit": {
            "rate":
            60,
            "retry_after":
            0.5,
            "error":
            "Rate limit exceeded. Please limit request rate to a burst of 20 and an average of 60/second.",
        }
    }

    await gw.handle_message(json.dumps(message))
    await asyncio.sleep(1)

    assert result["worked"]
Exemplo n.º 22
0
        level=logging.INFO,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
else:
    logging.basicConfig(
        level=logging.DEBUG,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')

logger.info("Starting up!")
loop = asyncio.get_event_loop()

logger.debug("Setting up WinSAT-1 Satellite")
winsat = WinSat(name="WinSAT-1")

logger.debug("Setting up MajorTom")
gateway = GatewayAPI(host=args.majortomhost,
                     gateway_token=args.gatewaytoken,
                     basic_auth=args.basicauth,
                     command_callback=winsat.command_callback,
                     cancel_callback=winsat.cancel_callback,
                     http=args.http)

logger.debug("Connecting to MajorTom")
asyncio.ensure_future(gateway.connect_with_retries())

logger.debug("Sending Command Definitions")
asyncio.ensure_future(
    gateway.update_command_definitions(system=winsat.name,
                                       definitions=winsat.definitions))

logger.debug("Starting Event Loop")
loop.run_forever()
def test_required_args():
    with pytest.raises(TypeError):
        gw = GatewayAPI()