def test_touching_requests(): print('Test touching requests: Two agents', now) sch_man = ScheduleManager(60, now=now) ag1 = ('Agent1', 'Task1', (['campus/building/rtu1', parse('2013-11-27 12:00:00'), parse('2013-11-27 12:30:00')],), PRIORITY_HIGH, now) ag2 = ('Agent2', 'Task2', (['campus/building/rtu1', parse('2013-11-27 12:30:00'), parse('2013-11-27 13:00:00')],), PRIORITY_HIGH, now) result1, event_time1 = verify_add_task(sch_man, *ag1) success1, data1, info_string1 = result1 assert all((success1, not data1, not info_string1, event_time1 == parse('2013-11-27 12:00:00'))) result2, event_time2 = verify_add_task(sch_man, *ag2) success2, data2, info_string2 = result2 assert all((success2, not data2, not info_string2, event_time2 == parse('2013-11-27 12:00:00'))) state = sch_man.get_schedule_state(now + timedelta(minutes=30)) assert state == { 'campus/building/rtu1': DeviceState('Agent1', 'Task1', 1800.0)} state = sch_man.get_schedule_state(now + timedelta(minutes=60)) assert state == { 'campus/building/rtu1': DeviceState('Agent2', 'Task2', 1800.0)}
def test_conflict_override_success_running_agent(): print('Test conflicting requests: Agent2 overrides running Agent1', now) sch_man = ScheduleManager(60, now=now) ag1 = ('Agent1', 'Task1', (['campus/building/rtu1', parse('2013-11-27 12:00:00'), parse('2013-11-27 12:35:00')],), PRIORITY_LOW_PREEMPT, now) now2 = now + timedelta(minutes=45) ag2 = ('Agent2', 'Task2', (['campus/building/rtu1', parse('2013-11-27 12:05:00'), parse('2013-11-27 13:00:00')],), PRIORITY_HIGH, now2) result1, event_time1 = verify_add_task(sch_man, *ag1) success1, data1, info_string1 = result1 assert all((success1, not data1, not info_string1, event_time1 == parse('2013-11-27 12:00:00'))) result2, event_time2 = verify_add_task(sch_man, *ag2) success2, data2, info_string2 = result2 assert success2 assert data2 == {('Agent1', 'Task1')} assert info_string2 == '' assert event_time2 == parse('2013-11-27 12:16:00') state = sch_man.get_schedule_state(now2 + timedelta(seconds=30)) assert state == { 'campus/building/rtu1': DeviceState('Agent1', 'Task1', 30.0)} state = sch_man.get_schedule_state(now2 + timedelta(seconds=60)) assert state == { 'campus/building/rtu1': DeviceState('Agent2', 'Task2', 2640.0)}
def test_conflict_override_success_running_agent(): print('Test conflicting requests: Agent2 overrides running Agent1', now) sch_man = ScheduleManager(60, now=now) ag1 = ('Agent1', 'Task1', ([ 'campus/building/rtu1', parse('2013-11-27 12:00:00'), parse('2013-11-27 12:35:00') ], ), PRIORITY_LOW_PREEMPT, now) now2 = now + timedelta(minutes=45) ag2 = ('Agent2', 'Task2', ([ 'campus/building/rtu1', parse('2013-11-27 12:05:00'), parse('2013-11-27 13:00:00') ], ), PRIORITY_HIGH, now2) result1, event_time1 = verify_add_task(sch_man, *ag1) success1, data1, info_string1 = result1 assert all((success1, not data1, not info_string1, event_time1 == parse('2013-11-27 12:00:00'))) result2, event_time2 = verify_add_task(sch_man, *ag2) success2, data2, info_string2 = result2 assert success2 assert data2 == {('Agent1', 'Task1')} assert info_string2 == '' assert event_time2 == parse('2013-11-27 12:16:00') state = sch_man.get_schedule_state(now2 + timedelta(seconds=30)) assert state == { 'campus/building/rtu1': DeviceState('Agent1', 'Task1', 30.0) } state = sch_man.get_schedule_state(now2 + timedelta(seconds=60)) assert state == { 'campus/building/rtu1': DeviceState('Agent2', 'Task2', 2640.0) }
def test_touching_requests(): print('Test touching requests: Two agents', now) sch_man = ScheduleManager(60, now=now) ag1 = ('Agent1', 'Task1', ([ 'campus/building/rtu1', parse('2013-11-27 12:00:00'), parse('2013-11-27 12:30:00') ], ), PRIORITY_HIGH, now) ag2 = ('Agent2', 'Task2', ([ 'campus/building/rtu1', parse('2013-11-27 12:30:00'), parse('2013-11-27 13:00:00') ], ), PRIORITY_HIGH, now) result1, event_time1 = verify_add_task(sch_man, *ag1) success1, data1, info_string1 = result1 assert all((success1, not data1, not info_string1, event_time1 == parse('2013-11-27 12:00:00'))) result2, event_time2 = verify_add_task(sch_man, *ag2) success2, data2, info_string2 = result2 assert all((success2, not data2, not info_string2, event_time2 == parse('2013-11-27 12:00:00'))) state = sch_man.get_schedule_state(now + timedelta(minutes=30)) assert state == { 'campus/building/rtu1': DeviceState('Agent1', 'Task1', 1800.0) } state = sch_man.get_schedule_state(now + timedelta(minutes=60)) assert state == { 'campus/building/rtu1': DeviceState('Agent2', 'Task2', 1800.0) }
def test_two_devices(): print('Basic Test: Two devices', now) sch_man = ScheduleManager(60, now=now) ag = ('Agent1', 'Task1', ([ 'campus/building/rtu1', parse('2013-11-27 12:00:00'), parse('2013-11-27 13:00:00') ], [ 'campus/building/rtu2', parse('2013-11-27 12:00:00'), parse('2013-11-27 13:00:00') ]), PRIORITY_HIGH, now) result1, event_time1 = verify_add_task(sch_man, *ag) success, data, info_string = result1 assert all((success, not data, not info_string, event_time1 == parse('2013-11-27 12:00:00'))) state = sch_man.get_schedule_state(now + timedelta(minutes=30)) assert state == { 'campus/building/rtu1': DeviceState('Agent1', 'Task1', 3600.0), 'campus/building/rtu2': DeviceState('Agent1', 'Task1', 3600.0) } state = sch_man.get_schedule_state(now + timedelta(minutes=60)) assert state == { 'campus/building/rtu1': DeviceState('Agent1', 'Task1', 1800.0), 'campus/building/rtu2': DeviceState('Agent1', 'Task1', 1800.0) }
def test_basic(): print('Basic Test', now) sch_man = ScheduleManager(60, now=now) ag = ('Agent1', 'Task1', (['campus/building/rtu1', parse('2013-11-27 12:00:00'), parse('2013-11-27 13:00:00')],), PRIORITY_HIGH, now) result1, event_time1 = verify_add_task(sch_man, *ag) success, data, info_string = result1 # success1, data1, info_string1 = result1 assert all((success, not data, not info_string, event_time1 == parse('2013-11-27 12:00:00'))) state = sch_man.get_schedule_state(now + timedelta(minutes=30)) assert state == { 'campus/building/rtu1': DeviceState('Agent1', 'Task1', 3600.0)} state = sch_man.get_schedule_state(now + timedelta(minutes=60)) assert state == { 'campus/building/rtu1': DeviceState('Agent1', 'Task1', 1800.0)}
now = datetime(year=2013, month=11, day=27, hour=11, minute=30) print_test_header('Basic Test', now) sch_man = ScheduleManager(timedelta(seconds=60), now=now) ag = ('Agent1', (['/campus/building/rtu1','2013-11-27 12:00:00','2013-11-27 13:00:00'],), PRIORITY_HIGH, now) result1 = test_add_task(sch_man, *ag) event_time1, preempted1, state1 = result1.data assert all((result1.success, not preempted1, not state1, event_time1 == parse('2013-11-27 12:00:00'))) state = sch_man.get_schedule_state(now + timedelta(minutes=30)) assert state == {'/campus/building/rtu1': DeviceState('Agent1', result1.task_id, 3600.0)} state = sch_man.get_schedule_state(now + timedelta(minutes=60)) assert state == {'/campus/building/rtu1': DeviceState('Agent1', result1.task_id, 1800.0)} print_test_header('Basic Test: Two devices', now) sch_man = ScheduleManager(timedelta(seconds=60), now=now) ag = ('Agent1', (['/campus/building/rtu1','2013-11-27 12:00:00','2013-11-27 13:00:00'], ['/campus/building/rtu2','2013-11-27 12:00:00','2013-11-27 13:00:00']), PRIORITY_HIGH, now) result1 = test_add_task(sch_man, *ag) event_time1, preempted1, state1 = result1.data assert all((result1.success, not preempted1, not state1, event_time1 == parse('2013-11-27 12:00:00')))
class Agent(PublishMixin, BaseAgent): '''Agent to listen for requests to talk to the sMAP driver.''' def __init__(self, **kwargs): super(Agent, self).__init__(**kwargs) self._update_event = None self._update_event_time = None self._device_states = {} self.setup_schedule() self.setup_heartbeats() def setup_heartbeats(self): for point in points: heartbeat_point = points[point].get("heartbeat_point") if heartbeat_point is None: continue heartbeat_handler = self.heartbeat_factory( point, heartbeat_point) self.periodic_timer(heartbeat_interval, heartbeat_handler) def heartbeat_factory(self, point, actuator): #Stupid lack on nonlocal in 2.x value = [False] request_url = '/'.join([url, point, ACTUATOR_COLLECTION, actuator]) publish_topic = '/'.join([point, actuator]) def update_heartbeat_value(): value[0] = not value[0] payload = {'state': str(int(value[0]))} try: r = requests.put(request_url, params=payload) self.process_smap_request_result(r, publish_topic, None) except requests.exceptions.ConnectionError: print "Warning: smap driver not running." return update_heartbeat_value def setup_schedule(self): now = datetime.datetime.now() self._schedule_manager = ScheduleManager( preempt_grace_time, now=now, state_file_name=schedule_state_file) self.update_device_state_and_schedule(now) def update_device_state_and_schedule(self, now): self._device_states = self._schedule_manager.get_schedule_state( now) schedule_next_event_time = self._schedule_manager.get_next_event_time( now) new_update_event_time = self._get_ajusted_next_event_time( now, schedule_next_event_time) for device, state in self._device_states.iteritems(): header = self.get_headers(state.agent_id, time=str(now), task_id=state.task_id) header['window'] = state.time_remaining topic = topics.ACTUATOR_SCHEDULE_ANNOUNCE_RAW.replace( '{device}', device) self.publish_json(topic, header, {}) if self._update_event is not None: #This won't hurt anything if we are canceling ourselves. self._update_event.cancel() self._update_event_time = new_update_event_time self._update_event = EventWithTime(self._update_schedule_state) self.schedule(self._update_event_time, self._update_event) def _get_ajusted_next_event_time(self, now, next_event_time): latest_next = now + datetime.timedelta( seconds=schedule_publish_interval) #Round to the next second to fix timer goofyness in agent timers. if latest_next.microsecond: latest_next = latest_next.replace( microsecond=0) + datetime.timedelta(seconds=1) if next_event_time is None or latest_next < next_event_time: return latest_next return next_event_time def _update_schedule_state(self, unix_time): #Find the current slot and update the state now = datetime.datetime.fromtimestamp(unix_time) self.update_device_state_and_schedule(now) @matching.match_regex(topics.ACTUATOR_GET() + '/(.+)') def handle_get(self, topic, headers, message, match): point = match.group(1) collection_tokens, point_name = point.rsplit('/', 1) requester = headers.get('requesterID') if self.check_lock(collection_tokens, requester): request_url = '/'.join( [url, collection_tokens, ACTUATOR_COLLECTION, point_name]) try: r = requests.get(request_url) self.process_smap_request_result(r, point, requester) except (requests.exceptions.ConnectionError) as ex: error = {'type': ex.__class__.__name__, 'value': str(ex)} self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) else: error = { 'type': 'LockError', 'value': 'does not have this lock' } self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) @matching.match_regex(topics.ACTUATOR_SET() + '/(.+)') def handle_set(self, topic, headers, message, match): point = match.group(1) collection_tokens, point_name = point.rsplit('/', 1) requester = headers.get('requesterID') headers = self.get_headers(requester) if not message: error = {'type': 'ValueError', 'value': 'missing argument'} self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) return else: try: message = jsonapi.loads(message[0]) if isinstance(message, bool): message = int(message) except ValueError as ex: # Could be ValueError of JSONDecodeError depending # on if simplesjson was used. JSONDecodeError # inherits from ValueError error = {'type': 'ValueError', 'value': str(ex)} self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) return if self.check_lock(collection_tokens, requester): request_url = '/'.join( [url, collection_tokens, ACTUATOR_COLLECTION, point_name]) payload = {'state': str(message)} try: r = requests.put(request_url, params=payload) self.process_smap_request_result(r, point, requester) except (requests.exceptions.ConnectionError) as ex: error = {'type': ex.__class__.__name__, 'value': str(ex)} self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) else: error = { 'type': 'LockError', 'value': 'does not have this lock' } self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) def check_lock(self, device, requester): device = device.strip('/') if device in self._device_states: device_state = self._device_states[device] return device_state.agent_id == requester return False @matching.match_exact(topics.ACTUATOR_SCHEDULE_REQUEST()) def handle_schedule_request(self, topic, headers, message, match): request_type = headers.get('type') now = datetime.datetime.now() if request_type == SCHEDULE_ACTION_NEW: self.handle_new(headers, message, now) elif request_type == SCHEDULE_ACTION_CANCEL: self.handle_cancel(headers, now) else: self.publish_json( topics.ACTUATOR_SCHEDULE_RESULT(), headers, { 'result': SCHEDULE_RESPONSE_FAILURE, 'data': {}, 'info': 'INVALID_REQUEST_TYPE' }) def handle_new(self, headers, message, now): requester = headers.get('requesterID') taskID = headers.get('taskID') priority = headers.get('priority') try: requests = jsonapi.loads(message[0]) except (ValueError, IndexError) as ex: # Could be ValueError of JSONDecodeError depending # on if simplesjson was used. JSONDecodeError # inherits from ValueError #We let the schedule manager tell us this is a bad request. requests = [] result = self._schedule_manager.request_slots( requester, taskID, requests, priority, now) success = SCHEDULE_RESPONSE_SUCCESS if result.success else SCHEDULE_RESPONSE_FAILURE #If we are successful we do something else with the real result data data = result.data if not result.success else {} topic = topics.ACTUATOR_SCHEDULE_RESULT() headers = self.get_headers(requester, task_id=taskID) headers['type'] = SCHEDULE_ACTION_NEW self.publish_json(topic, headers, { 'result': success, 'data': data, 'info': result.info_string }) #Dealing with success and other first world problems. if result.success: self.update_device_state_and_schedule(now) for preempted_task in result.data: topic = topics.ACTUATOR_SCHEDULE_RESULT() headers = self.get_headers(preempted_task[0], task_id=preempted_task[1]) headers['type'] = SCHEDULE_ACTION_CANCEL self.publish_json( topic, headers, { 'result': SCHEDULE_CANCEL_PREEMPTED, 'info': '', 'data': { 'agentID': requester, 'taskID': taskID } }) def handle_cancel(self, headers, now): requester = headers.get('requesterID') taskID = headers.get('taskID') result = self._schedule_manager.cancel_task(requester, taskID, now) success = SCHEDULE_RESPONSE_SUCCESS if result.success else SCHEDULE_RESPONSE_FAILURE topic = topics.ACTUATOR_SCHEDULE_RESULT() self.publish_json(topic, headers, { 'result': success, 'info': result.info_string, 'data': {} }) if result.success: self.update_device_state_and_schedule(now) def get_headers(self, requester, time=None, task_id=None): headers = {} if time is not None: headers['time'] = time else: headers = {'time': str(datetime.datetime.utcnow())} if requester is not None: headers['requesterID'] = requester if task_id is not None: headers['taskID'] = task_id return headers def process_smap_request_result(self, request, point, requester): headers = self.get_headers(requester) try: request.raise_for_status() results = request.json() readings = results['Readings'] reading = readings[0][1] self.push_result_topic_pair(VALUE_RESPONSE_PREFIX, point, headers, reading) except requests.exceptions.HTTPError as ex: error = { 'type': ex.__class__.__name__, 'value': str(request.text) } self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) except (ValueError, IndexError, KeyError, requests.exceptions.ConnectionError) as ex: error = {'type': ex.__class__.__name__, 'value': str(ex)} self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) def push_result_topic_pair(self, prefix, point, headers, *args): topic = normtopic('/'.join([prefix, point])) self.publish_json(topic, headers, *args)
now = datetime(year=2013, month=11, day=27, hour=11, minute=30) print_test_header('Basic Test', now) sch_man = ScheduleManager(60, now=now) ag = ('Agent1', 'Task1', (['campus/building/rtu1','2013-11-27 12:00:00','2013-11-27 13:00:00'],), PRIORITY_HIGH, now) result1, event_time1= test_add_task(sch_man, *ag) success, data, info_string = result1 # success1, data1, info_string1 = result1 assert all((success, not data, not info_string, event_time1 == parse('2013-11-27 12:00:00'))) # dict: {'campus/building/rtu1': DeviceState(agent_id='Agent1', task_id='Task1', time_remaining=3600.0)} state = sch_man.get_schedule_state(now + timedelta(minutes=30)) assert state == {'campus/building/rtu1': DeviceState('Agent1', 'Task1', 3600.0)} state = sch_man.get_schedule_state(now + timedelta(minutes=60)) assert state == {'campus/building/rtu1': DeviceState('Agent1', 'Task1', 1800.0)} print_test_header('Basic Test: Two devices', now) sch_man = ScheduleManager(60, now=now) ag = ('Agent1', 'Task1', (['campus/building/rtu1','2013-11-27 12:00:00','2013-11-27 13:00:00'], ['campus/building/rtu2','2013-11-27 12:00:00','2013-11-27 13:00:00']), PRIORITY_HIGH, now) result1, event_time1= test_add_task(sch_man, *ag) success, data, info_string = result1 assert all((success, not data, not info_string, event_time1 == parse('2013-11-27 12:00:00')))
class Agent(PublishMixin, BaseAgent): """Agent to listen for requests to talk to the sMAP driver.""" def __init__(self, **kwargs): super(Agent, self).__init__(**kwargs) self._update_event = None self._update_event_time = None self._device_states = {} self.setup_schedule() self.setup_heartbeats() def setup_heartbeats(self): for point in points: heartbeat_point = points[point].get("heartbeat_point") if heartbeat_point is None: continue heartbeat_handler = self.heartbeat_factory(point, heartbeat_point) self.periodic_timer(heartbeat_interval, heartbeat_handler) def heartbeat_factory(self, point, actuator): # Stupid lack on nonlocal in 2.x value = [False] request_url = "/".join([url, point, ACTUATOR_COLLECTION, actuator]) publish_topic = "/".join([point, actuator]) def update_heartbeat_value(): _log.debug("update_heartbeat") value[0] = not value[0] payload = {"state": str(int(value[0]))} try: _log.debug("About to publish actuation") r = requests.put(request_url, params=payload, timeout=connection_timeout) self.process_smap_request_result(r, publish_topic, None) except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as ex: print "Warning: smap driver not running." _log.error("Connection error: " + str(ex)) return update_heartbeat_value def setup_schedule(self): now = datetime.datetime.now() self._schedule_manager = ScheduleManager(preempt_grace_time, now=now, state_file_name=schedule_state_file) self.update_device_state_and_schedule(now) def update_device_state_and_schedule(self, now): _log.debug("update_device_state_and schedule") self._device_states = self._schedule_manager.get_schedule_state(now) schedule_next_event_time = self._schedule_manager.get_next_event_time(now) new_update_event_time = self._get_ajusted_next_event_time(now, schedule_next_event_time) for device, state in self._device_states.iteritems(): header = self.get_headers(state.agent_id, time=str(now), task_id=state.task_id) header["window"] = state.time_remaining topic = topics.ACTUATOR_SCHEDULE_ANNOUNCE_RAW.replace("{device}", device) self.publish_json(topic, header, {}) if self._update_event is not None: # This won't hurt anything if we are canceling ourselves. self._update_event.cancel() self._update_event_time = new_update_event_time self._update_event = EventWithTime(self._update_schedule_state) self.schedule(self._update_event_time, self._update_event) def _get_ajusted_next_event_time(self, now, next_event_time): _log.debug("_get_adjusted_next_event_time") latest_next = now + datetime.timedelta(seconds=schedule_publish_interval) # Round to the next second to fix timer goofyness in agent timers. if latest_next.microsecond: latest_next = latest_next.replace(microsecond=0) + datetime.timedelta(seconds=1) if next_event_time is None or latest_next < next_event_time: return latest_next return next_event_time def _update_schedule_state(self, unix_time): # Find the current slot and update the state now = datetime.datetime.fromtimestamp(unix_time) self.update_device_state_and_schedule(now) @matching.match_regex(topics.ACTUATOR_GET() + "/(.+)") def handle_get(self, topic, headers, message, match): point = match.group(1) collection_tokens, point_name = point.rsplit("/", 1) requester = headers.get("requesterID") if self.check_lock(collection_tokens, requester): request_url = "/".join([url, collection_tokens, ACTUATOR_COLLECTION, point_name]) try: r = requests.get(request_url, timeout=connection_timeout) self.process_smap_request_result(r, point, requester) except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as ex: error = {"type": ex.__class__.__name__, "value": str(ex)} self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) else: error = {"type": "LockError", "value": "does not have this lock"} self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) @matching.match_regex(topics.ACTUATOR_SET() + "/(.+)") def handle_set(self, topic, headers, message, match): _log.debug("handle_set: {topic},{headers}, {message}".format(topic=topic, headers=headers, message=message)) point = match.group(1) collection_tokens, point_name = point.rsplit("/", 1) requester = headers.get("requesterID") headers = self.get_headers(requester) if not message: error = {"type": "ValueError", "value": "missing argument"} _log.debug("ValueError: " + str(error)) self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) return else: try: message = jsonapi.loads(message[0]) if isinstance(message, bool): message = int(message) except ValueError as ex: # Could be ValueError of JSONDecodeError depending # on if simplesjson was used. JSONDecodeError # inherits from ValueError _log.debug("ValueError: " + message) error = {"type": "ValueError", "value": str(ex)} self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) return if self.check_lock(collection_tokens, requester): request_url = "/".join([url, collection_tokens, ACTUATOR_COLLECTION, point_name]) payload = {"state": str(message)} try: r = requests.put(request_url, params=payload, timeout=connection_timeout) self.process_smap_request_result(r, point, requester) except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as ex: error = {"type": ex.__class__.__name__, "value": str(ex)} self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) _log.debug("ConnectionError: " + str(error)) else: error = {"type": "LockError", "value": "does not have this lock"} _log.debug("LockError: " + str(error)) self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) def check_lock(self, device, requester): _log.debug("check_lock: {device}, {requester}".format(device=device, requester=requester)) device = device.strip("/") if device in self._device_states: device_state = self._device_states[device] return device_state.agent_id == requester return False @matching.match_exact(topics.ACTUATOR_SCHEDULE_REQUEST()) def handle_schedule_request(self, topic, headers, message, match): request_type = headers.get("type") now = datetime.datetime.now() _log.debug( "handle_schedule_request: {topic}, {headers}, {message}".format( topic=topic, headers=str(headers), message=str(message) ) ) if request_type == SCHEDULE_ACTION_NEW: self.handle_new(headers, message, now) elif request_type == SCHEDULE_ACTION_CANCEL: self.handle_cancel(headers, now) else: _log.debug("handle-schedule_request, invalid request type") self.publish_json( topics.ACTUATOR_SCHEDULE_RESULT(), headers, {"result": SCHEDULE_RESPONSE_FAILURE, "data": {}, "info": "INVALID_REQUEST_TYPE"}, ) def handle_new(self, headers, message, now): requester = headers.get("requesterID") taskID = headers.get("taskID") priority = headers.get("priority") _log.debug( "Got new schedule request: {headers}, {message}".format(headers=str(headers), message=str(message)) ) try: requests = jsonapi.loads(message[0]) except (ValueError, IndexError) as ex: # Could be ValueError of JSONDecodeError depending # on if simplesjson was used. JSONDecodeError # inherits from ValueError # We let the schedule manager tell us this is a bad request. _log.error("bad request: {request}, {error}".format(request=requests, error=str(ex))) requests = [] result = self._schedule_manager.request_slots(requester, taskID, requests, priority, now) success = SCHEDULE_RESPONSE_SUCCESS if result.success else SCHEDULE_RESPONSE_FAILURE # If we are successful we do something else with the real result data data = result.data if not result.success else {} topic = topics.ACTUATOR_SCHEDULE_RESULT() headers = self.get_headers(requester, task_id=taskID) headers["type"] = SCHEDULE_ACTION_NEW self.publish_json(topic, headers, {"result": success, "data": data, "info": result.info_string}) # Dealing with success and other first world problems. if result.success: self.update_device_state_and_schedule(now) for preempted_task in result.data: topic = topics.ACTUATOR_SCHEDULE_RESULT() headers = self.get_headers(preempted_task[0], task_id=preempted_task[1]) headers["type"] = SCHEDULE_ACTION_CANCEL self.publish_json( topic, headers, { "result": SCHEDULE_CANCEL_PREEMPTED, "info": "", "data": {"agentID": requester, "taskID": taskID}, }, ) def handle_cancel(self, headers, now): requester = headers.get("requesterID") taskID = headers.get("taskID") result = self._schedule_manager.cancel_task(requester, taskID, now) success = SCHEDULE_RESPONSE_SUCCESS if result.success else SCHEDULE_RESPONSE_FAILURE topic = topics.ACTUATOR_SCHEDULE_RESULT() self.publish_json(topic, headers, {"result": success, "info": result.info_string, "data": {}}) if result.success: self.update_device_state_and_schedule(now) def get_headers(self, requester, time=None, task_id=None): headers = {} if time is not None: headers["time"] = time else: headers = {"time": str(datetime.datetime.utcnow())} if requester is not None: headers["requesterID"] = requester if task_id is not None: headers["taskID"] = task_id return headers def process_smap_request_result(self, request, point, requester): _log.debug( "Start of process_smap: \n{request}, \n{point}, \n{requester}".format( request=request, point=point, requester=requester ) ) headers = self.get_headers(requester) try: request.raise_for_status() results = request.json() readings = results["Readings"] _log.debug("Readings: {readings}".format(readings=readings)) reading = readings[0][1] self.push_result_topic_pair(VALUE_RESPONSE_PREFIX, point, headers, reading) except requests.exceptions.HTTPError as ex: error = {"type": ex.__class__.__name__, "value": str(request.text)} _log.error("process_smap HTTPError: " + str(error)) self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) except (ValueError, IndexError, KeyError, requests.exceptions.ConnectionError) as ex: error = {"type": ex.__class__.__name__, "value": str(ex)} _log.error("process_smap RequestError: " + str(error)) self.push_result_topic_pair(ERROR_RESPONSE_PREFIX, point, headers, error) _log.debug( "End of process_smap: \n{request}, \n{point}, \n{requester}".format( request=request, point=point, requester=requester ) ) def push_result_topic_pair(self, prefix, point, headers, *args): topic = normtopic("/".join([prefix, point])) self.publish_json(topic, headers, *args)