DiamondLightSource/pythonSoftIOC

Waveform of strings no longer works

Closed this issue · 3 comments

This:

labels = builder.WaveformOut(
    "ENUM:LABELS", initial_value=[b"ZERO", b"ONE", b"MANY"]
)

used to give a waveform with FTVL of STRING under pythonSoftIOC 3.2, but now it gives:

Traceback (most recent call last):
  File "/dls/science/users/tmc43/common/python/PandABlocks-client/pandablocks/ioc/ioc.py", line 1490, in create_record
    self, record_name, field_info, str_vals
  File "/dls/science/users/tmc43/common/python/PandABlocks-client/pandablocks/ioc/ioc.py", line 943, in _make_bit_mux
    initial_value=[b"ZERO", b"ONE", b"MANY"]
  File "/scratch/tmc43/pipenv/PandABlocks-client-0xhNhEPE/lib/python3.7/site-packages/softioc/builder.py", line 226, in WaveformOut
    _waveform(value, fields)
  File "/scratch/tmc43/pipenv/PandABlocks-client-0xhNhEPE/lib/python3.7/site-packages/softioc/builder.py", line 215, in _waveform
    fields['FTVL'] = NumpyDtypeToDbf[datatype.name]
KeyError: 'bytes32'

Interesting regression. Do you feel like bisecting it? Unfortunately 3.2 is quite a long way back, it may be simpler to reimplement the functionality!

I didn't realise that arrays of strings actually worked.

I'm investigating it now. There has been a large amount of changes in this area unfortunately. I'll see what I can do.

I've found most of the missing pieces from the 3.2 -> 4.* change that broke this:

The mapping between types changed rather a lot. The old NumpyCharCodeToDbr mapping was replaced by NumpyDtypeToDbf (and other bits). Adding this mapping: 'bytes32': 'STRING' means we can initialize a Waveform of the given form without error, but caget'ing it doesn't work right.

The second major change is WaveformBase._write_value. Previously there was a call to numpy.require() using a saved dtype (inferred earlier in the initialisation process) that was set to "S40" i.e. an EPICS string. That conversion step just doesn't exist anymore. Hacking it by just adding this line at the beginning of the method: value = numpy.require(value, dtype=numpy.dtype('S40')) makes everything almost work:

>>> caget("AAA:AAA")
ca_array(['ZERO', '', ''], dtype='<U4')

I haven't yet worked out why elements 1 and 2 are now empty. And I'm not sure what the proper fix of the _write_value method is, but it's nearly there.