get_multiple_points volttron RPC call

58 Views Asked by At

Any chance I could get a tip for proper way to build an agent that could do read multiple points from multiple devices on a BACnet system? I am viewing the actuator agent code trying learn how to make the proper rpc call.

So going through the agent development procedure with the agent creation wizard.

In the init I have this just hard coded at the moment:

def __init__(self, **kwargs):
    super(Setteroccvav, self).__init__(**kwargs)
    _log.debug("vip_identity: " + self.core.identity)

    self.default_config = {}


    self.agent_id = "dr_event_setpoint_adj_agent"

    self.topic = "slipstream_internal/slipstream_hq/"
    self.jci_zonetemp_string = "/ZN-T"

So the BACnet system in the building has JCI VAV boxes all with the same zone temperature sensor point self.jci_zonetemp_string and self.topic is how I pulled them into volttron/config store through BACnet discovery processes.

In my actuate point function (copied from CSV driver example) am I at all close for how to make the rpc call named reads using the get_multiple_points? Hoping to scrape the zone temperature sensor readings on BACnet device ID's 6,7,8,9,10 which are all the same VAV box controller with the same points/BAS program running.

def actuate_point(self):
    """
    Request that the Actuator set a point on the CSV device
    """

    # Create a start and end timestep to serve as the times we reserve to communicate with the CSV Device
    _now = get_aware_utc_now()
    str_now = format_timestamp(_now)
    _end = _now + td(seconds=10)
    str_end = format_timestamp(_end)

    # Wrap the timestamps and device topic (used by the Actuator to identify the device) into an actuator request
    schedule_request = [[self.ahu_topic, str_now, str_end]]
    
    # Use a remote procedure call to ask the actuator to schedule us some time on the device
    result = self.vip.rpc.call(
        'platform.actuator', 'request_new_schedule', self.agent_id, 'my_test', 'HIGH', schedule_request).get(
        timeout=4)
    _log.info(f'*** [INFO] *** - SCHEDULED TIME ON ACTUATOR From "actuate_point" method sucess')

 
    reads = publish_agent.vip.rpc.call(
                   'platform.actuator',
                   'get_multiple_points',
                   self.agent_id,
                   [(('self.topic'+'6', self.jci_zonetemp_string)),
                   (('self.topic'+'7', self.jci_zonetemp_string)),
                   (('self.topic'+'8', self.jci_zonetemp_string)),
                   (('self.topic'+'9', self.jci_zonetemp_string)),
                   (('self.topic'+'10', self.jci_zonetemp_string))]).get(timeout=10)

Any tips before I break something on the live system greatly appreciated :)

2

There are 2 best solutions below

0
On BEST ANSWER

The basic form of an RPC call to the actuator is as follows:

# use the agent's VIP connection to make an RPC call to the actuator agent 
result = self.vip.rpc.call('platform.actuator', <RPC exported function>, <args>).get(timeout=<seconds>)

Because we're working with devices, we need to know which devices we're interested in, and what their topics are. We also need to know which points on the devices that we're interested in.

device_map = {
  'device1': '201201',
  'device2': '201202',
  'device3': '201203',
  'device4': '201204', 
}

building_topic = 'campus/building'

all_device_points = ['point1', 'point2', 'point3']

Getting points with the actuator requires a list of point topics, or device/point topic pairs.

# we only need one of the following:
point topics = []
for device in device_map.values():
    for point in all_device_points:
        point_topics.append('/'.join([building_topic, device, point]))

device_point_pairs = []
for device in device_map.values():
    for point in all_device_points:
        device_point_pairs.append(('/'.join([building_topic, device]),point,))

Now we send our RPC request to the actuator:

# can use instead device_point_pairs
point_results = self.vip.rpc.call('platform.actuator', 'get_multiple_points', point_topics).get(timeout=3)
0
On

maybe it's just my interpretation of your question, but it seems a little open-ended - so I shall respond in a similar vein - general (& I'll try to keep it short).

First, you need the list of info for targeting each device in-turn; i.e. it might consist of just a IP(v4) address (for the physical device) & the (logical) device's BOIN (BACnet Object Instance Number) - or if the request is been routed/forwarded on by/via a BACnet router/BACnet gateway then maybe also the DNET # & the DADR too.

Then you probably want - for each device/one at a time, to retrieve the first/0-element value of the device's Object-List property - in order to get the number of objects it contains, to allow you to know how many objects are available (including the logical device/device-type object) - that you need to retrieve from it/iterate over; NOTE: in the real world, as much as it's common for the device-type object to be the first one in the list, there's no guarantee it will always be the case.

As much as the BACnet standard started allowing for the retrieval of the Property-List (property) from each & every object, most equipment don't as-yet support it, so you might need your own idea of what properties (/at least the ones of interest to you) that each different object-type supports; probably at the very-very least know which ones/object-types support the Present-Value property & which ones don't.

One ideal would be to have the following mocked facets - as fakes for testing purposes instead of testing against a live/important device (- or at least consider testing against a noddy BACnet enabled Raspberry PI or the harware-based like):

  • a mock for your BACnet service
  • a mock for the BACnet communication stack
  • a mock for your device as a whole (- if you can't write your own one, then maybe even start with the YABE 'Room Control Simulator' as a starting point)

Hope this helps (in some way).