def create_message(self, data, schema: dict = MessageSchemas.MESSAGE): """ Create a new message instance pre populated with the sender_ID and time known to the nats_handler. Uses the data arg as value for the "data" field and validates on the provided schema. Raises an exception if the validation is unsuccessful. Args: data (dict): Data used to populate the "data" field of a message. schema (dict): Schema to validate the message Returns: Message: Message object populated with the data. """ message_type = "unknown" time_sent = self.time_sent if not time_sent: time_sent = datetime.now().isoformat(timespec='milliseconds') if "name" in schema.keys(): message_type = schema["name"] return Message.decode_json( { "sender_ID": self.sender_id, "origin_ID": self.sender_id, "message_type": message_type, "time_sent": time_sent, "data": data }, schema)
async def test_publish(self): """ Testing publishing to a channel """ loop = self._asyncioTestLoop nats = NatsHandler("test", "0.0.0.0", "4222", loop=loop, user="******", password="******") await nats.connect() message = Message.decode_json( { "sender_ID": "User", "time_sent": "2020-07-06", "data": { "testData": "This is a test" } }, MessageSchemas.TEST_MESSAGE) result = await nats.send_message("subscribe-test", message) self.assertTrue(result) await nats.disconnect()
async def test_send_data(self): """ Testing whether sending data to a channel works. """ loop = self._asyncioTestLoop nats = NatsHandler("test", "0.0.0.0", "4222", loop=loop, user="******", password="******") await nats.connect() message = Message.decode_json( { "sender_ID": "User", "time_sent": "2020-07-06", "data": { "testData": "This is a test" } }, MessageSchemas.TEST_MESSAGE) result = await nats.send_data("subscribe-test", message) self.assertTrue(result) await nats.disconnect() self.assertEqual(len(nats.data_table), 1)
async def test_cubesat_X_attitude_provider(self): """ Test for cubesat_X_attitude_provider() callback """ # Creating variables that will be used as arguments for the callback loop = asyncio.get_running_loop() nats = utils.FakeNatsHandler("cubesat_2", "4222", loop=loop, user="******", password="******") await nats.connect() # Opening a file that contains a sample shared storage f = open("test_orbits_config.json") shared_storage = json.load(f) # Data that will be sent in the message data = { "sender_ID": "cubesat_2", "time_sent": "2020-07-05T09:51:49", "data": { "id": "cubesat_1", "attitude": "nadir_tracking", "time": "2022-12-02T03:00:00.000" } } message = Message.decode_json(data, MessageSchemas.ATTITUDE_MESSAGE) await orbit_service.cubesat_X_attitude_provider( message, nats, shared_storage, None) # Verifying that "cubesat_1"'s state was updated properly self.assertTrue(shared_storage["swarm"]["cubesat_1"]["orbit"] ["attitude"] == message.data["attitude"])
async def callback_wrapper(msg): # try executing the callback and log if exception occurs try: # decode message and copy raw message to preserve the response channel name raw_message = msg msg = Message.decode_raw(msg.data, message_schema) # temporarily copy shared storage, so callback cannot perform invalid changes shared_storage = self.shared_storage.copy() # execute callback if len(signature(callback_function).parameters) == 5: response = await callback_function(msg, self.nats_client, shared_storage, self._logger, self.kubernetes_client) else: response = await callback_function(msg, self.nats_client, shared_storage, self._logger) # check whether the shared storage is still valid and set it if that is the case if not validate_json(shared_storage, self._schema): raise ValueError( "Invalid change in shared storage") self.shared_storage = shared_storage # buffer the current shared storage in redis self.redis_client.set_shared_storage( self.shared_storage) # send the response via NATS await self.nats_client.send_message(raw_message.reply, response) except Exception as e: await self._logger.error(traceback.format_exc())
def create_message(self, data, schema): """ TODO """ return Message.decode_json( { "sender_ID": self.sender_id, "time_sent": "sometime", "data": data }, schema)
async def test_request_response(self): """ Testing whether request response works and whether the callback is called. """ loop = self._asyncioTestLoop loop.set_exception_handler(None) nats = NatsHandler("test", "0.0.0.0", "4222", loop=loop, user="******", password="******") await nats.connect() async def callback(msg): print("Got message") loop = self._asyncioTestLoop raw_message = msg msg = Message.decode_raw(msg.data, MessageSchemas.TEST_MESSAGE) print(msg) self.assertEqual(msg.encode_json(), { "sender_ID": "User", "origin_ID": "User", "message_type": "test_message", "time_sent": "2020-07-06", "data": { "testData": "This is a test" } }) await nats.send_message(raw_message.reply, msg) await nats.subscribe_callback("response-test", callback) message = Message.decode_json({ "sender_ID": "User", "time_sent": "2020-07-06", "data": { "testData": "This is a test" } }, MessageSchemas.TEST_MESSAGE) response = await nats.request_message("response-test", message, MessageSchemas.TEST_MESSAGE, timeout=1) self.assertEqual(response.encode_json(), { "sender_ID": "test", "origin_ID": "User", "message_type": "test_message", "time_sent": "2020-07-06", "data": { "testData": "This is a test" } }) result = await nats.unsubscribe_callback("response-test", callback) self.assertTrue(result) await nats.disconnect()
async def callback(msg): print("Got message") print(msg) self.assertEquals(Message.decode_raw(msg.data, MessageSchemas.TEST_MESSAGE).encode_json(), { "sender_ID": "User", "time_sent": "2020-07-06", "data": { "testData": "This is a test" } } ) print("Is equal") raise ValueError("TEST")
async def test_receive(self): """ TODO: LOOK at first comment inside test--see if resolved yet Testing whether receiving actually works and whether the callback is called. Could not figure out yet how to assert within the callback. USE WITH CAUTION!! Must check the print output to see if the messages were actually received """ # TODO: Figure out how to assert within the callback and check that it is called in the first place print("Starting") loop = self._asyncioTestLoop nats = NatsHandler("test", "0.0.0.0", "4222", loop=loop, user="******", password="******") await nats.connect() async def callback(msg): print("Got message") print(msg) self.assertEquals( Message.decode_raw(msg.data, MessageSchemas.TEST_MESSAGE).encode_json(), { "sender_ID": "User", "time_sent": "2020-07-06", "data": { "testData": "This is a test" } }) print("Is equal") raise ValueError("TEST") await nats.subscribe_callback("subscribe-test", callback) message = Message.decode_json( { "sender_ID": "User", "time_sent": "2020-07-06", "data": { "testData": "This is a test" } }, MessageSchemas.TEST_MESSAGE) await nats.send_message("subscribe-test", message) await nats.disconnect()
async def callback(msg): print("Got message") loop = self._asyncioTestLoop raw_message = msg msg = Message.decode_raw(msg.data, MessageSchemas.TEST_MESSAGE) print(msg) self.assertEqual(msg.encode_json(), { "sender_ID": "User", "origin_ID": "User", "message_type": "test_message", "time_sent": "2020-07-06", "data": { "testData": "This is a test" } }) await nats.send_message(raw_message.reply, msg)
async def request_message(self, topic, message, schema, timeout=1): """ Sends a request to a channel and returns the response. Args: topic (string): channel name to publish to message (object): message to send schema (dict): Schema of the expected response timeout (int, optional): Timeout that limits how long to wait for a response. Defaults to 1. Returns: object: returns the response. """ message = message.encode_raw() result = await self.nc.request(topic, message, timeout) return Message.decode_raw(result.data, schema)
async def handle_api_message(message, nats, shared_storage, logger): # if a validator function was given, call it to determine whether the message should be processed if not validator or validator(message, nats, shared_storage, logger): async with aiohttp.ClientSession() as session: # construct the URL to access the data using the info from the API message url = f"http://{message.data['host']}:{message.data['port']}{message.data['route']}/{message.data['data_id']}" async with session.get(url) as response: # check whether GET was successful if response.status == 200: # decode the message and execute the callback msg = Message.decode_json(await response.json(), message_schema) await callback_function(msg, self.nats_client, self.shared_storage, self._logger) return await self._logger.error(json.dumps(await response.json()))
async def test_cubesat_state(self): """ Test for cubesat_state() callback """ # Creating variables that will be used as arguments for the callback loop = asyncio.get_running_loop() nats = FakeNatsHandler("cubesat_1", "4222", loop=loop, user="******", password="******") await nats.connect() # Opening a file that contains a sample shared storage f = open("test_orbits_config.json") shared_storage = json.load(f) # Updated state that will be sent state = { "cubesat_1": { "orbit": CONSTS.ORBIT_3, "last_update_time": "2022-12-02T03:00:00.000" } } # Data that will be sent in the message data = { "sender_ID": "cubesat_2", "time_sent": "2020-07-05T09:51:49", "data": { "id": "cubesat_1", "state": state } } message = Message.decode_json(data, MessageSchemas.STATE_MESSAGE) await orbit_service.cubesat_state(message, nats, shared_storage, None) # Verifying that cubesat_state() worked properly self.assertTrue( shared_storage["swarm"]["cubesat_1"] == state["cubesat_1"])
async def test_simulation_timepulse_propagate(self): """ Test the simulation_timepulse_propagate() callback """ # Creating variables that will be used as arguments for the callback logger = FakeLogger() loop = asyncio.get_running_loop() nats = FakeNatsHandler("cubesat_1", "4222", loop=loop, user="******", password="******") await nats.connect() # opening a file that contains a sample shared storage f = open("test_orbits_config.json") shared_storage = json.load(f) # Testing the callback across mulitple timesteps for i in range(10, 59): # creating sample data data = { "sender_ID": "cubesat_2", "time_sent": "2021-12-05T00:" + str(i) + ":00.000", "data": { "time": "2021-12-05T00:" + str(i) + ":00.000" } } orbit_1 = shared_storage["swarm"]["cubesat_1"]["orbit"] orbit_2 = shared_storage["swarm"]["cubesat_2"]["orbit"] attitude_provider_1 = { "type": "moving_body_tracking", "parameters": shared_storage["swarm"][shared_storage["swarm"]["cubesat_1"] ["orbit"]["attitude"]]["orbit"] } attitude_provider_2 = { "type": "moving_body_tracking", "parameters": shared_storage["swarm"][shared_storage["swarm"]["cubesat_2"] ["orbit"]["attitude"]]["orbit"] } message = Message.decode_json(data, MessageSchemas.TIMESTEP_MESSAGE) await orbit_service.simulation_timepulse_propagate( message, nats, shared_storage, logger) time = absolute_time_converter_utc_string(data["data"]["time"]) # Creating propagators and attitude provider from orbit configuration of satellites propagator_1 = orekit_utils.analytical_propagator(orbit_1) propagator_2 = orekit_utils.analytical_propagator(orbit_2) attitude_provider_1 = orekit_utils.attitude_provider_constructor( attitude_provider_1["type"], attitude_provider_1["parameters"]) attitude_provider_2 = orekit_utils.attitude_provider_constructor( attitude_provider_2["type"], attitude_provider_2["parameters"]) # Setting attitude and propagating the orbit propagator_1.setAttitudeProvider(attitude_provider_1) propagator_2.setAttitudeProvider(attitude_provider_2) state_1 = propagator_1.propagate(time) state_2 = propagator_2.propagate(time) # Updating param cubesat_1_param = orekit_utils.get_keplerian_parameters(state_1) cubesat_1_param.update({"attitude": "cubesat_2"}) cubesat_1_param.update({"frame": "EME"}) cubesat_2_param = orekit_utils.get_keplerian_parameters(state_2) cubesat_2_param.update({"attitude": "cubesat_1"}) cubesat_2_param.update({"frame": "EME"}) # Checking to see if simulation_timestep_propagate updated params correctly self.assertTrue(shared_storage["swarm"]["cubesat_1"]["orbit"] == cubesat_1_param) self.assertTrue(shared_storage["swarm"]["cubesat_2"]["orbit"] == cubesat_2_param) self.assertTrue(shared_storage["swarm"]["cubesat_1"]["orbit"] ["attitude"] == "cubesat_2") self.assertTrue(shared_storage["swarm"]["cubesat_2"]["orbit"] ["attitude"] == "cubesat_1") print(shared_storage["swarm"]["cubesat_1"]["target_in_view"]) print(shared_storage["swarm"]["cubesat_2"]["target_in_view"]) # Making sure that phonebook gets updated properly if i <= 48: self.assertTrue(shared_storage["sat_phonebook"]["cubesat_2"]) self.assertTrue( shared_storage["swarm"]["cubesat_2"]["target_in_view"]) else: self.assertFalse(shared_storage["sat_phonebook"]["cubesat_2"]) self.assertFalse( shared_storage["swarm"]["cubesat_2"]["target_in_view"])