Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not possible to distinguish between scalars and arrays of length 1 #69

Open
simon-ess opened this issue Jun 3, 2024 · 3 comments
Open
Assignees

Comments

@simon-ess
Copy link
Contributor

Describe the bug
It is not possible at the moment to distinguish between the following records via PVAccess:

record(waveform, "one") {
    field(FTVL, "STRING")
    field(NELM, "1")
    field(INP, {const: "foo"})
}

record(stringin, "onestring") {
    field(INP, {const: "bar"})
}

This ultimately manifests in the fact that fetching and processing data from a record that is a waveform of strings typically requires some additional checks, since you get something like

$ pvget one
one 2024-06-03 14:23:52.988  foo

whereas if the array has NELM > 1 then you instead get

$ pvget two
one 2024-06-03 14:23:52.988  [foo, bar]

which is typically handled differently in e.g. pyepics, p4p, etc.

Note that this is not really restricted to PVXS, or even PVAccessCPP, but also ChannelAccess. In channel access I believe that due to the protocol there is no possible fix; in PVAccess, it should be fixable since an array of length 1 of type string is sent as an NTScalar, whereas an array of length > 1 is sent as an NTScalarArray; it should be possible to send an array of length one also as an NTScalarArray, which should fix this.

To Reproduce
Steps to reproduce the behavior:

  1. Start an IOC with the db file above
  2. get the values
  3. be sad

Expected behavior
This is a bit murky, because I might be proposing a breaking change in how IOCs communicate over PVAccess. I think it would be more consistent to have arrays always send as NTScalarArrays instead of NTScalars if they are length 1. But there might be a good reason for the current behaviour.

Information (please complete the following):

$ pvxs/bin/darwin-x86/pvxinfo -D
Host: darwin-x86
Target: darwin-x86 Darwin clang
Toolchain
    __cplusplus = 201103
    clang 14.0.3 (clang-1403.0.22.14.1)
    GCC 4.2.1
    _LIBCPP_VERSION 160006
Versions
    PVXS 1.2.3 (1.3.1-5-g57f79ce747f062a32eed-dirty)
    EPICS 7.0.8.1-DEV
    libevent 2.2.0-alpha-dev
Runtime
    uname() -> Darwin CIN-716194 22.6.0 Darwin Kernel Version 22.6.0: Mon Apr 22 20:54:28 PDT 2024; root:xnu-8796.141.3.705.2~1/RELEASE_X86_64 x86_64
    epicsThreadGetCPUs() -> 16
    osiLocalAddr() -> 172.18.21.40
    osiSockDiscoverBroadcastAddresses() ->
        172.18.23.255
        172.18.23.255
        192.168.205.255
Effective Client config from environment
    EPICS_PVA_ADDR_LIST=172.18.23.255 192.168.205.255
    EPICS_PVA_AUTO_ADDR_LIST=NO
    EPICS_PVA_BROADCAST_PORT=5076
    EPICS_PVA_CONN_TMO=30
    EPICS_PVA_INTF_ADDR_LIST=0.0.0.0
    EPICS_PVA_NAME_SERVERS=
    EPICS_PVA_SERVER_PORT=5075
Effective Server config from environment
    EPICS_PVAS_AUTO_BEACON_ADDR_LIST=NO
    EPICS_PVAS_BEACON_ADDR_LIST=172.18.23.255 192.168.205.255
    EPICS_PVAS_BROADCAST_PORT=5076
    EPICS_PVAS_IGNORE_ADDR_LIST=
    EPICS_PVAS_INTF_ADDR_LIST=0.0.0.0
    EPICS_PVAS_SERVER_PORT=5075

Additional context
Add any other context about the problem here.

@simon-ess simon-ess changed the title Not possible to distinguish between strings and arrays of strings of length 1 Not possible to distinguish between scalars and arrays of length 1 Jun 3, 2024
@anjohnson
Copy link

The absence of a difference is baked a long way into the IOC code, deliberately so I believe (Bob D. might be able to explain that).

This isn't the only case where EPICS requires a client to know something about the PV it's connecting to. That also happens with an array of CHARs, is it a string or a series of 8-bit integers? The client has to know which it's meant to be to properly display the value.

@simon-ess
Copy link
Contributor Author

While I was digging around I could see that CA probably fundamentally cannot distinguish between these two types, but I do think it should be possible to do so for PVA; given that we send the data types over the wire, we should be able to say that "this is an array, but its length is 1".

It's worth noting that one of the places that this decision is made at the moment is here; it's a very simply check that only looks at the length of the data and not the type of the data. This could be improved.

(This gives me flashbacks of coding in APL; it is actually a bit non-trivial to have arrays of length 1, although it is possible)

We can close this issue if this is truly how things should behave. But I think it is worth considering an improvement here.

@mdavidsaver
Copy link
Owner

The absence of a difference is baked a long way into the IOC code ...

Concretely, this difference is expressed to QSRV through struct dbChannel having an element type and maximum number of elements, but no minimum element count. So testing that maximum is, I think, the best which can be done generically with the present dbAccess API. (without hard coding knowledge about certain fields of certain record types)

bool isArray = dbChannelFinalElements(chan)!=1;

Of course, this still leaves cases where a "scalar" actually turns out to have zero elements. At which point things get messy...

pvxs/ioc/iocsource.cpp

Lines 83 to 85 in 57f79ce

} else if(nReq==0) {
// this was an actual max length 1 array, which has zero elements now.
memset(&buf, 0, sizeof(buf));

... we should be able to say that "this is an array, but its length is 1".

I have had requests to allow modifications of field type mapping between PDB and PVA in places where there is no clear one-to-one relation. eg. coerce numeric to boolean. One ~obvious, though open ended, way to express this would be through info() tags.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants