VOLTTRON / volttron

VOLTTRON Distributed Control System Platform
https://volttron.readthedocs.io/
Other
458 stars 216 forks source link

modbus driver sends only one word of float point value. #1283

Closed hyper1003 closed 7 years ago

hyper1003 commented 7 years ago

1) I tried to write a float value (0xABCD) to modbus device using volttron modbus driver. In test.csv, the float point was set to '>f' register type. I found this driver always send on a word (0xAB). ps. 0xABCD means two words. (4bytes) 0xAB means one word. (2bytes) So, I worked around this matter like bellows. actual float value is 0xABCD then converts it bellow two value then send it to two different point. unsigned int value = 0xAB ---> modbus word point 0 unsigned int value =0xCD ----> modbus word point 1 But it isn't a best way i think.

I'm very curious , when I read input register using '>f" type it read a float value(0xABCD) , correctly. but write doesn't work.

Can I send float value to directly to modbus float point?

2) reading float value '>f' works well because most of modbus devices support big endian type mod bus address, but some devices use different way, like bellows actual value = 0xABCD point address 0 : 0xCD point address 1: 0xAB In this case '<f' means 0xBADC(!wrong) Are there any way to solve this matter?

Thanks..

kmonson commented 7 years ago
  1. The MODBUS driver is supposed to handle this properly. This was a case that I thought I had tested extensively when developing the driver. I'll need to investigate this.

  2. Currently the MODBUS driver does not have support for mixed-endian multi-address registers.

hyper1003 commented 7 years ago
  1. Currently the MODBUS driver can read 4bytes float or long value directly not one by one word ( using multi register read fucntion code) and returns value according to the configured endian type ('>' or '<'). so I expected the same behavior to write float value to a device. Because it is really convenient way. please let me know your investigation this.

  2. Can I ask you have a plan to support mixed-endian multi-address registers?

kmonson commented 7 years ago

Hopefully I'll be able to add mixed endian support soon. The problem is that I do not have a device to test against currently.

kmonson commented 7 years ago

I tested the driver with a floating point value using a test device that reports debugging information about what is being written. I cannot duplicate the problem. Both words are written properly and are subsequently the new value is read properly from the device.

What I did see is that it is really easy to use a test value that writes 0 to the least significant word. For instance my first test value was 30.5 which is represented as a float as 0x41F40000 and my second test -30.5 is 0xC1F40000. I had to use a value that is not directly represent-able as a float to get non zero values in the least significant word.

30.03 works and is represented in floating point (in hex) as 0x41F03D71 and is written correctly.

I also tested integers with large values and it works correctly.

What version of VOLTTRON are you using? Could you please comment with a copy of the CSV file that you are using. Maybe it's a problem with your registry configuration file.

hyper1003 commented 7 years ago

I captured the version information in log file .

2017-04-13 08:41:21,909 (volttroncentralagent-3.6.0 12799) volttron.platform.vip.agent.core DEBUG: identity: volttron.central

2017-04-13 08:41:21,918 (sqlhistorianagent-3.6.1 12798) volttron.platform.vip.agent.core DEBUG: Running onstart methods.

2017-04-13 08:41:21,993 (vcplatformagent-3.6.0 12796) volttron.platform.vip.agent.core DEBUG: identity: platform.agent

2017-04-13 08:41:22,064 (master_driveragent-2.0 12797) master_driver.agent INFO: maximum concurrently open sockets limited to 52428 (derived from system limits)

ygkim@R530 ~/volttron $ git checkout origin/releases/4.0.1 M services/core/ActuatorAgent/actuator-deploy.service endianTest.pdf M services/core/ActuatorAgent/actuator/agent.py HEAD is now at 371dc7a... add patch for old urlparse

kmonson commented 7 years ago

I don't see anything wrong with the registry CSV. It does seem kind of generic. What kind of device are you using to test the driver with?

hyper1003 commented 7 years ago

I think the modbus target devices and the csv files are correct.

Let me tell you my simple test agent. I use the the agent.py in volttron/services/core/ActuatorAgent/actuator/ I changed two parts in the original agent.py 1) def init 2) def _temp_function(self):

As I reported, I sent a float value 27.7 (0x41DD999A) to the target modbus driver and snipped the actual tcp/mod bus packet. the wireshark packet snipper shows me in the sented modbus packet there is one word 0x41DD only. Of course the target modbus device received only one word, 0x41DD, too.

Will you please check bellow my agent.py code? I'm not sure I publish the value correclty.

def init(self, heartbeat_interval=60, schedule_publish_interval=60, preempt_grace_time=60, driver_vip_identity='platform.driver', iseed=1, kwargs): super(ActuatorAgent, self).init(kwargs) _log.debug("vip_identity: " + self.core.identity) self._update_event = None self._device_states = {} self.schedule_state_file = "_schedule_state" self.heartbeat_greenlet = None self.heartbeat_interval = heartbeat_interval self._schedule_manager = None

    self.schedule_publish_interval = schedule_publish_interval
    self.subscriptions_setup = False

>>>>>ygkim 2017-03-21

    self.campus='inu-campus'
    self.building='it-building'
    self.unit='gui'
    self.point='co2'
    self.default_config = {"heartbeat_interval": heartbeat_interval,
                          "schedule_publish_interval": schedule_publish_interval,
                          "preempt_grace_time": preempt_grace_time,
                          "driver_vip_identity": driver_vip_identity}

    self.vip.config.set_default("config", self.default_config)
    self.vip.config.subscribe(self.configure, actions=["NEW", "UPDATE"], pattern="config")

def _temp_function(self):

example for data reading in actuator agent

>>>>>ygkim 2017-03-22

     result1= 27.7
    headers = {
                'AgentID': self.core.identity,
                'type': 'NEW_SCHEDULE',
                'requesterID': self.core.identity, 
                'taskID': self.core.identity + "-TASK",
                'priority': 'HIGH',
            }    

    start = str(utils.get_aware_utc_now())
    end = str(utils.get_aware_utc_now()+datetime.timedelta(minutes=1))
    msg = [
            [(self.campus+'/'+self.building+'/'+self.unit), 
             start,     
             end],
          ]
    self.vip.pubsub.publish('pubsub',topics.ACTUATOR_SCHEDULE_REQUEST, headers, msg)

>>>>>ygkim 2017-03-22 topic=topics.ACTUATOR_SET(campus=self.campus,building=self.building,unit=self.unit,point=self.point)

    self.vip.pubsub.publish('pubsub',topic,headers,result1)

Thanks

kmonson commented 7 years ago

For testing I would use the following setup:

Install a listener agent, the master driver, and the actuator.

Run the platform in the foreground so you can see what is happening as it happens. When the driver publishes the state of the device the listener agent will put it in the log and you will be able to see it right away.

Setup your MODBUS driver with an interval setting of 10 seconds. This way you can see the new state of the device shortly after you have written to it.

Run the script below to make changes to the device in a different terminal. Be sure to change the tests list to match your configuration. I would suggest starting with one test on one point.

After running the script watch the log for the next publish of the device state (as logged by the listener agent) and look to see if the changed point reflects what you changed it to.

# -*- coding: utf-8 -*-

import gevent
from volttron.platform.vip.agent import Agent
from volttron.platform import get_address
from volttron.platform.keystore import KeyStore

keystore = KeyStore()
MY_ID = "config_store_stress_test"
agent = Agent(address=get_address(), identity=MY_ID,
              publickey=keystore.public, secretkey=keystore.secret)

event = gevent.event.Event()
gevent.spawn(agent.core.run, event)
event.wait()

def set_point(point, value):
    return agent.vip.rpc.call(
                'platform.actuator',  # Target agent
                'set_point',  # Method to call
                MY_ID,  # Requestor
                point,  # Point to set
                value,  # New value
            ).get(timeout=30)

#Modify this list to match up with the points you want to modify and the values you want to test with.
tests = [("modbus0/point1", -20.5),
         ("modbus0/point2", 30.03),
         ("modbus0/point3", 20),
         ("modbus0/point4", 30)]

for test in tests:
    set_point(*test)

In the future I would suggest not using the pubsub interface for the actuator. It is much easier to use the RPC interface as shown in the above script. Settings points on a device without creating a schedule first is only possible in the latest version of VOLTTRON in develop.

I'd like to see the PCAP file from your tests.