I would like to use RPC calls in my volttron application but I am unable to get any call working. All calls fail with a "no route to host" error
<stderr> ERROR: Unreachable: VIP error (113): No route to host: rpcserver.agent_1
Essentially there are two agents, a "server" agent that exports a RPC procedure, and a "client" agent that calls the procedure.
In the "server" agent I have exported a method of the agent class as follows:
@RPC.export('setConfig')
def setConfig(self, config):
self.config = config
self.initialize_device()
The "client" agent call the exported method as follows:
self.vip.rpc.call(sender, 'setConfig', self.config[sender]).get()
Where "sender" is the VIP identity of the "server" agent (which turns out to be "rpcserver.agent_1" and corresponds to the value of the "sender" argument when receiving a pubsub message from rpcserver.agent. I have defined the identity as rpcserver.agent_{n} in the IDENTITY file).
My questions are:
1. Am I doing something obviously wrong or missing some step to set up RPC subsystem properly?
2. Is the "peer" argument in self.vip.rpc.call(peer, method, ...)
expected to be the agent's identity? This is not clear in the documentation (I have tried other options such as agent name or uuid but none worked)
I am running volttron 5.1.0 in a Ubuntu VM.
Any help with this would be very much appreciated. Best regards
DETAILS:
This is for a control application that interconnects multiple devices. Agents are loaded dynamically depending on the available devices in the network. I would like to try out RPCs instead of using only pubsub. I have already quite thoroughly searched the Volttron code and documentation for details on the RPC API to no avail so far.
Minimal "server" class:
class rpcServerAgent(Agent):
def __init__(self, config, **kwargs):
super(rpcServerAgent, self).__init__(**kwargs)
self.config = config
@Core.receiver('onstart')
def onstart(self, sender, **kwargs):
self.vip.rpc.export(self.setConfig, name='setConfig') # Also tried online exporting
# Ask the client to call the exported procedure
myutils.publish(self, topic='rpc/test', message={}) # myutils.publish publishes the message on pubsub
@RPC.export('setConfig')
def setConfig(self, config):
self.config = config
self.initialize_device()
myutils.publish(self, topic='rpc/clientready')
def initialize_device(self):
pass
Minimal "client" class:
class rpcClientAgent(Agent):
def __init__(self, config, **kwargs):
super(rpcClientAgent, self).__init__(**kwargs)
self.config = {'rpcclient.agent_1': {'a': 0, 'b': 1}} # dummy config for rpcclient.agent
@Core.receiver('onstart')
def onstart(self, sender, **kwargs):
self.vip.pubsub.subscribe(peer='pubsub',
prefix='rpc',
callback=self.__handle_request__).get(timeout=5)
def call_RPC(self, sender):
sender = sender.strip() # volttron adds a newline at the end
self.vip.rpc.call(sender, 'setConfig', self.config[sender]).get() # assume that self.config[sender] is well-defined
@PubSub.subscribe('pubsub', 'rpc')
def __handle_request__(self, peer, sender, bus, topic, headers, message):
try:
msg = json.loads(message)
except:
raise ValueError("failed to decode message")
topics = topic.split('/')
if len(topics) > 1:
if topics[0] == 'rpc':
if topics[1] == 'test':
self.call_RPC(sender)
Expected behavior: exported function is called and a pubsub message with topic "rpc/clientready" is published.
Actual behavior: RPc call fails with error "Unreachable: VIP error (113): No route to host: rpcclient.agent_1"
Edit I eventually found the problem was that the agent's identity inside volttron ended with a '\n' character. This was due to gedit automatically appending it and apparently the string is not stripped by volttron.