epics-base / jca

Java Channel Access client API
https://www.javadoc.io/doc/org.epics/jca/latest/index.html
Other
8 stars 14 forks source link

How can I get the timestamp when generating the pv value? #50

Closed HongYi-Shern closed 2 years ago

slominskir commented 3 years ago

Short answer: you have to write a ton of code.

Long answer: You must set the type explicitly in order to obtain TIME DBR types from JCA (the default is base types). For example, if using a monitor and the base type is unknown you'll need to create a lookup method to map base types to TIME types. Like so:

CAJChannel channel = (CAJChannel)context.createChannel("channel1");
context.pendIO(2.0);
DBRType type = getTimeTypeFromFieldType(channel.getFieldType());
int mask = Monitor.VALUE | Monitor.ALARM;
CAJMonitor monitor = (CAJMonitor) channel.addMonitor(type, channel.getElementCount(), mask);

where lookup method (probably should be part of JCA lib) is something like:

    private DBRType getTimeTypeFromFieldType(DBRType fieldType) {
        DBRType time = null;

        switch(fieldType.getName()) {
            case "DBR_DOUBLE":
                time = DBRType.TIME_DOUBLE;
                break;
            case "DBR_FLOAT":
                time = DBRType.TIME_FLOAT;
                break;
            case "DBR_INT":
                time = DBRType.TIME_INT;
                break;
            case "DBR_SHORT":
                time = DBRType.TIME_SHORT;
                break;
            case "DBR_ENUM":
                time = DBRType.TIME_ENUM;
                break;
            case "DBR_BYTE":
                time = DBRType.TIME_BYTE;
                break;
            case "DBR_STRING":
                time = DBRType.TIME_STRING;
                break;
        }

        return time;
    }

Now you'll have to do some casting and type checking on monitor update. Probably need another method that should be built-in:

   private List eventToObjectList(MonitorEvent event) {
        DBR dbr = event.getDBR();

        List<? extends Object> list;

        TIME time = null;

        try {
            if(!dbr.isTIME()) {
                throw new RuntimeException("Should be monitoring time types, but found non-time type!");
            }

            if (dbr.isDOUBLE()) {
                time = (DBR_TIME_Double)dbr;
                double[] value = ((gov.aps.jca.dbr.DOUBLE) dbr).getDoubleValue();
                list = DoubleStream.of(value).boxed().collect(Collectors.toList());
            } else if (dbr.isFLOAT()) {
                time = (DBR_TIME_Float)dbr;
                float[] value = ((gov.aps.jca.dbr.FLOAT) dbr).getFloatValue();
                list = toFloatList(value);
            } else if (dbr.isINT()) {
                time = (DBR_TIME_Int)dbr;
                int[] value = ((gov.aps.jca.dbr.INT) dbr).getIntValue();
                list = IntStream.of(value).boxed().collect(Collectors.toList());
            } else if (dbr.isSHORT()) {
                time = (DBR_TIME_Short)dbr;
                short[] value = ((gov.aps.jca.dbr.SHORT) dbr).getShortValue();
                list = toShortList(value);
            } else if (dbr.isENUM()) {
                time = (DBR_TIME_Enum)dbr;
                short[] value = ((gov.aps.jca.dbr.ENUM) dbr).getEnumValue();
                list = toShortList(value);
            } else if (dbr.isBYTE()) {
                time = (DBR_TIME_Byte)dbr;
                byte[] value = ((gov.aps.jca.dbr.BYTE) dbr).getByteValue();
                list = toByteList(value);
            } else {
                time = (DBR_TIME_String)dbr;
                String[] value = ((gov.aps.jca.dbr.STRING) dbr).getStringValue();
                list = Stream.of(value).collect(Collectors.toList());
            }
        } catch (Exception e) {
            System.err.println("Unable to create list from value: " + e);
            dbr.printInfo();
        }
    }

    private List<Float> toFloatList(float[] value) {
        List<Float> list = new ArrayList<>();

        if(value != null) {
            for (float v : value) {
                list.add(v);
            }
        }

        return list;
    }

    private List<Short> toShortList(short[] value) {
        List<Short> list = new ArrayList<>();

        if(value != null) {
            for (short v : value) {
                list.add(v);
            }
        }

        return list;
    }

    private List<Byte> toByteList(byte[] value) {
        List<Byte> list = new ArrayList<>();

        if(value != null) {
            for (byte v : value) {
                list.add(v);
            }
        }

        return list;
    }
anjohnson commented 3 years ago

I don't know if anything like these ever made into the Java API, but the C/C++ API for Channel Access has a series of macros (in db_access.h) for doing type checks and conversions like that:

/*----------------------------------------------------------------------------
* repository for some useful PV database constants and utilities
*
* type checking macros -- return non-zero if condition is true, zero otherwise
*
*      int dbf_type_is_valid(type)      type is a valid DBF_xxx
*      int dbr_type_is_valid(type)      type is a valid DBR_xxx
*      int dbr_type_is_plain(type)      type is a valid plain DBR_xxx
*      int dbr_type_is_STS(type)        type is a valid DBR_STS_xxx
*      int dbr_type_is_TIME(type)       type is a valid DBR_TIME_xxx
*      int dbr_type_is_GR(type)         type is a valid DBR_GR_xxx
*      int dbr_type_is_CTRL(type)       type is a valid DBR_CTRL_xxx
*      int dbr_type_is_STRING(type)     type is a valid DBR_STRING_xxx
*      int dbr_type_is_SHORT(type)      type is a valid DBR_SHORT_xxx
*      int dbr_type_is_FLOAT(type)      type is a valid DBR_FLOAT_xxx
*      int dbr_type_is_ENUM(type)       type is a valid DBR_ENUM_xxx
*      int dbr_type_is_CHAR(type)       type is a valid DBR_CHAR_xxx
*      int dbr_type_is_LONG(type)       type is a valid DBR_LONG_xxx
*      int dbr_type_is_DOUBLE(type)     type is a valid DBR_DOUBLE_xxx
*
* type conversion macros
*
*    char *dbf_type_to_text(type)       returns text matching DBF_xxx
*     void dbf_text_to_type(text, type) finds DBF_xxx matching text
*      int dbf_type_to_DBR(type)        returns DBR_xxx matching DBF_xxx
*      int dbf_type_to_DBR_TIME(type)   returns DBR_TIME_xxx matching DBF_xxx
*      int dbf_type_to_DBR_GR(type)     returns DBR_GR_xxx matching DBF_xxx
*      int dbf_type_to_DBR_CTRL(type)   returns DBR_CTRL_xxx matching DBF_xxx
*    char *dbr_type_to_text(type)       returns text matching DBR_xxx
*     void dbr_text_to_type(text, type) finds DBR_xxx matching text
*---------------------------------------------------------------------------*/

and for finding the value from the structure:

/*
 * ptr to value given a pointer to the structure and the DBR type
 */
#define dbr_value_ptr(PDBR, DBR_TYPE)
kasemir commented 3 years ago

Following up, Ryan's example above shows how to check the original data type and get the associated ..TIME.. type. When you then receive a monitor value, it shows how to extract the value, and you can fundamentally fetch the time like this:

if (dbr.isTIME())
{
    TimeStamp epics_time = ((TIME)dbr).getTimeStamp();
    // Convert to a more convenient Java time stamp,
    // translate from EPICS epoch of 1990
    Instant instant = Instant.ofEpochSecond(epics_time.secPastEpoch() + 631152000L,
                                            (int) epics_time.nsec());
    System.out.println("Time is " + instant);
}

You might want to make the code more robust by checking for epics_time == null and also for epics_time.secPastEpoch() == 0, which can happen when the record was never processed and doesn't have a valid time stamp.

Can we close this issue?