def onmessage(self, peer, sender, bus, topic, headers, message): d = {'topic': topic, 'headers': headers, 'message': message} # Forward message to FNCS if not fncs.is_initialized(): raise RuntimeError("FNCS connection was terminated. Killing Bridge.") fncsmessage = str(message) topic = topic.replace('fncs/input/','') fncs.publish(topic, fncsmessage) _log.debug('Volttron->FNCS:\nTopic:%s\nMessage:%s\n'%(topic, message))
def start(self, sender, **kwargs): self.vip.pubsub.subscribe(peer = 'pubsub', prefix = 'fncs/input/', #prefix = '', callback = self.onmessage).get(timeout=5) #Register with FNCS cfg = "name = {0[name]}\ntime_delta = {0[time_delta]}\nbroker = {0[broker]}\n".format(self.fncs_zpl) if 'values' in self.fncs_zpl.keys(): cfg += "values" for x in self.fncs_zpl['values'].keys(): cfg += "\n {0}\n topic = {1[topic]}\n defualt = {1[default]}\n type = {1[type]}\n list = {1[list]}".format(x,self.fncs_zpl['values'][x]) fncs.initialize(cfg) if not fncs.is_initialized(): raise RuntimeError("FNCS connection failed!") self.publish_heartbeat() print(self.heartbeat_period) self.core.periodic(self.heartbeat_period, self.publish_heartbeat)
def publish_to_fncs(self, goss_message): """publish a message received from the GOSS bus to the FNCS bus. Function arguments: simulation_id -- Type: string. Description: The simulation id. It must not be an empty string. Default: None. goss_message -- Type: string. Description: The message from the GOSS bus as a json string. It must not be an empty string. Default: None. Function returns: None. Function exceptions: RuntimeError() ValueError() """ message_str = 'publish to fncs bus {} {}'.format(self.simulation_id, goss_message) self._gridappsd.send_simulation_status('RUNNING', message_str, DEBUG) if not self.simulation_id or type(self.simulation_id) != str: raise ValueError( 'simulation_id must be a nonempty string.\n' + 'simulation_id = {0}'.format(self.simulation_id)) if not goss_message or type(goss_message) != str: raise ValueError( 'goss_message must be a nonempty string.\n' + 'goss_message = {0}'.format(goss_message)) if not fncs.is_initialized(): raise RuntimeError( 'Cannot publish message as there is no connection' + ' to the FNCS message bus.') try: test_goss_message_format = yaml.safe_load(goss_message) if type(test_goss_message_format) != dict: raise ValueError( 'goss_message is not a json formatted string.' + '\ngoss_message = {0}'.format(goss_message)) except ValueError as ve: raise ValueError(ve) except: raise RuntimeError( 'Unexpected error occured while executing yaml.safe_load(goss_message' + '{0}'.format(sys.exc_info()[0])) fncs_input_topic = '{0}/fncs_input'.format(self.simulation_id) message_str = 'fncs input topic ' + fncs_input_topic self._gridappsd.send_simulation_status('RUNNING', message_str, DEBUG) fncs.publish_anon(fncs_input_topic, goss_message)
def _publishToFncsBus(simulationId, gossMessage): '''publish a message received from the GOSS bus to the FNCS bus. Function arguments: simulationId -- Type: string. Description: The simulation id. It must not be an empty string. Default: None. gossMessage -- Type: string. Description: The message from the GOSS bus as a json string. It must not be an empty string. Default: None. Function returns: None. Function exceptions: RuntimeError() ValueError() ''' logger.debug('publish to fncs bus ' + simulationId + ' ' + str(gossMessage)) if simulationId == None or simulationId == '' or type(simulationId) != str: raise ValueError('simulationId must be a nonempty string.\n' + 'simulationId = {0}'.format(simulationId)) if gossMessage == None or gossMessage == '' or type(gossMessage) != str: raise ValueError('gossMessage must be a nonempty string.\n' + 'gossMessage = {0}'.format(gossMessage)) if not fncs.is_initialized(): raise RuntimeError('Cannot publish message as there is no connection' + ' to the FNCS message bus.') try: testGossMessageFormat = yaml.safe_load(gossMessage) if type(testGossMessageFormat) != dict: raise ValueError('gossMessage is not a json formatted string.' + '\ngossMessage = {0}'.format(gossMessage)) except ValueError as ve: raise ValueError(ve) except: raise RuntimeError( 'Unexpected error occured while executing yaml.safe_load(gossMessage' + '{0}'.format(sys.exc_info()[0])) fncsInputTopic = '{0}/fncs_input'.format(simulationId) logger.debug('fncs input topic ' + fncsInputTopic) fncs.publish_anon(fncsInputTopic, gossMessage)
def on_disconnected(self): if fncs.is_initialized(): fncs.die()
def on_error(self, headers, message): message_str = 'Error in {} {}'.format('GridAPPSDListener', message) self._gridappsd.send_simulation_status('STOPPED', message_str) if fncs.is_initialized(): fncs.die()
def on_message(self, headers, msg): message = {} try: message_str = 'received message ' + str(msg) if fncs.is_initialized(): self._gridappsd.send_simulation_status('RUNNING', message_str, DEBUG) else: self._gridappsd.send_simulation_status('STARTED', message_str, DEBUG) json_msg = yaml.safe_load(str(msg)) if json_msg['command'] == 'isInitialized': message_str = 'isInitialized check: ' + str(is_initialized) if fncs.is_initialized(): self._gridappsd.send_simulation_status('RUNNING', message_str, DEBUG) else: self._gridappsd.send_simulation_status('STARTED', message_str, DEBUG) message['command'] = 'isInitialized' message['response'] = str(is_initialized) if self.simulation_id is not None: message['output'] = self._bridge.get_messages_from_fncs() message_str = 'Added isInitialized output, sending message {}'.format(message) if fncs.is_initialized(): self._gridappsd.send_simulation_status('RUNNING', message_str, DEBUG) else: self._gridappsd.send_simulation_status('RUNNING', message_str, DEBUG) self._gridappsd.send(topic=self._bridge.simulation_output_topic, message=json.dumps(message)) elif json_msg['command'] == 'update': message['command'] = 'update' # does not return self._bridge.publish_to_fncs(json.dumps(json_msg['message'])) elif json_msg['command'] == 'nextTimeStep': message_str = 'is next timestep' self._gridappsd.send_simulation_status('RUNNING', message_str, DEBUG) message['command'] = 'nextTimeStep' current_time = json_msg['currentTime'] message_str = 'incrementing to ' + str(current_time) self._gridappsd.send_simulation_status('RUNNING', message_str, DEBUG) # current_time is incrementing integer 0 ,1, 2.... representing seconds self._bridge.timestep_complete(current_time) message_str = 'done with timestep ' + str(current_time) self._gridappsd.send_simulation_status('RUNNING', message_str, DEBUG) message_str = 'simulation id ' + str(self._bridge.simulation_id) self._gridappsd.send_simulation_status('RUNNING', message_str, DEBUG) message['output'] = self._bridge.get_messages_from_fncs() response_msg = json.dumps(message) message_str = 'sending fncs output message ' + str(response_msg) self._gridappsd.send_simulation_status('RUNNING', message_str, DEBUG) self._gridappsd.send(topic=self._bridge.simulation_output_topic, message=json.dumps(message)) elif json_msg['command'] == 'stop': message_str = 'Stopping the simulation' self._gridappsd.send_simulation_status('STOPPED', message_str) fncs.die() sys.exit() except Exception as e: message_str = 'Error in command ' + str(e) self._gridappsd.send_simulation_status('ERROR', message_str, ERROR) if fncs.is_initialized(): fncs.die()
def start_bridge(self): """Register with the fncs_broker and return. Function arguments: broker_location -- Type: string. Description: The ip location and port for the fncs_broker. It must not be an empty string. Default: 'tcp://localhost:5570'. Function returns: None. Function exceptions: RuntimeError() ValueError() """ global is_initialized # First connect with goos via the GridAPPSD interface self._gridappsd = GridAPPSD(self.simulation_id, id=2, base_simulation_status_topic=BASE_SIMULATION_STATUS_TOPIC) self._gridappsd_listener = GridAPPSDListener(self, self._gridappsd) self._gridappsd.subscribe(self.simulation_input_topic, callback=self._gridappsd_listener) self._gridappsd.send_simulation_status("STARTING", "Starting Bridge for simulation id: {}".format(self.simulation_id)) configuration_zpl = '' try: message_str = 'Registering with FNCS broker ' + str(self.simulation_id) + ' and broker ' + self.fncs_broker_location self._gridappsd.send_simulation_status("STARTED", message_str) message_str = 'connected to goss {}'.format(self._gridappsd.connected) self._gridappsd.send_simulation_status("STARTED", message_str) if not self.simulation_id or type(self.simulation_id) != str: raise ValueError( 'simulation_id must be a nonempty string.\n' + 'simulation_id = {0}'.format(self.simulation_id)) if not self.fncs_broker_location or type(self.fncs_broker_location) != str: raise ValueError( 'broker_location must be a nonempty string.\n' + 'broker_location = {0}'.format(self.fncs_broker_location)) fncs_configuration = { 'name': 'FNCS_GOSS_Bridge_' + self.simulation_id, 'time_delta': '1s', 'broker': self.fncs_broker_location, 'values': { self.simulation_id: { 'topic': self.simulation_id + '/fncs_output', 'default': '{}', 'type': 'JSON', 'list': 'false' } } } configuration_zpl = ('name = {0}\n'.format(fncs_configuration['name']) + 'time_delta = {0}\n'.format(fncs_configuration['time_delta']) + 'broker = {0}\nvalues'.format(fncs_configuration['broker'])) for x in fncs_configuration['values'].keys(): configuration_zpl += '\n {0}'.format(x) configuration_zpl += '\n topic = {0}'.format( fncs_configuration['values'][x]['topic']) configuration_zpl += '\n default = {0}'.format( fncs_configuration['values'][x]['default']) configuration_zpl += '\n type = {0}'.format( fncs_configuration['values'][x]['type']) configuration_zpl += '\n list = {0}'.format( fncs_configuration['values'][x]['list']) fncs.initialize(configuration_zpl) is_initialized = fncs.is_initialized() if is_initialized: message_str = 'Registered with fncs ' + str(is_initialized) self._gridappsd.send_simulation_status("RUNNING", message_str) except Exception as e: message_str = 'Error while registering with fncs broker ' + str(e) self._gridappsd.send_simulation_status('ERROR', message_str, 'ERROR') if fncs.is_initialized(): fncs.die() if not fncs.is_initialized(): message_str = 'fncs.initialize(configuration_zpl) failed!\n' + 'configuration_zpl = {0}'.format( configuration_zpl) self._gridappsd.send_simulation_status('ERROR', message_str, 'ERROR') if fncs.is_initialized(): fncs.die() raise RuntimeError( 'fncs.initialize(configuration_zpl) failed!\n' + 'configuration_zpl = {0}'.format(configuration_zpl))
def on_message(self, headers, msg): message = {} try: message_str = 'received message ' + str(msg) if fncs.is_initialized(): _send_simulation_status('RUNNING', message_str, 'DEBUG') else: _send_simulation_status('STARTED', message_str, 'DEBUG') json_msg = yaml.safe_load(str(msg)) if json_msg['command'] == 'isInitialized': message_str = 'isInitialized check: ' + str(is_initialized) if fncs.is_initialized(): _send_simulation_status('RUNNING', message_str, 'DEBUG') else: _send_simulation_status('STARTED', message_str, 'DEBUG') message['command'] = 'isInitialized' message['response'] = str(is_initialized) if (simulation_id != None): message['output'] = _get_fncs_bus_messages(simulation_id) message_str = 'Added isInitialized output, sending message ' + str( message) + ' connection ' + str(goss_connection) if fncs.is_initialized(): _send_simulation_status('RUNNING', message_str, 'DEBUG') else: _send_simulation_status('STARTED', message_str, 'DEBUG') goss_connection.send(output_to_goss_topic, json.dumps(message)) goss_connection.send(output_to_goss_queue, json.dumps(message)) elif json_msg['command'] == 'update': message['command'] = 'update' _publish_to_fncs_bus( simulation_id, json.dumps(json_msg['message'])) #does not return elif json_msg['command'] == 'nextTimeStep': message_str = 'is next timestep' _send_simulation_status('RUNNING', message_str, 'DEBUG') message['command'] = 'nextTimeStep' current_time = json_msg['currentTime'] message_str = 'incrementing to ' + str(current_time) _send_simulation_status('RUNNING', message_str, 'DEBUG') _done_with_time_step( current_time ) #current_time is incrementing integer 0 ,1, 2.... representing seconds message_str = 'done with timestep ' + str(current_time) _send_simulation_status('RUNNING', message_str, 'DEBUG') message_str = 'simulation id ' + str(simulation_id) _send_simulation_status('RUNNING', message_str, 'DEBUG') message['output'] = _get_fncs_bus_messages(simulation_id) response_msg = json.dumps(message) message_str = 'sending fncs output message ' + str( response_msg) _send_simulation_status('RUNNING', message_str, 'DEBUG') goss_connection.send(output_to_goss_topic, response_msg) goss_connection.send(output_to_goss_queue, response_msg) elif json_msg['command'] == 'stop': message_str = 'Stopping the simulation' _send_simulation_status('stopped', message_str, 'INFO') fncs.die() sys.exit() except Exception as e: message_str = 'Error in command ' + str(e) _send_simulation_status('ERROR', message_str, 'ERROR') if fncs.is_initialized(): fncs.die()
def _register_with_fncs_broker(broker_location='tcp://localhost:5570'): """Register with the fncs_broker and return. Function arguments: broker_location -- Type: string. Description: The ip location and port for the fncs_broker. It must not be an empty string. Default: 'tcp://localhost:5570'. Function returns: None. Function exceptions: RuntimeError() ValueError() """ global is_initialized configuration_zpl = '' try: message_str = 'Registering with FNCS broker ' + str( simulation_id) + ' and broker ' + broker_location _send_simulation_status('STARTED', message_str, 'INFO') message_str = 'still connected to goss 1 ' + str( goss_connection.is_connected()) _send_simulation_status('STARTED', message_str, 'INFO') if simulation_id == None or simulation_id == '' or type( simulation_id) != str: raise ValueError('simulation_id must be a nonempty string.\n' + 'simulation_id = {0}'.format(simulation_id)) if (broker_location == None or broker_location == '' or type(broker_location) != str): raise ValueError('broker_location must be a nonempty string.\n' + 'broker_location = {0}'.format(broker_location)) fncs_configuration = { 'name': 'FNCS_GOSS_Bridge_' + simulation_id, 'time_delta': '1s', 'broker': broker_location, 'values': { simulation_id: { 'topic': simulation_id + '/fncs_output', 'default': '{}', 'type': 'JSON', 'list': 'false' } } } configuration_zpl = ( 'name = {0}\n'.format(fncs_configuration['name']) + 'time_delta = {0}\n'.format(fncs_configuration['time_delta']) + 'broker = {0}\nvalues'.format(fncs_configuration['broker'])) for x in fncs_configuration['values'].keys(): configuration_zpl += '\n {0}'.format(x) configuration_zpl += '\n topic = {0}'.format( fncs_configuration['values'][x]['topic']) configuration_zpl += '\n default = {0}'.format( fncs_configuration['values'][x]['default']) configuration_zpl += '\n type = {0}'.format( fncs_configuration['values'][x]['type']) configuration_zpl += '\n list = {0}'.format( fncs_configuration['values'][x]['list']) fncs.initialize(configuration_zpl) is_initialized = fncs.is_initialized() if is_initialized: message_str = 'Registered with fncs ' + str(is_initialized) _send_simulation_status('RUNNING', message_str, 'INFO') except Exception as e: message_str = 'Error while registering with fncs broker ' + str(e) _send_simulation_status('ERROR', message_str, 'ERROR') if fncs.is_initialized(): fncs.die() if not fncs.is_initialized(): message_str = 'fncs.initialize(configuration_zpl) failed!\n' + 'configuration_zpl = {0}'.format( configuration_zpl) _send_simulation_status('ERROR', message_str, 'ERROR') if fncs.is_initialized(): fncs.die() raise RuntimeError('fncs.initialize(configuration_zpl) failed!\n' + 'configuration_zpl = {0}'.format(configuration_zpl))
def on_error(self, headers, message): message_str = 'Error in goss listener ' + str(message) _send_simulation_status('ERROR', message_str, 'ERROR') if fncs.is_initialized(): fncs.die()
def _registerWithFncsBroker(simId, brokerLocation='tcp://localhost:5570'): '''Register with the fncs_broker and return. Function arguments: simulationId -- Type: string. Description: The simulation id. It must not be an empty string. Default: None. brokerLocation -- Type: string. Description: The ip location and port for the fncs_broker. It must not be an empty string. Default: 'tcp://localhost:5570'. Function returns: None. Function exceptions: RuntimeError() ValueError() ''' global simulationId global isInitialized simulationId = simId try: logger.info('Registering with FNCS broker ' + str(simulationId) + ' and broker ' + brokerLocation) logger.debug('still connected to goss 1 ' + str(gossConnection.is_connected())) if simulationId == None or simulationId == '' or type( simulationId) != str: raise ValueError('simulationId must be a nonempty string.\n' + 'simulationId = {0}'.format(simulationId)) if (brokerLocation == None or brokerLocation == '' or type(brokerLocation) != str): raise ValueError('brokerLocation must be a nonempty string.\n' + 'brokerLocation = {0}'.format(brokerLocation)) fncsConfiguration = { 'name': 'FNCS_GOSS_Bridge_' + simulationId, 'time_delta': '1s', 'broker': brokerLocation, 'values': { simulationId: { 'topic': simulationId + '/fncs_output', 'default': '{}', 'type': 'JSON', 'list': 'false' } } } configurationZpl = ( 'name = {0}\n'.format(fncsConfiguration['name']) + 'time_delta = {0}\n'.format(fncsConfiguration['time_delta']) + 'broker = {0}\nvalues'.format(fncsConfiguration['broker'])) for x in fncsConfiguration['values'].keys(): configurationZpl += '\n {0}'.format(x) configurationZpl += '\n topic = {0}'.format( fncsConfiguration['values'][x]['topic']) configurationZpl += '\n default = {0}'.format( fncsConfiguration['values'][x]['default']) configurationZpl += '\n type = {0}'.format( fncsConfiguration['values'][x]['type']) configurationZpl += '\n list = {0}'.format( fncsConfiguration['values'][x]['list']) fncs.initialize(configurationZpl) isInitialized = fncs.is_initialized() logger.info('Registered with fncs ' + str(isInitialized)) except Exception as e: logger.error('Error while registering with fncs broker ' + str(e)) if not fncs.is_initialized(): raise RuntimeError('fncs.initialize(configurationZpl) failed!\n' + 'configurationZpl = {0}'.format(configurationZpl))
def publish_heartbeat(self): '''Send heartbeat message every HEARTBEAT_PERIOD seconds. HEARTBEAT_PERIOD is set and can be adjusted in the settings module. ''' now = datetime.utcnow().isoformat(' ') + 'Z' nowdate = datetime.utcnow() print "publish_heartbeat", now timeDiff = nowdate - self.simStart valMap = defaultdict(dict) metaMap = defaultdict(dict) headers = {headers_mod.TIMESTAMP: now, headers_mod.DATE: now} #Tell FNCS we are at our next timestep if not fncs.is_initialized(): raise RuntimeError("FNCS connection was terminated. Killing Bridge.") elif self.simtime > self.simlength: fncs.finalize() self.core.stop() elif timeDiff.seconds >= 1: self.simtime+=self.heartbeat_period*self.heartbeat_multiplier print "fncs.time_request(",self.simtime,") request" self.simtime = fncs.time_request(self.simtime) print "fncs.time_request() response", self.simtime #Grab Subscriptions from FNCS to publish to Volttron message bus subKeys = fncs.get_events() if len(subKeys) > 0: for x in subKeys: valStr = fncs.get_value(x) #parse message to split value and unit valList = valStr.split(' ') if len(valList) == 1: val = valList[0] valUnit = ''; try: val = float(val) except: pass elif len(valList) == 2: val = valList[0] valUnit = valList[1] if 'int' in self.fncs_zpl['values'][x]['type']: val = int(val) elif 'double' in self.fncs_zpl['values'][x]['type']: val = float(val) elif 'complex' in self.fncs_zpl['values'][x]['type']: raise RuntimeError("complex data type is currently not supported in Volttron.") #TODO: come up with a better way to handle all types that can come in from fncs else: warnings.warn("FNCS message could not be parsed into value and unit. The message will be farwarded to Volttron message bus as is.") val = valStr valUnit = '' fncsmessage = [val, {'units' : '{0}'.format(valUnit), 'tz' : 'UTC', 'type': '{0[type]}'.format(self.fncs_zpl['values'][x])}] fncsTopic = common.FNCS_OUTPUT_PATH(path = 'devices/{0[topic]}'.format(self.fncs_zpl['values'][x])) #fncs/output/devices/topic self.vip.pubsub.publish('pubsub', fncsTopic, headers, fncsmessage).get(timeout=5) _log.debug('FNCS->Volttron:\nTopic:%s\n:Message:%s\n'%(fncsTopic, str(fncsmessage))) device, point = self.fncs_zpl['values'][x]['topic'].rsplit('/', 1) deviceAllTopic = common.FNCS_OUTPUT_PATH(path = 'devices/' + device + '/all') valMap[deviceAllTopic][point] = val metaMap[deviceAllTopic][point] = fncsmessage[1] for k in valMap.keys(): allMessage = [valMap[k], metaMap[k]] self.vip.pubsub.publish('pubsub', k, headers, allMessage).get(timeout=5) _log.debug('FNCS->Volttron:\nTopic:%s\n:Message:%s\n'%(k, str(allMessage))) #Publish heartbeat message to voltron bus self.vip.pubsub.publish( 'pubsub', '{0[name]}/heartbeat'.format(self.fncs_zpl), headers, now).get(timeout=5)
#Publish heartbeat message to voltron bus self.vip.pubsub.publish( 'pubsub', '{0[name]}/heartbeat'.format(self.fncs_zpl), headers, now).get(timeout=5) def fncs_bridge(**kwargs): config = utils.load_config('FNCS_VOLTTRON_Bridge.config') heartbeat_period = config.get('heartbeat_period', 1) heartbeat_multiplier = config.get('heartbeat_multiplier', 1) fncs_zpl = config["fncs_zpl"] params = config["remote_platform_params"] simulation_run_time = config.get("simulation_run_time", "1h") return FNCS_VOLTTRON_Bridge(simulation_run_time, heartbeat_period, heartbeat_multiplier, fncs_zpl, address=remote_url(**params), identity='FNCS_Volttron_Bridge') def main(): '''Main method to start the agent''' utils.vip_main(fncs_bridge) if __name__ == '__main__': # Entry point for script try: sys.exit(main()) except KeyboardInterrupt: if fncs.is_initialized(): fncs.die() pass