From:                                         Davis, Mark

Sent:                                           Friday, May 28, 2010 10:45 AM

To:                                               Vuppala, Vasu

Cc:                                               Priller, John

Subject:                                     RE: First portion of CA/PV interface to ModBus register definitions

 

Vasu,

 

I have added the functions to support event processing now.  With these additional functions you should have everything you need to interface with the register definitions without any application-specific code required.

 

New functions:

 

      int pvCheckEvents(long pid, uint checkEvents);

 

      void pvCheckAllEvents(time_t *dateTime, uint *ms);

 

And a change to one of the previous functions:

 

     int pvGetVal(long pid, uint toType, uint toSize, void *toVal, uint event);

 

 

The new functions work together as follows:

 

    pvCheckEvents() returns a bit-mask of the events that the last call to pvCheckAllEvents() detected and adds to the events to be checked for the PV the next time pvCheckAllEvents() is called.

 

    pvCheckAllEvents() updates the bitmask of events that happened for each PV (based solely on the calls to pvCheckEvents() since the last time pvCheckAllEvents() was called)

 

 

These functions should be used as follows:

 

   Each time you get a new subscription, call pvCheckEvents() with the PID for the channel and the checkEvents argument = to the Monitor Mask supplied with the subscription.

 

   On a periodic basis (once each time through the main loop of the epics_cad task, which should occur about 5 to 10 times per second), call pvCheckAllEvents() once, then call pvCheckEvents() for each subscription on each connection.

 

 

The functions work as follows:

 

   A checkEvents and eventsOccured bit-mask is kept for each PV.  A call to pvCheckEvents() adds the bits in the checkEvents argument to the ones already set for the PV.  Each call to pvCheckAllEvents() clears the eventsOccured mask, updates it based on the checkEvents mask for the PV, then clears the PV’s checkEvents mask.

 

Note how this keeps things very simple:  Having pvCheckAllEvents() clear the checkEvents mask for each PV and pvCheckEvents() reassert the bits, we insure that the checkEvents bits for each PV represent only the active subscriptions each time pvCheckAllEvents() is called.  No need for the additional memory and complexity of trying to keep a counter for each event for each PV, or (even worse) the structures that would be needed to support a callback approach.

 

 

The addedl parameter in the pvGetVal() call allows you to get the current value for the PV (event = CUR_VALUE) or the value the PV had when the specified event was detected by the last call to pvCheckAllEvents() (e.g. event = DBE_VALUE).

 

 

This model continues to assume that there is a single process managing all the connections.  I still think this is the best model.  Basically the loop looks like this:

 

-          Check for search requests and process each one received

 

-          Check for a new Virtual Circuit request (i.e. TCP connection via a call to accept())

 

-          Call poll() with a timeout on each of the existing TCP connections

 

-          For each connection that we received some data on, process at least one command if possible

 

-          Call pvCheckAllEvents() (keep the returned date and time for use when sending messages that include a timestamp with the data)

 

-          For each subscription on each circuit, call pvCheckEvents() and send a message (as needed) for each event detected (use write_() so you can specify a timeout)

 

To avoid or at least minimize the risk of delays caused by a connection that is having a problems, I suggest a combination of the following:

-          ioctl(fd, FIONREAD, …), to determine how many bytes can be read without blocking

-          Changing an fd to non-blocking mode before doing a read()

-          Use of read_() and write_(), which provide a timeout on a blocking read or write

-          If an error occurs on a circuit, then close the connection for that circuit.

 

The read_() and write_() functions, while not standard Linux/Unix functions, could easily be implemented on those platforms.

 

While there is always the possibility that a connection with a problem will cause a delay with processing of the other connections, this approach still seems to be the best in terms of avoiding the need for synchronizing multiple tasks or using large buffers to deal with incomplete commands (which we don’t’ have the resources for).   And given the fact that we will rarely HAVE more than one active connection, I think even that risk is not significant.

 

Also, keep in mind that IOC’s have to deal with hundreds or even thousands of connections, and I don’t believe they create one thread or task per connection.  So they must make similar compromises.

 

 

The test_pv command has been updated so that if you don’t specify a new value for the PV it will run a loop that checks for value change events on the specified PV.  This provides a basic example of how to use all the PV functions.

 

The reg command has a new –d option for setting the deadband value for each PV.  I still have to add code to make changes persistent (i.e. make sure they are written to a file on flash, restored on bootup, and automatically backed up to and restored if needed from the server – all things I already do with the devdata file, which I may simply add these values to).

 

The test_poll command provides an example of how poll can be used.

 

 

NOTE:  The only event supported at this time is the value-change event (DBE_VALUE).  Adding other events would be fairly simple, but would increase memory usage and processing time, so I think we should leave these up to the record code running on the IOC (which is what everyone but the IOC will be using to communicate with the device).  I designed everything so it COULD easily be expanded if necessary or desired when we port this to another platform with more resources.

 

Mark

 

 

From: Davis, Mark
Sent: Wednesday, May 26, 2010 10:57 AM
To: Vuppala, Vasu
Subject: First portion of CA/PV interface to ModBus register definitions

 

Vasu,

 

I have the following functions defined and working so far:

 

   long pvGetPID(char *pvName);

 

   int pvGetInfo(long pid, PV_Info *pvInfo);

 

   int pvGetVal(long pid, uint toType, uint toSize, void *toVal);

 

   int pvSetVal(long pid, uint fromType, uint fromSize, void *fromVal);

 

These are defined in deamons/modbus.c.  There is a test command for them that also serves as a basic example of their use in usr/test_pv.c

 

These provide basic functionality as follows:

 

-          pvGetPID() returns a PID value given the name of a value (minus the hostname portion), or -1 if there is no such value.

 

-          pvGetInfo() fills in a structure which currently contains the data type, the size, and a bit-mapped flags value.  The types are defined in include/sys/modbus.h (DT_S16, DT_U16, DT_S32, DT_U32, DT_F32, DT_STR).  The size is in bytes.  And the flags currently contains bits for read and write access (PV_RD, PV_WR).  Returns 0 if successful, < 0 if an error.

 

-          pvGetVal() gets the current value of a PV in the requested format.  Returns 0 if successful, < 0 if an error.

 

-          pvSetVal() attempts to write a new value to a PV.  Returns 0 if successful, < 0 if an error.

 

For the data types you will need to map back and forth between the CA values and the PV_Info type values (and of course add on the time of day, etc, for the CA types that represent structures with more than just the PV’s value).

 

The functions are not quite complete yet:  For example they currently don’t attempt to check the range of values or check for an error returned by the function that actually updates a register value.  But they appear to work otherwise, and I think the interface provides all that is needed for just reading and writing so you can expand your tests to work on real values.  The next step will be to add functions to support the event processing, and then go back and add more error checking to the get and set functions.

 

One more note on your testing:  The processing of the analog inputs and the control logic to operate a supply (multiplied by 8 if all 8 supplies are on) takes a considerable chunk of CPU time.  The code that updates an analog input’s values is skipped if no change occurs, and when nothing is connected to the inputs (as is the case with the box you are testing on), they will drift to the bottom end of the range and not change.  Coupled with the fact that the supplies are not logically “on”, the box you are testing on has considerably more idle time than one that has active inputs and all the supplies turned on.  Comparing the one you are testing on to the other one in my office it looks like about a 15% difference in CPU usage.

 

Mark

 

 

 

 

 



__________ Information from ESET NOD32 Antivirus, version of virus signature database 5147 (20100526) __________

The message was checked by ESET NOD32 Antivirus.

http://www.eset.com



__________ Information from ESET NOD32 Antivirus, version of virus signature database 5153 (20100528) __________

The message was checked by ESET NOD32 Antivirus.

http://www.eset.com