def test_store_passive_message(self): """JRPC test: services.communiations.storePassiveMessage (2) Simple test for validating the storage of passive messages. """ if self.__verbose_testing: print('>>> test_store_passive_message') self.assertEqual( comms_jrpc.store_passive_message( groundstation_id=self.__gs_1_id, timestamp=misc.get_utc_timestamp(misc.get_now_utc()), doppler_shift=0.0, message=db_tools.MESSAGE_BASE64 ), 1, 'Message ID expected not to be none' ) message = comms_models.PassiveMessage.objects.get(pk=1).message self.assertEqual( db_tools.MESSAGE_BASE64.decode(), message, 'In-database stored message differs, diff = ' + str( difflib.ndiff(db_tools.MESSAGE_BASE64.decode(), message)) ) if self.__verbose_testing: print('>>> message_1 (RAW) = ' + str(message)) print('>>> message_1 (STR) = ' + str(base64.b64decode(message))) self.assertEqual( comms_jrpc.store_passive_message( groundstation_id=self.__gs_1_id, timestamp=misc.get_utc_timestamp(misc.get_now_utc()), doppler_shift=0.0, message=db_tools.MESSAGE_BASE64 ), 2, 'Message ID expected to be 2' ) message = comms_models.PassiveMessage.objects.get(pk=2).message self.assertEqual( db_tools.MESSAGE_BASE64.decode(), message, 'In-database stored message differs, diff = ' + str( difflib.ndiff(db_tools.MESSAGE_BASE64.decode(), message)) ) if self.__verbose_testing: print('>>> message_2 (RAW) = ' + str(message)) print('>>> message_2 (STR) = ' + str(base64.b64decode(message)))
def get_messages(launch_id, start): """JRPC method Method that retrieves the messages for this launch that have been received since the given start date parameter until right now. :param launch_id: Identifier of the launch :param start: Datetime start, should be sooner than now. :return: Array with objects of the following type: { groundstation_id: $(groundstation), timestamp: $(start_date_isoformat), message: $(message) } """ if not start: raise Exception('<start> value is not valid') launch = launch_models.Launch.objects.get(identifier=launch_id) launch_gss = launch.groundstations.all() # start_dt = pytz.utc.localize(dt_parser.parse(start)) start_dt = dt_parser.parse(start).astimezone(pytz.utc) end_dt = misc.get_now_utc() start_ts = misc.get_utc_timestamp(start_dt) end_ts = misc.get_utc_timestamp(end_dt) messages = comms_models.PassiveMessage.objects.filter( groundstation__in=launch_gss, groundstation_timestamp__gt=start_ts, groundstation_timestamp__lt=end_ts ) return messages_serializers.serialize_messages(messages)
def create_jrpc_daily_rule(operation=rules.RULE_OP_ADD, date_i=None, date_f=None, starting_time=None, ending_time=None): if date_i is None: date_i = misc.get_today_utc() + datetime.timedelta(days=1) if date_f is None: date_f = misc.get_today_utc() + datetime.timedelta(days=366) now = misc.get_now_utc() if starting_time is None: starting_time = now + datetime.timedelta(minutes=30) if ending_time is None: ending_time = now + datetime.timedelta(minutes=45) return { rules.RULE_OP: operation, rules.RULE_PERIODICITY: rules.RULE_PERIODICITY_DAILY, rules.RULE_DATES: { rules.RULE_DAILY_I_DATE: date_i.isoformat(), rules.RULE_DAILY_F_DATE: date_f.isoformat(), rules.RULE_S_TIME: starting_time.isoformat(), rules.RULE_E_TIME: ending_time.isoformat() }, }
def create_jrpc_daily_rule( operation=rules.RULE_OP_ADD, date_i=None, date_f=None, starting_time=None, ending_time=None ): if date_i is None: date_i = misc.get_today_utc() + datetime.timedelta(days=1) if date_f is None: date_f = misc.get_today_utc() + datetime.timedelta(days=366) now = misc.get_now_utc() if starting_time is None: starting_time = now + datetime.timedelta(minutes=30) if ending_time is None: ending_time = now + datetime.timedelta(minutes=45) return { rules.RULE_OP: operation, rules.RULE_PERIODICITY: rules.RULE_PERIODICITY_DAILY, rules.RULE_DATES: { rules.RULE_DAILY_I_DATE: date_i.isoformat(), rules.RULE_DAILY_F_DATE: date_f.isoformat(), rules.RULE_S_TIME: starting_time.isoformat(), rules.RULE_E_TIME: ending_time.isoformat() }, }
def get_messages(launch_id, start): """JRPC method Method that retrieves the messages for this launch that have been received since the given start date parameter until right now. :param launch_id: Identifier of the launch :param start: Datetime start, should be sooner than now. :return: Array with objects of the following type: { groundstation_id: $(groundstation), timestamp: $(start_date_isoformat), message: $(message) } """ if not start: raise Exception('<start> value is not valid') launch = launch_models.Launch.objects.get(identifier=launch_id) launch_gss = launch.groundstations.all() # start_dt = pytz.utc.localize(dt_parser.parse(start)) start_dt = dt_parser.parse(start).astimezone(pytz.utc) end_dt = misc.get_now_utc() start_ts = misc.get_utc_timestamp(start_dt) end_ts = misc.get_utc_timestamp(end_dt) messages = comms_models.PassiveMessage.objects.filter( groundstation__in=launch_gss, groundstation_timestamp__gt=start_ts, groundstation_timestamp__lt=end_ts) return messages_serializers.serialize_messages(messages)
def test_add_daily_rule(self): """JRPC test: (D) cfg.gs.channel.addRule, cfg.gs.channel.removeRule Should correctly add a DAILY rule to the system. """ if self.__verbose_testing: print('>>> TEST (test_gs_channel_add_rule)') now = misc.get_now_utc() r_1_s_time = now + datetime.timedelta(minutes=30) r_1_e_time = now + datetime.timedelta(minutes=45) # 1) A daily rule is inserted in the database: rule_cfg = db_tools.create_jrpc_daily_rule( starting_time=r_1_s_time, ending_time=r_1_e_time ) rule_pk = jrpc_rules.add_rule(self.__gs_1_id, rule_cfg) # 2) get the rule back through the JRPC interface rules_g1c1 = jrpc_rules.list_channel_rules(self.__gs_1_id) expected_r = { jrpc_serial.RULE_PK_K: rule_pk, jrpc_serial.RULE_PERIODICITY: jrpc_serial.RULE_PERIODICITY_DAILY, jrpc_serial.RULE_OP: jrpc_serial.RULE_OP_ADD, jrpc_serial.RULE_DATES: { jrpc_serial.RULE_DAILY_I_DATE: common_serial .serialize_iso8601_date( misc.get_today_utc() + datetime.timedelta(days=1) ), jrpc_serial.RULE_DAILY_F_DATE: common_serial .serialize_iso8601_date( misc.get_today_utc() + datetime.timedelta(days=366) ), jrpc_serial.RULE_S_TIME: common_serial .serialize_iso8601_time( r_1_s_time ), jrpc_serial.RULE_E_TIME: common_serial .serialize_iso8601_time( r_1_e_time ) } } if self.__verbose_testing: print('>>> rules from JRPC[' + str(len(rules_g1c1)) + ']:') for r in rules_g1c1: misc.print_dictionary(r) print('>>> expected_r:') misc.print_dictionary(expected_r) self.assertEqual(rules_g1c1[0], expected_r)
def create_message(groundstation, message=MESSAGE__1_TEST): server_dt = misc.get_now_utc() server_ts = misc.get_utc_timestamp(server_dt) gs_dt = server_dt - datetime.timedelta(hours=2) gs_ts = misc.get_utc_timestamp(gs_dt) return comms_models.PassiveMessage.objects.create( groundstation=groundstation, doppler_shift=0.0, groundstation_timestamp=gs_ts, reception_timestamp=server_ts, message=message)
def create_message(groundstation, message=MESSAGE__1_TEST): server_dt = misc.get_now_utc() server_ts = misc.get_utc_timestamp(server_dt) gs_dt = server_dt - datetime.timedelta(hours=2) gs_ts = misc.get_utc_timestamp(gs_dt) return comms_models.PassiveMessage.objects.create( groundstation=groundstation, doppler_shift=0.0, groundstation_timestamp=gs_ts, reception_timestamp=server_ts, message=message )
def get_next_slot(user_email): """JRPC: services.scheduling.slots.next Returns the next slot that is going to be available for the given user. Args: user_email: String with the email of the user Returns: Slot object with the following operational slot (null if no available) """ return schedule_serializers.serialize_slot( operational_models.OperationalSlot.objects.filter( Q(pass_slot__spacecraft__user__email=user_email) | Q(pass_slot__groundstation__user__email=user_email)).order_by( 'start').filter(end__gt=sn_misc.get_now_utc()).first())
def get_simulation_window(): """Simulation window slot. Static method that returns the current 'in-use' simulation window, this is, the start and end datetime objects for the simulation of the slots that is currently being used. :return: Tuple (start, end) with the simulation window currently in use (UTC localized). """ # From the 'window duration', 1 day has to be substracted (the day in # course). start = misc.get_now_utc() end = misc.get_next_midnight()\ + OrbitalSimulator.get_window_duration()\ - datetime.timedelta(days=1) return start, end
def get_simulation_window(): """Simulation window slot. Static method that returns the current 'in-use' simulation window, this is, the start and end datetime objects for the simulation of the slots that is currently being used. :return: Tuple (start, end) with the simulation window currently in use (UTC localized). """ # From the 'window duration', 1 day has to be substracted (the day in # course). start = misc.get_now_utc() end = misc.get_next_midnight()\ + OrbitalSimulator.get_window_duration()\ - datetime.timedelta(days=1) return start, end
def _create_test_operational_slots( start, end, minimum_duration=datetime.timedelta(minutes=5)): """ Static method that creates the OperationalSlots to be used for testing purposes. :return: List with the testing OperationalSlots. """ if start is None: now = misc.get_now_utc() return [(now, now + minimum_duration)] if end is None: return [(start, start + minimum_duration)] return [(start, end)]
def serialize_test_slot_information(): """ Serializes the information about a TESTING slot, that is intended to be used only for TESTING PURPOSES. :return: JSON-like structure with the information of the operational slot """ s_time = common_misc.get_now_utc(no_microseconds=True) e_time = s_time + datetime.timedelta(hours=2) return { STATE_K: 'TEST', 'gs_username': '******', 'sc_username': '******', 'starting_time': s_time.isoformat(), 'ending_time': e_time.isoformat(), }
def _create_test_operational_slots( start, end, minimum_duration=datetime.timedelta(minutes=5) ): """ Static method that creates the OperationalSlots to be used for testing purposes. :return: List with the testing OperationalSlots. """ if start is None: now = misc.get_now_utc() return [(now, now + minimum_duration)] if end is None: return [(start, start + minimum_duration)] return [(start, end)]
def test_add_daily_rule(self): """JRPC test: (D) cfg.gs.channel.addRule, cfg.gs.channel.removeRule Should correctly add a DAILY rule to the system. """ if self.__verbose_testing: print('>>> TEST (test_gs_channel_add_rule)') now = misc.get_now_utc() r_1_s_time = now + datetime.timedelta(minutes=30) r_1_e_time = now + datetime.timedelta(minutes=45) # 1) A daily rule is inserted in the database: rule_cfg = db_tools.create_jrpc_daily_rule(starting_time=r_1_s_time, ending_time=r_1_e_time) rule_pk = jrpc_rules.add_rule(self.__gs_1_id, rule_cfg) # 2) get the rule back through the JRPC interface rules_g1c1 = jrpc_rules.list_channel_rules(self.__gs_1_id) expected_r = { jrpc_serial.RULE_PK_K: rule_pk, jrpc_serial.RULE_PERIODICITY: jrpc_serial.RULE_PERIODICITY_DAILY, jrpc_serial.RULE_OP: jrpc_serial.RULE_OP_ADD, jrpc_serial.RULE_DATES: { jrpc_serial.RULE_DAILY_I_DATE: common_serial.serialize_iso8601_date(misc.get_today_utc() + datetime.timedelta( days=1)), jrpc_serial.RULE_DAILY_F_DATE: common_serial.serialize_iso8601_date(misc.get_today_utc() + datetime.timedelta( days=366)), jrpc_serial.RULE_S_TIME: common_serial.serialize_iso8601_time(r_1_s_time), jrpc_serial.RULE_E_TIME: common_serial.serialize_iso8601_time(r_1_e_time) } } if self.__verbose_testing: print('>>> rules from JRPC[' + str(len(rules_g1c1)) + ']:') for r in rules_g1c1: misc.print_dictionary(r) print('>>> expected_r:') misc.print_dictionary(expected_r) self.assertEqual(rules_g1c1[0], expected_r)
def get_next_slot(user_email): """JRPC: services.scheduling.slots.next Returns the next slot that is going to be available for the given user. Args: user_email: String with the email of the user Returns: Slot object with the following operational slot (null if no available) """ return schedule_serializers.serialize_slot( operational_models.OperationalSlot.objects.filter( Q(pass_slot__spacecraft__user__email=user_email) | Q(pass_slot__groundstation__user__email=user_email) ).order_by('start').filter( end__gt=sn_misc.get_now_utc() ).first() )
def clean_groundtracks(threshold=misc.get_now_utc()): """Periodic groundtracks cleanup @param threshold: datetime threshold to clean the old groundtracks """ logger.info('>>> Cleaning groundtracks') try: no_deleted = gt_models.GroundTrack.objects.delete_older( threshold).delete() logger.debug('>>> tasks@clean_passes.filtered = ' + str(no_deleted)) except Exception as ex: logger.exception( '>>> Exception cleaning groundtracks, ex = ' + str(ex), ex) return logger.info('>>> DONE cleaning groundtracks')
def create_jrpc_once_rule(operation=rules.RULE_OP_ADD, starting_time=None, ending_time=None): now = misc.get_now_utc() if starting_time is None: starting_time = now + datetime.timedelta(minutes=30) if ending_time is None: ending_time = now + datetime.timedelta(minutes=45) return { rules.RULE_OP: operation, rules.RULE_PERIODICITY: rules.RULE_PERIODICITY_ONCE, rules.RULE_DATES: { rules.RULE_ONCE_S_TIME: starting_time.isoformat(), rules.RULE_ONCE_E_TIME: ending_time.isoformat() }, }
def clean_passes(threshold=misc.get_now_utc()): """Periodic groundtracks cleanup Cleans the outdated passes from the database. @param threshold: datetime threshold to clean the old passes """ logger.info('>>> Cleaning passes, threshold = ' + str(threshold)) try: no_deleted = pass_models.PassSlots.objects.filter( end__lte=threshold).delete() logger.debug('>>> tasks@clean_passes.filtered = ' + str(no_deleted)) except Exception as ex: logger.exception('>>> Exception cleaning passes, ex = ' + str(ex), ex) return logger.info('>>> DONE cleaning passes')
def serialize_groundtrack(groundtrack): """JSON-RPC method. JSON remotelly invokable method that returns the estimated groundtrack for a given spacecraft. :param groundtrack: The estimated groundtrack. :return: Array containing objects of the type { 'timestamp', 'latitude', 'longitude' }. """ result = [] index = 0 gt_length = len(groundtrack.timestamp) start_date = misc.get_now_utc() end_date = start_date + SimulationSerializer._PERIOD start_ts = start_date.timestamp() end_ts = end_date.timestamp() print('>>> @serialize_groundtrack.start_ts = ' + str(start_ts)) print('>>> @serialize_groundtrack.end_ts = ' + str(end_ts)) while index < gt_length: ts_i = groundtrack.timestamp[index] # print('>>> @serialize_groundtrack.ts_i = ' + str(ts_i)) if ts_i > end_ts: print('>>> @serialize_groundtrack, BREAK') break if ts_i > start_ts: result.append({ SimulationSerializer.TIMESTAMP_K: ts_i, SimulationSerializer.LATITUDE_K: groundtrack.latitude[ index ], SimulationSerializer.LONGITUDE_K: groundtrack.longitude[ index ] }) index += SimulationSerializer._DECIMATION_RATE return result
def create_jrpc_once_rule( operation=rules.RULE_OP_ADD, starting_time=None, ending_time=None ): now = misc.get_now_utc() if starting_time is None: starting_time = now + datetime.timedelta(minutes=30) if ending_time is None: ending_time = now + datetime.timedelta(minutes=45) return { rules.RULE_OP: operation, rules.RULE_PERIODICITY: rules.RULE_PERIODICITY_ONCE, rules.RULE_DATES: { rules.RULE_ONCE_S_TIME: starting_time.isoformat(), rules.RULE_ONCE_E_TIME: ending_time.isoformat() }, }
def serialize_groundtrack(groundtrack): """JSON-RPC method. JSON remotelly invokable method that returns the estimated groundtrack for a given spacecraft. :param groundtrack: The estimated groundtrack. :return: Array containing objects of the type { 'timestamp', 'latitude', 'longitude' }. """ result = [] index = 0 gt_length = len(groundtrack.timestamp) start_date = misc.get_now_utc() end_date = start_date + SimulationSerializer._PERIOD start_ts = start_date.timestamp() end_ts = end_date.timestamp() print('>>> @serialize_groundtrack.start_ts = ' + str(start_ts)) print('>>> @serialize_groundtrack.end_ts = ' + str(end_ts)) while index < gt_length: ts_i = groundtrack.timestamp[index] # print('>>> @serialize_groundtrack.ts_i = ' + str(ts_i)) if ts_i > end_ts: print('>>> @serialize_groundtrack, BREAK') break if ts_i > start_ts: result.append({ SimulationSerializer.TIMESTAMP_K: ts_i, SimulationSerializer.LATITUDE_K: groundtrack.latitude[index], SimulationSerializer.LONGITUDE_K: groundtrack.longitude[index] }) index += SimulationSerializer._DECIMATION_RATE return result
def store_passive_message(groundstation_id, timestamp, doppler_shift, message): """Stores a passive message from a Ground Station. This method stores a message obtained in a passive manner (this is, without requiring from any remote operation to be scheduled) by a given Ground Station in the database. :param groundstation_id: Identifier of the GroundStation :param timestamp: Moment of the reception of the message at the remote Ground Station :param doppler_shift: Doppler shift during the reception of the message :param message: The message to be stored :return: 'true' is returned whenever the message was correctly stored, otherwise, an exception is thrown. """ if message is None: raise Exception('No message included') groundstation = segment_models.GroundStation.objects.get( identifier=groundstation_id ) if not groundstation: raise Exception( 'GroundStation does not exist! id = ' + str(groundstation_id) ) # Tries to decode the message in BASE64, if wrong, an exception is thrown. # Otherwise, this is just a check for the message, the message will be # stored in BASE64 in the database anyway. base64.b64decode(message) message_o = comm_models.PassiveMessage.objects.create( groundstation=groundstation, groundstation_timestamp=timestamp, reception_timestamp=misc.get_utc_timestamp( misc.get_now_utc() ), doppler_shift=doppler_shift, message=message ) return message_o.pk
def delete_older(self, threshold=misc.get_now_utc()): """Filtering order This method implements the filtering for groundtrack timestamps older than the given threshold. TODO :: get rid of djorm_pgarray :param threshold: Threshold for the filter :return: Number of elements deleted from the database """ no_deleted = 0 for gt in self.all(): logger.info( '>>> @groundtracks.delete_older, gt.sc = ' + str(gt.spacecraft.identifier) ) if not gt.timestamp: logger.info( '>>> @groundtracks.delete_older, EMPTY GT' ) continue index = bisect.bisect_left(gt.timestamp, threshold.timestamp()) ts_l = gt.timestamp la_l = gt.latitude lo_l = gt.longitude gt.timestamp = ts_l[index:] gt.latitude = la_l[index:] gt.longitude = lo_l[index:] gt.save() no_deleted += index return no_deleted
def test_test_slot(self): """JRPC test: services.scheduling.getSlot (SLOT -1) Basic TEST slot test """ if self.__verbose_testing: print('##### test_test_slot') s_time = sn_misc.get_now_utc(no_microseconds=True) e_time = s_time + datetime.timedelta(hours=2) if self.__verbose_testing: print('s_time = ' + str(s_time.isoformat())) print('e_time = ' + str(e_time.isoformat())) self.assertEquals( scheduling_slots.get_slot(self.__test_slot_id), { 'state': 'TEST', 'gs_username': '******', 'sc_username': '******', 'starting_time': s_time.isoformat(), 'ending_time': e_time.isoformat() })
def test_test_slot(self): """JRPC test: services.scheduling.getSlot (SLOT -1) Basic TEST slot test """ if self.__verbose_testing: print('##### test_test_slot') s_time = sn_misc.get_now_utc(no_microseconds=True) e_time = s_time + datetime.timedelta(hours=2) if self.__verbose_testing: print('s_time = ' + str(s_time.isoformat())) print('e_time = ' + str(e_time.isoformat())) self.assertEquals( scheduling_slots.get_slot(self.__test_slot_id), { 'state': 'TEST', 'gs_username': '******', 'sc_username': '******', 'starting_time': s_time.isoformat(), 'ending_time': e_time.isoformat() } )
def delete_older(self, threshold=misc.get_now_utc()): """Filtering order This method implements the filtering for groundtrack timestamps older than the given threshold. TODO :: get rid of djorm_pgarray :param threshold: Threshold for the filter :return: Number of elements deleted from the database """ no_deleted = 0 for gt in self.all(): logger.info('>>> @groundtracks.delete_older, gt.sc = ' + str(gt.spacecraft.identifier)) if not gt.timestamp: logger.info('>>> @groundtracks.delete_older, EMPTY GT') continue index = bisect.bisect_left(gt.timestamp, threshold.timestamp()) ts_l = gt.timestamp la_l = gt.latitude lo_l = gt.longitude gt.timestamp = ts_l[index:] gt.latitude = la_l[index:] gt.longitude = lo_l[index:] gt.save() no_deleted += index return no_deleted
def test_get_queryset(self): """UNIT test: view process Test for validating the retrieval of the messages for the GroundStations of a given user (the one making the request). """ if self.__verbose_testing: print('>>> test_get_queryset:') # 1) User with no ground stations registered view = comms_views.PassiveMessages() view.request = self.__request_ung actual_m = view.get_queryset() self.assertEqual(len(actual_m), 0, 'List should be empty') # 2) User with a ground station registered that has no messages view = comms_views.PassiveMessages() view.request = self.__request actual_m = view.get_queryset() self.assertEqual(len(actual_m), 0, 'List should be empty') # 3) User with a ground station registered and a message available comms_jrpc.store_passive_message( groundstation_id=self.__gs_1_id, timestamp=misc.get_utc_timestamp(misc.get_now_utc()), doppler_shift=0.0, message=self.__b64_message ) view = comms_views.PassiveMessages() view.request = self.__request actual_m = view.get_queryset() self.assertEqual(len(actual_m), 1, 'List should contain one message') # 4) User with a ground station registered and two messages available comms_jrpc.store_passive_message( groundstation_id=self.__gs_1_id, timestamp=misc.get_utc_timestamp(misc.get_now_utc()), doppler_shift=0.0, message=self.__b64_message ) view = comms_views.PassiveMessages() view.request = self.__request actual_m = view.get_queryset() self.assertEqual(len(actual_m), 2, 'List should contain two messages') # 5) User 2 should receive no messages from user 1 groundstations view = comms_views.PassiveMessages() view.request = self.__request_gs_2 actual_m = view.get_queryset() self.assertEqual(len(actual_m), 0, 'List should be empty') comms_jrpc.store_passive_message( groundstation_id=self.__gs_2_id, timestamp=misc.get_utc_timestamp(misc.get_now_utc()), doppler_shift=0.0, message=self.__b64_message ) view = comms_views.PassiveMessages() view.request = self.__request_gs_2 actual_m = view.get_queryset() self.assertEqual(len(actual_m), 1, 'List should have one message') if self.__verbose_testing: for m in actual_m: print('>>> m = ' + str(m))
def test_bug_1(self): """INTR test: services.scheduling - operational slot generation """ self.__gs_1_id = 'gs-vigo' self.__gs_1_ch_1_id = 'chan-cas-1' self.__sc_1_id = 'sc-serpens' self.__sc_1_ch_1_id = 'xatco-fm-1' self.__sc_1_ch_1_f = 437500000 self.__band = db_tools.create_band() self.__user_profile = db_tools.create_user_profile() # 1) create vigo gs self.__gs = db_tools.create_gs( user_profile=self.__user_profile, identifier=self.__gs_1_id ) # CHECK A: NO ADDITIONAL pass slots, no operational slots # ### There are pass slots for the already-propagated spacecraft p_slots_0 = pass_models.PassSlots.objects.filter( groundstation__identifier=self.__gs_1_id, spacecraft__identifier=self.__sc_1_id ) self.assertEqual(len(p_slots_0), 0) o_slots_0 = operational_models.OperationalSlot.objects.filter( pass_slot__in=p_slots_0 ) self.assertEqual(len(o_slots_0), 0) # 2) serpens spacecraft self.__sc = db_tools.create_sc( user_profile=self.__user_profile, identifier=self.__sc_1_id ) # CHECK B: MORE pass slots, no operational slots # ### There are pass slots for the already-propagated spacecraft p_slots_1 = pass_models.PassSlots.objects.filter( groundstation__identifier=self.__gs_1_id, spacecraft__identifier=self.__sc_1_id ) self.assertGreater(len(p_slots_1), len(p_slots_0)) o_slots_0 = operational_models.OperationalSlot.objects.filter( pass_slot__in=p_slots_0 ) self.assertEqual(len(o_slots_0), 0) # 3) we add channels and, therefore, compatibility matches # without availability rules, no operational slots self.__sc_1_ch_1 = db_tools.sc_add_channel( self.__sc, self.__sc_1_ch_1_f, self.__sc_1_ch_1_id ) self.__gs_1_ch_1 = db_tools.gs_add_channel( self.__gs, self.__band, self.__gs_1_ch_1_id ) # CHECK C: SAME pass slots, no operational slots # ### There are pass slots for the already-propagated spacecraft p_slots_2 = pass_models.PassSlots.objects.filter( groundstation__identifier=self.__gs_1_id, spacecraft__identifier=self.__sc_1_id ) self.assertEqual(len(p_slots_2), len(p_slots_1)) o_slots_0 = operational_models.OperationalSlot.objects.filter( pass_slot__in=p_slots_0 ) self.assertEqual(len(o_slots_0), 0) # 4) we add a daily rule 12 hours, 00:00:01am to 11:59:59pm UTC # all pass slots should became operational slots. self.__rule_1 = rules_jrpc.add_rule( self.__gs_1_id, db_tools.create_jrpc_daily_rule( date_i=sn_misc.get_today_utc(), date_f=sn_misc.get_today_utc() + datetime.timedelta(days=50), starting_time=sn_misc.get_next_midnight() + datetime.timedelta( seconds=1 ), ending_time=sn_misc.get_next_midnight() + datetime.timedelta( hours=23, minutes=59, seconds=59 ) ) ) # CHECK D: 3 availability slots (1 per day, almost 24 hours) # should transform all pass slots into operational slots a_slots = availability_models.AvailabilitySlot.objects.values_list( 'start', 'end' ) x_slots = [ ( sn_misc.get_today_utc() + datetime.timedelta( seconds=1 ), sn_misc.get_today_utc() + datetime.timedelta( hours=23, minutes=59, seconds=59 ) ), ( sn_misc.get_today_utc() + datetime.timedelta( days=1, seconds=1 ), sn_misc.get_today_utc() + datetime.timedelta( days=1, hours=23, minutes=59, seconds=59 ) ), ( sn_misc.get_today_utc() + datetime.timedelta( days=2, seconds=1 ), sn_misc.get_today_utc() + datetime.timedelta( days=2, hours=23, minutes=59, seconds=59 ) ) ] self.assertCountEqual(a_slots, x_slots) p_slots_applicable_objs = pass_models.PassSlots.objects.filter( groundstation__identifier=self.__gs_1_id, spacecraft__identifier=self.__sc_1_id, start__gte=sn_misc.get_now_utc() ) p_slots_applicable = p_slots_applicable_objs.values_list( 'start', 'end' ) self.assertGreaterEqual(len(p_slots_2), len(p_slots_applicable)) o_slots_1 = operational_models.OperationalSlot.objects.filter( pass_slot__in=p_slots_applicable_objs, state=operational_models.STATE_FREE ).values_list('start', 'end') self.assertCountEqual(p_slots_applicable, o_slots_1) # CHECK E: RPC interface should return an equivalent set of slots: o_slots_gs = gs_ops_rpc.get_operational_slots(self.__gs_1_id) self.assertEqual(len(o_slots_gs[self.__sc_1_id]), len(o_slots_1)) if self.__verbose_testing: sn_misc.print_list(o_slots_1, name='o_slots_1') sn_misc.print_list( o_slots_gs[self.__sc_1_id], name='o_slots-' + self.__gs_1_id )
def test_bug_1(self): """INTR test: services.scheduling - operational slot generation """ self.__gs_1_id = 'gs-vigo' self.__gs_1_ch_1_id = 'chan-cas-1' self.__sc_1_id = 'sc-serpens' self.__sc_1_ch_1_id = 'xatco-fm-1' self.__sc_1_ch_1_f = 437500000 self.__band = db_tools.create_band() self.__user_profile = db_tools.create_user_profile() # 1) create vigo gs self.__gs = db_tools.create_gs(user_profile=self.__user_profile, identifier=self.__gs_1_id) # CHECK A: NO ADDITIONAL pass slots, no operational slots # ### There are pass slots for the already-propagated spacecraft p_slots_0 = pass_models.PassSlots.objects.filter( groundstation__identifier=self.__gs_1_id, spacecraft__identifier=self.__sc_1_id) self.assertEqual(len(p_slots_0), 0) o_slots_0 = operational_models.OperationalSlot.objects.filter( pass_slot__in=p_slots_0) self.assertEqual(len(o_slots_0), 0) # 2) serpens spacecraft self.__sc = db_tools.create_sc(user_profile=self.__user_profile, identifier=self.__sc_1_id) # CHECK B: MORE pass slots, no operational slots # ### There are pass slots for the already-propagated spacecraft p_slots_1 = pass_models.PassSlots.objects.filter( groundstation__identifier=self.__gs_1_id, spacecraft__identifier=self.__sc_1_id) self.assertGreater(len(p_slots_1), len(p_slots_0)) o_slots_0 = operational_models.OperationalSlot.objects.filter( pass_slot__in=p_slots_0) self.assertEqual(len(o_slots_0), 0) # 3) we add channels and, therefore, compatibility matches # without availability rules, no operational slots self.__sc_1_ch_1 = db_tools.sc_add_channel(self.__sc, self.__sc_1_ch_1_f, self.__sc_1_ch_1_id) self.__gs_1_ch_1 = db_tools.gs_add_channel(self.__gs, self.__band, self.__gs_1_ch_1_id) # CHECK C: SAME pass slots, no operational slots # ### There are pass slots for the already-propagated spacecraft p_slots_2 = pass_models.PassSlots.objects.filter( groundstation__identifier=self.__gs_1_id, spacecraft__identifier=self.__sc_1_id) self.assertEqual(len(p_slots_2), len(p_slots_1)) o_slots_0 = operational_models.OperationalSlot.objects.filter( pass_slot__in=p_slots_0) self.assertEqual(len(o_slots_0), 0) # 4) we add a daily rule 12 hours, 00:00:01am to 11:59:59pm UTC # all pass slots should became operational slots. self.__rule_1 = rules_jrpc.add_rule( self.__gs_1_id, db_tools.create_jrpc_daily_rule( date_i=sn_misc.get_today_utc(), date_f=sn_misc.get_today_utc() + datetime.timedelta(days=50), starting_time=sn_misc.get_next_midnight() + datetime.timedelta(seconds=1), ending_time=sn_misc.get_next_midnight() + datetime.timedelta(hours=23, minutes=59, seconds=59))) # CHECK D: 3 availability slots (1 per day, almost 24 hours) # should transform all pass slots into operational slots a_slots = availability_models.AvailabilitySlot.objects.values_list( 'start', 'end') x_slots = [ (sn_misc.get_today_utc() + datetime.timedelta(seconds=1), sn_misc.get_today_utc() + datetime.timedelta(hours=23, minutes=59, seconds=59)), (sn_misc.get_today_utc() + datetime.timedelta(days=1, seconds=1), sn_misc.get_today_utc() + datetime.timedelta(days=1, hours=23, minutes=59, seconds=59)), (sn_misc.get_today_utc() + datetime.timedelta(days=2, seconds=1), sn_misc.get_today_utc() + datetime.timedelta(days=2, hours=23, minutes=59, seconds=59)) ] self.assertCountEqual(a_slots, x_slots) p_slots_applicable_objs = pass_models.PassSlots.objects.filter( groundstation__identifier=self.__gs_1_id, spacecraft__identifier=self.__sc_1_id, start__gte=sn_misc.get_now_utc()) p_slots_applicable = p_slots_applicable_objs.values_list( 'start', 'end') self.assertGreaterEqual(len(p_slots_2), len(p_slots_applicable)) o_slots_1 = operational_models.OperationalSlot.objects.filter( pass_slot__in=p_slots_applicable_objs, state=operational_models.STATE_FREE).values_list('start', 'end') self.assertCountEqual(p_slots_applicable, o_slots_1) # CHECK E: RPC interface should return an equivalent set of slots: o_slots_gs = gs_ops_rpc.get_operational_slots(self.__gs_1_id) self.assertEqual(len(o_slots_gs[self.__sc_1_id]), len(o_slots_1)) if self.__verbose_testing: sn_misc.print_list(o_slots_1, name='o_slots_1') sn_misc.print_list(o_slots_gs[self.__sc_1_id], name='o_slots-' + self.__gs_1_id)
def _test_sc_select_slots(self): """ Validates the method for the selection of the slots. """ if self.__verbose_testing: print('##### test_sc_get_changes') operational_models.OperationalSlot.objects.reset_ids_counter() # ### channels required for the tests self.assertTrue( jrpc_sc_chs.sc_channel_create( spacecraft_id=self.__sc_1_id, channel_id=self.__sc_1_ch_1_id, configuration=self.__sc_1_ch_1_cfg ), 'Channel should have been created!' ) self.assertTrue( jrpc_gs_chs.gs_channel_create( groundstation_id=self.__gs_1_id, channel_id=self.__gs_1_ch_1_id, configuration=self.__gs_1_ch_1_cfg ), 'Channel should have been created!' ) date_i = misc.get_today_utc() + datetime.timedelta(days=1) date_f = misc.get_today_utc() + datetime.timedelta(days=366) now = misc.get_now_utc() s_time = now + datetime.timedelta(minutes=30) e_time = now + datetime.timedelta(minutes=45) jrpc_rules.add_rule( self.__gs_1_id, db_tools.create_jrpc_daily_rule( date_i=date_i, date_f=date_f, starting_time=s_time, ending_time=e_time ) ) # 1) select all the slots and retrieve the changes actual = operational_models.OperationalSlot.objects.all() id_list = db_tools.create_identifier_list(actual) actual = jrpc_sc_scheduling.select_slots(self.__sc_1_id, id_list) self.assertEqual( len(actual), 2, 'Wrong slots number!' ) # ### clean up sc/gs self.assertTrue( jrpc_gs_chs.gs_channel_delete( groundstation_id=self.__gs_1_id, channel_id=self.__gs_1_ch_1_id ), 'Could not delete GroundStationChannel = ' + str( self.__gs_1_ch_1_id ) ) self.assertTrue( jrpc_sc_chs.sc_channel_delete( spacecraft_id=self.__sc_1_id, channel_id=self.__sc_1_ch_1_id ), 'Could not delete SpacecraftChannel = ' + str(self.__sc_1_ch_1_id) )
def _test_get_messages(self): """JRPC test: services.leop.getMessages Validates the retrieval of messages from the server. """ # 1) interface robustness self.assertRaises( launch_models.Launch.DoesNotExist, messages_jrpc.get_messages, 'DOESNTEXIST', 'nulldate' ) self.assertRaises( Exception, messages_jrpc.get_messages, self.__leop_id, None ) self.assertRaises( Exception, messages_jrpc.get_messages, self.__leop_id, None ) self.assertRaises( Exception, messages_jrpc.get_messages, self.__leop_id, 'null' ) # 2) basic empty response self.assertEqual( messages_jrpc.get_messages( self.__leop_id, '2002-12-26T00:00:00-06:39' ), [], 'No messages, an empty array should have been returned' ) # 3) feeding 1 message, should be retrieved # 3.a) gs created and added to the launch self.assertEqual( launch_jrpc.add_groundstations( self.__leop_id, [self.__gs_1_id], **{'request': self.__request_2} ), {launch_serial.JRPC_K_LEOP_ID: self.__leop_id}, 'The identifier of the Launch should have been returned' ) # 3.b) database fed with fake data frame message_1 = db_tools.create_message(self.__gs_1) # 3.c) service finally invoked yesterday = misc.get_now_utc() - datetime.timedelta(days=1) actual = messages_jrpc.get_messages( self.__leop_id, yesterday.isoformat() ) expected = [{ launch_serial.JRPC_K_GS_ID: self.__gs_1_id, messages_serial.JRPC_K_TS: message_1.groundstation_timestamp, messages_serial.JRPC_K_MESSAGE: db_tools.MESSAGE__1_TEST }] self.assertEqual(actual, expected) # 4) multiple groundstations: # 4.a) gs created and added to the launch self.assertEqual( launch_jrpc.add_groundstations( self.__leop_id, [self.__gs_2_id], **{'request': self.__request_2} ), {launch_serial.JRPC_K_LEOP_ID: self.__leop_id}, 'The identifier of the Launch should have been returned' ) # 3.b) database fed with fake data frame message_2 = db_tools.create_message( self.__gs_2, message=db_tools.MESSAGE_BASE64 ) # 3.c) service finally invoked yesterday = misc.get_now_utc() - datetime.timedelta(days=1) actual = messages_jrpc.get_messages( self.__leop_id, yesterday.isoformat() ) expected.append({ launch_serial.JRPC_K_GS_ID: self.__gs_2_id, messages_serial.JRPC_K_TS: message_2.groundstation_timestamp, messages_serial.JRPC_K_MESSAGE: db_tools.MESSAGE_BASE64.decode() }) self.assertEqual(actual, expected)
def _sc_get_operational_slots(self): """ Validates the JRPC method <sc_get_operational_slots> """ if self.__verbose_testing: print('##### test_sc_get_operational_slots') operational_models.OperationalSlot.objects.reset_ids_counter() # 1) non-existant Spacecraft self.assertRaises( models.ObjectDoesNotExist, jrpc_sc_scheduling.get_operational_slots, 0 ) # 2) basic test, should not generate slots until the GS is added, # raising an exception to confirm it self.assertEqual( jrpc_sc_chs.sc_channel_create( spacecraft_id=self.__sc_1_id, channel_id=self.__sc_1_ch_1_id, configuration=self.__sc_1_ch_1_cfg ), True, 'Channel should have been created!' ) self.assertRaises( Exception, jrpc_sc_scheduling.get_operational_slots, self.__sc_1_ch_1_id ) # 3) basic test, should generate 2 FREE slots self.assertEqual( jrpc_gs_chs.gs_channel_create( groundstation_id=self.__gs_1_id, channel_id=self.__gs_1_ch_1_id, configuration=self.__gs_1_ch_1_cfg ), True, 'Channel should have been created!' ) date_i = misc.get_today_utc() + datetime.timedelta(days=1) date_f = misc.get_today_utc() + datetime.timedelta(days=366) now = misc.get_now_utc() s_time = now + datetime.timedelta(minutes=30) e_time = now + datetime.timedelta(minutes=45) jrpc_rules.add_rule( self.__gs_1_id, self.__gs_1_ch_1_id, db_tools.create_jrpc_daily_rule( date_i=date_i, date_f=date_f, starting_time=s_time, ending_time=e_time ) ) actual = jrpc_sc_scheduling.get_operational_slots(self.__sc_1_id) expected = { self.__sc_1_ch_1_id: { self.__gs_1_ch_1_id: { segment_serializers.GS_ID_K: self.__sc_1_id, jrpc_sch_serial.SLOTS_K: [{ jrpc_sch_serial.SLOT_IDENTIFIER_K: '1', jrpc_sch_serial.STATE_K: operational_models.STATE_FREE, jrpc_sch_serial.DATE_START_K: ( s_time + datetime.timedelta(days=1) ).isoformat(), jrpc_sch_serial.DATE_END_K: ( e_time + datetime.timedelta(days=1) ).isoformat() }, { jrpc_sch_serial.SLOT_IDENTIFIER_K: '2', jrpc_sch_serial.STATE_K: operational_models.STATE_FREE, jrpc_sch_serial.DATE_START_K: ( s_time + datetime.timedelta(days=2) ).isoformat(), jrpc_sch_serial.DATE_END_K: ( e_time + datetime.timedelta(days=2) ).isoformat() }] } } } self.assertEqual(actual, expected, 'Expected different slots!') # ### clean up self.assertTrue( jrpc_gs_chs.gs_channel_delete( groundstation_id=self.__gs_1_id, channel_id=self.__gs_1_ch_1_id ), 'Could not delete GroundStationChannel = ' + str( self.__gs_1_ch_1_id ) ) self.assertTrue( jrpc_sc_chs.sc_channel_delete( spacecraft_id=self.__sc_1_id, channel_id=self.__sc_1_ch_1_id ), 'Could not delete SpacecraftChannel = ' + str( self.__sc_1_ch_1_id ) )
def get_simulation_window(): return ( sn_misc.get_now_utc(), sn_misc.get_next_midnight() + datetime.timedelta(days=2) )
def get_simulation_window(): return (sn_misc.get_now_utc(), sn_misc.get_next_midnight() + datetime.timedelta(days=2))
def _test_get_messages(self): """JRPC test: services.leop.getMessages Validates the retrieval of messages from the server. """ # 1) interface robustness self.assertRaises(launch_models.Launch.DoesNotExist, messages_jrpc.get_messages, 'DOESNTEXIST', 'nulldate') self.assertRaises(Exception, messages_jrpc.get_messages, self.__leop_id, None) self.assertRaises(Exception, messages_jrpc.get_messages, self.__leop_id, None) self.assertRaises(Exception, messages_jrpc.get_messages, self.__leop_id, 'null') # 2) basic empty response self.assertEqual( messages_jrpc.get_messages(self.__leop_id, '2002-12-26T00:00:00-06:39'), [], 'No messages, an empty array should have been returned') # 3) feeding 1 message, should be retrieved # 3.a) gs created and added to the launch self.assertEqual( launch_jrpc.add_groundstations(self.__leop_id, [self.__gs_1_id], **{'request': self.__request_2}), {launch_serial.JRPC_K_LEOP_ID: self.__leop_id}, 'The identifier of the Launch should have been returned') # 3.b) database fed with fake data frame message_1 = db_tools.create_message(self.__gs_1) # 3.c) service finally invoked yesterday = misc.get_now_utc() - datetime.timedelta(days=1) actual = messages_jrpc.get_messages(self.__leop_id, yesterday.isoformat()) expected = [{ launch_serial.JRPC_K_GS_ID: self.__gs_1_id, messages_serial.JRPC_K_TS: message_1.groundstation_timestamp, messages_serial.JRPC_K_MESSAGE: db_tools.MESSAGE__1_TEST }] self.assertEqual(actual, expected) # 4) multiple groundstations: # 4.a) gs created and added to the launch self.assertEqual( launch_jrpc.add_groundstations(self.__leop_id, [self.__gs_2_id], **{'request': self.__request_2}), {launch_serial.JRPC_K_LEOP_ID: self.__leop_id}, 'The identifier of the Launch should have been returned') # 3.b) database fed with fake data frame message_2 = db_tools.create_message(self.__gs_2, message=db_tools.MESSAGE_BASE64) # 3.c) service finally invoked yesterday = misc.get_now_utc() - datetime.timedelta(days=1) actual = messages_jrpc.get_messages(self.__leop_id, yesterday.isoformat()) expected.append({ launch_serial.JRPC_K_GS_ID: self.__gs_2_id, messages_serial.JRPC_K_TS: message_2.groundstation_timestamp, messages_serial.JRPC_K_MESSAGE: db_tools.MESSAGE_BASE64.decode() }) self.assertEqual(actual, expected)
def test_get_queryset(self): """UNIT test: view process Test for validating the retrieval of the messages for the GroundStations of a given user (the one making the request). """ if self.__verbose_testing: print('>>> test_get_queryset:') # 1) User with no ground stations registered view = comms_views.PassiveMessages() view.request = self.__request_ung actual_m = view.get_queryset() self.assertEqual(len(actual_m), 0, 'List should be empty') # 2) User with a ground station registered that has no messages view = comms_views.PassiveMessages() view.request = self.__request actual_m = view.get_queryset() self.assertEqual(len(actual_m), 0, 'List should be empty') # 3) User with a ground station registered and a message available comms_jrpc.store_passive_message(groundstation_id=self.__gs_1_id, timestamp=misc.get_utc_timestamp( misc.get_now_utc()), doppler_shift=0.0, message=self.__b64_message) view = comms_views.PassiveMessages() view.request = self.__request actual_m = view.get_queryset() self.assertEqual(len(actual_m), 1, 'List should contain one message') # 4) User with a ground station registered and two messages available comms_jrpc.store_passive_message(groundstation_id=self.__gs_1_id, timestamp=misc.get_utc_timestamp( misc.get_now_utc()), doppler_shift=0.0, message=self.__b64_message) view = comms_views.PassiveMessages() view.request = self.__request actual_m = view.get_queryset() self.assertEqual(len(actual_m), 2, 'List should contain two messages') # 5) User 2 should receive no messages from user 1 groundstations view = comms_views.PassiveMessages() view.request = self.__request_gs_2 actual_m = view.get_queryset() self.assertEqual(len(actual_m), 0, 'List should be empty') comms_jrpc.store_passive_message(groundstation_id=self.__gs_2_id, timestamp=misc.get_utc_timestamp( misc.get_now_utc()), doppler_shift=0.0, message=self.__b64_message) view = comms_views.PassiveMessages() view.request = self.__request_gs_2 actual_m = view.get_queryset() self.assertEqual(len(actual_m), 1, 'List should have one message') if self.__verbose_testing: for m in actual_m: print('>>> m = ' + str(m))