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

Standardise record value setting/getting #60

Merged
merged 138 commits into from Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from 134 commits
Commits
Show all changes
138 commits
Select commit Hold shift + click to select a range
6a9ea1e
Use _default_ in Out records
AlexanderWells-diamond Nov 18, 2021
c4ae72c
Rebasing branch from master
AlexanderWells-diamond Nov 19, 2021
75f5ecf
Fix records alarm status if no initial value
AlexanderWells-diamond Nov 19, 2021
cdbbc8a
Fix typo
AlexanderWells-diamond Nov 19, 2021
0c2238f
Add type information to records
AlexanderWells-diamond Nov 19, 2021
d64e715
String handling for waveforms. Remove value casting
AlexanderWells-diamond Nov 19, 2021
c1fb7dc
Waveform out string handling fixes
AlexanderWells-diamond Nov 19, 2021
3badfd3
Fix type returned from waveforms, and tests
AlexanderWells-diamond Nov 19, 2021
dcba54c
Add truncating strings before record init
AlexanderWells-diamond Nov 22, 2021
fadaacb
Handle byte strings in waveforms
AlexanderWells-diamond Nov 22, 2021
3e02993
Support byte strings in stringIn/Out records
AlexanderWells-diamond Nov 22, 2021
9a5b3b2
Python2-compatible string formatting
AlexanderWells-diamond Nov 22, 2021
a3af507
Add UTF-8 tests
AlexanderWells-diamond Nov 23, 2021
a6837e7
Support for more input types to int+float records
AlexanderWells-diamond Nov 23, 2021
b7883f1
Refactor to have easier Waveform value handling
AlexanderWells-diamond Nov 23, 2021
69683a3
Remove unnecessary TODO
AlexanderWells-diamond Nov 23, 2021
5fd3819
Python2 compatibility changes
AlexanderWells-diamond Nov 23, 2021
e094ba7
Attempt to handle Python2 strings properly
AlexanderWells-diamond Nov 23, 2021
b5b7586
Change name to remove pytest warning
AlexanderWells-diamond Nov 23, 2021
62ac99d
Additional Python2 UTF-8 handling
AlexanderWells-diamond Nov 23, 2021
237d590
Fix incorrect Python2 version test
AlexanderWells-diamond Nov 23, 2021
99334e1
Stop cothread on Win+another Python2 string fix
AlexanderWells-diamond Nov 23, 2021
c69272b
Revert "Stop cothread on Win+another Python2 string fix"
AlexanderWells-diamond Nov 23, 2021
2a1d5ee
Another Python2 string handling fix
AlexanderWells-diamond Nov 23, 2021
ed464d0
Delete unused fixture
AlexanderWells-diamond Nov 23, 2021
e471817
Python2 decoding fix. Skip IOC init on Windows.
AlexanderWells-diamond Nov 24, 2021
00d622d
Swap to using unicode strings in tests.
AlexanderWells-diamond Nov 24, 2021
9f32a36
Fix previous commit's unicode handling
AlexanderWells-diamond Nov 24, 2021
9656b87
Add tests for default value checking
AlexanderWells-diamond Dec 1, 2021
cfc1370
Remove unnecessary unicode specifiers
AlexanderWells-diamond Dec 1, 2021
2c51781
Cover additional cases
AlexanderWells-diamond Dec 1, 2021
7f8e57a
Remove TODO
AlexanderWells-diamond Dec 1, 2021
4e59eaa
Formatting fixes from code review
AlexanderWells-diamond Dec 1, 2021
db7b430
Change accepted typed to exclude ctypes and None
AlexanderWells-diamond Dec 1, 2021
7c417af
Add missing requires_cothread marker
AlexanderWells-diamond Dec 1, 2021
157b934
Fix Windows fail - move inner function to top level
AlexanderWells-diamond Dec 2, 2021
f54eaf3
Fix formatting issues
AlexanderWells-diamond Dec 2, 2021
05c5c7a
Remove unnecessary exception, and fix formatting
AlexanderWells-diamond Dec 2, 2021
da2e38b
Remove another unnecessary exception
AlexanderWells-diamond Dec 6, 2021
ddf87bd
Skip tests putting None into String records.
AlexanderWells-diamond Dec 6, 2021
c5b14fb
Add tests for inf, -inf, and nan values
AlexanderWells-diamond Dec 9, 2021
1079739
Refactor test_records into test classes
AlexanderWells-diamond Dec 9, 2021
1688283
First CAGet tests working
AlexanderWells-diamond Dec 9, 2021
cea2448
Add more CAGet tests
AlexanderWells-diamond Dec 9, 2021
c57e1f1
Allow record_value_asserts to run on Windows
AlexanderWells-diamond Dec 9, 2021
7871e2a
Use neater type conversion after caget
AlexanderWells-diamond Dec 9, 2021
f3b36cf
Increase timeout to alleviate test failures
AlexanderWells-diamond Dec 10, 2021
0604d88
Add Yield call to ensure IOC processes new value
AlexanderWells-diamond Dec 10, 2021
1ca6334
Bump timeout further still for MacOS CI runners
AlexanderWells-diamond Dec 10, 2021
6b45466
Use up to date cibuildwheel
AlexanderWells-diamond Dec 10, 2021
c36a50d
Decode CAPut'd strings in the same way .set() does
AlexanderWells-diamond Dec 10, 2021
20cf010
Add CAPut - get() test cases
AlexanderWells-diamond Dec 10, 2021
0bcd86f
Add CAGet tests to the Default tests
AlexanderWells-diamond Dec 10, 2021
2389c6a
Skip building musllinux
AlexanderWells-diamond Dec 10, 2021
dd88095
Another attempt to stop musllinux builds
AlexanderWells-diamond Dec 10, 2021
2e9f37c
Another attempt to stop musllinux builds
AlexanderWells-diamond Dec 10, 2021
95d6d83
Improve synchronisation for parent-child messages
AlexanderWells-diamond Dec 13, 2021
ceae66c
Disable tests - investigating Windows build issues
AlexanderWells-diamond Dec 13, 2021
39e2e18
Revert cibuildwheel version change
AlexanderWells-diamond Dec 13, 2021
0998b87
Revert "Revert cibuildwheel version change"
AlexanderWells-diamond Dec 13, 2021
26e0dd0
Use cibuildwheel action
AlexanderWells-diamond Dec 13, 2021
2036b61
Install "build" package
AlexanderWells-diamond Dec 13, 2021
1ad31a2
Fix path for archive upload
AlexanderWells-diamond Dec 13, 2021
c6e9257
Revert changes to this file as action didn't work
AlexanderWells-diamond Dec 13, 2021
9330dd4
Explain contents of CIBW_SKIP env var
AlexanderWells-diamond Dec 13, 2021
112a2bf
Add directory contents printing to cibuildwheel
AlexanderWells-diamond Dec 13, 2021
5d9ece7
Try storing output in dist folder
AlexanderWells-diamond Dec 13, 2021
741f70e
Change ls command on linux
AlexanderWells-diamond Dec 13, 2021
c1d3f8c
Increase verbosity and tweak BEFORE action
AlexanderWells-diamond Dec 13, 2021
946aa31
Print tree output early
AlexanderWells-diamond Dec 13, 2021
66c1348
Back to cibuildwheel V1 to examine tree output
AlexanderWells-diamond Dec 13, 2021
c81471e
Tweak debugging instructions for windows
AlexanderWells-diamond Dec 14, 2021
494e93d
Roll back cibuildwheel version to 2.2.2
AlexanderWells-diamond Dec 14, 2021
03a626b
Remove logging and reinstate running tests
AlexanderWells-diamond Dec 14, 2021
1a2f0a3
Pin cibuildwheel to 2.2.2
AlexanderWells-diamond Dec 14, 2021
954668b
Change to only run CI once on pull requests
AlexanderWells-diamond Dec 14, 2021
9618484
Add Unit Test Results action to CI
AlexanderWells-diamond Dec 14, 2021
5881882
Use different approach to saving JUnit results
AlexanderWells-diamond Dec 14, 2021
fa88521
Stop docs CI from running multiple times
AlexanderWells-diamond Dec 14, 2021
9a66d8d
Tweak results XML upload location
AlexanderWells-diamond Dec 14, 2021
6d56f3a
Fix invalid artifact name
AlexanderWells-diamond Dec 14, 2021
df3d724
Bump cibuildwheel version and add pip option
AlexanderWells-diamond Dec 15, 2021
2956acb
Fix merge issues
AlexanderWells-diamond Dec 15, 2021
dfc8ea9
Refactor tests to massively reduce subprocesses
AlexanderWells-diamond Dec 15, 2021
8d61dfd
Apply Black formatting
AlexanderWells-diamond Dec 15, 2021
48c440d
Specify old pip behaviour in yaml file
AlexanderWells-diamond Dec 15, 2021
7714053
Go back to pinning cibuildwheel 2.2.2
AlexanderWells-diamond Dec 15, 2021
f9a8af1
Attempt to use cibuildwheel 2.3.1 again
AlexanderWells-diamond Dec 15, 2021
51b37d9
Add comment explaining env var
AlexanderWells-diamond Dec 15, 2021
7f54987
Initial rewrite of core device value handling
Araneidae Jan 4, 2022
f5817cd
Fix missing waveform default value
Araneidae Jan 4, 2022
8ecdd88
Update version of manylinux image
AlexanderWells-diamond Jan 4, 2022
ed1b193
Add special support for long string records
Araneidae Jan 4, 2022
105d2d3
Make usage of quotation marks consistent
Araneidae Jan 5, 2022
0df634e
Fixes to string handling
Araneidae Jan 5, 2022
ed3a2c5
Revisit long string initialisation
Araneidae Jan 5, 2022
0d7b372
Merge remote-tracking branch 'github/sort_setting_values' into merge-…
Araneidae Jan 5, 2022
2b76593
Add tests for Issue #43
AlexanderWells-diamond Jan 5, 2022
83bacad
Add tests for Action, longStringIn, longStringOut
AlexanderWells-diamond Jan 5, 2022
9b26f91
Add very long string tests
AlexanderWells-diamond Jan 5, 2022
9fccffd
Refactor the Validate tests
AlexanderWells-diamond Jan 6, 2022
9f57ed1
Improve error message during assertion failure
AlexanderWells-diamond Jan 6, 2022
63d26d9
Remove tests that are now invalid
AlexanderWells-diamond Jan 10, 2022
c8dfbd0
Remove invalid tests and apply test fixes
AlexanderWells-diamond Jan 11, 2022
bdab3d8
Revisit waveform initialisation
Araneidae Jan 11, 2022
6bd9407
Merge remote-tracking branch 'github/merge-tests' into merge-tests
Araneidae Jan 11, 2022
f719013
Add special support for writing bytes to waveforms
Araneidae Jan 11, 2022
0feae58
Update tests after latest code changes
AlexanderWells-diamond Jan 11, 2022
fe6b436
Fix more tests
AlexanderWells-diamond Jan 12, 2022
71361af
Fix behaviour of early .get() on uninitialised Out records
Araneidae Jan 12, 2022
a990af5
Ensure waveform record and data types match properly
Araneidae Jan 12, 2022
3cdbfdd
Merge remote-tracking branch 'github/merge-tests' into merge-tests
Araneidae Jan 12, 2022
b748856
Add second case where we have to reduce int64 to int32
Araneidae Jan 12, 2022
f8b67f8
Fix more tests
AlexanderWells-diamond Jan 12, 2022
b4f7995
Merge branch 'merge-tests' of github.com:dls-controls/pythonIoc into …
AlexanderWells-diamond Jan 12, 2022
2c93f39
Fix bug in waveform change comparison
Araneidae Jan 12, 2022
62ac061
Merge branch 'merge-tests' of github.com:dls-controls/pythonIoc into …
AlexanderWells-diamond Jan 12, 2022
049da54
More test fixes
AlexanderWells-diamond Jan 12, 2022
f805f02
Skip caget value checks when waveform is length 1
AlexanderWells-diamond Jan 12, 2022
40cb943
Use array_equal to compare numpy arrays
Araneidae Jan 13, 2022
61a8a47
Ensure we don't create zero length waveforms
Araneidae Jan 13, 2022
42332fb
Fix package dependencies
AlexanderWells-diamond Jan 13, 2022
b544c9e
Revert "Fix package dependencies"
AlexanderWells-diamond Jan 13, 2022
7ce0246
Pin epicscorelibs version to fix dependency errors
AlexanderWells-diamond Jan 13, 2022
d7f02f3
Revert "Pin epicscorelibs version to fix dependency errors"
AlexanderWells-diamond Jan 13, 2022
21d510e
Pin epicscorelibs version to fix dependency errors
AlexanderWells-diamond Jan 13, 2022
31bdb2b
Add docs for longStringIn/Out
AlexanderWells-diamond Jan 13, 2022
82f91f0
Clean up misleading docs
AlexanderWells-diamond Jan 13, 2022
7f6ccd3
Add changelog entry for PR#60
AlexanderWells-diamond Jan 13, 2022
b5e66eb
Now also block writing over-long strings to longString records
Araneidae Jan 13, 2022
a7dc10b
Merge remote-tracking branch 'github/sort_setting_values' into sort_s…
Araneidae Jan 13, 2022
8d30a64
Merge branch 'sort_setting_values' of github.com:dls-controls/pythonI…
AlexanderWells-diamond Jan 13, 2022
4cbdb94
Refactor tests to split value tests into own file
AlexanderWells-diamond Jan 13, 2022
98de8f6
Add tests for issue #55
AlexanderWells-diamond Jan 13, 2022
dd99311
Add coverage to Multiprocess tests
AlexanderWells-diamond Jan 25, 2022
adaa286
Restore original epicscorelibs dependency
AlexanderWells-diamond Jan 25, 2022
e12572f
Update lock file with epicscorelibs changes
AlexanderWells-diamond Jan 25, 2022
0dd1544
Add procedure to update all dependencies
AlexanderWells-diamond Jan 25, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 46 additions & 7 deletions .github/workflows/code.yml
Expand Up @@ -2,7 +2,15 @@ name: Code CI

on:
push:
branches:
- master
- main
tags:
- "*"
pull_request:
schedule:
# Run every Monday at 8am
- cron: '0 8 * * MON'

jobs:
lint:
Expand Down Expand Up @@ -30,15 +38,18 @@ jobs:
python: [cp36, cp37, cp38, cp39]

include:
# Put coverage in the project directory for mac
# Put coverage and results files in the project directory for mac
- os: macos-latest
cov_file: "{project}/dist/coverage.xml"
results_file: "{project}/dist/pytest-results.xml"
# And for windows
- os: windows-latest
cov_file: "{project}/dist/coverage.xml"
# But put coverage in the output dir mounted in docker for linux
results_file: "{project}/dist/pytest-results.xml"
# But put coverage and results files in the output dir mounted in docker for linux
- os: ubuntu-latest
cov_file: /output/coverage.xml
results_file: /output/pytest-results.xml
# Build an sdist on linux so it has the right line endings
- os: ubuntu-latest
sdist: true
Expand All @@ -60,7 +71,8 @@ jobs:
python-version: "3.7"

- name: Install Python Dependencies
run: pip install build cibuildwheel==1.* # The 2.* releases dropped Python2 support.
# Pin cibuildwheel due to https://github.com/pypa/cibuildwheel/issues/962
run: pip install build cibuildwheel>=2.3.1

- name: Build Sdist
if: matrix.sdist
Expand All @@ -69,14 +81,17 @@ jobs:
- name: Build Wheel
run: cibuildwheel --output-dir dist
env:
# Force old behaviour of pip - see https://github.com/pypa/cibuildwheel/issues/962
CIBW_ENVIRONMENT: PIP_USE_DEPRECATED=out-of-tree-build
CIBW_BUILD: ${{ matrix.python }}*64
CIBW_TEST_EXTRAS: dev
CIBW_TEST_COMMAND: pytest {project}/tests --cov-report xml:${{ matrix.cov_file }}
CIBW_TEST_COMMAND: pytest {project}/tests --cov-report xml:${{ matrix.cov_file }} --junit-xml=${{ matrix.results_file }}
# Disable auditwheel as it isn't compatible with setuptools_dso approach
# https://github.com/mdavidsaver/setuptools_dso/issues/17
CIBW_REPAIR_WHEEL_COMMAND: ""
CIBW_MANYLINUX_X86_64_IMAGE: manylinux1
CIBW_ENVIRONMENT_LINUX: SETUPTOOLS_DSO_PLAT_NAME=manylinux1_x86_64
CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
CIBW_ENVIRONMENT_LINUX: SETUPTOOLS_DSO_PLAT_NAME=manylinux2014_x86_64
CIBW_SKIP: "*-musllinux*" # epicscorelibs doesn't build on musllinux platforms

- name: Upload Wheel and Sdist
uses: actions/upload-artifact@v2
Expand All @@ -90,6 +105,30 @@ jobs:
name: ${{ matrix.os }}/${{ matrix.python }}
directory: dist

- name: Upload Unit Test Results
if: always()
uses: actions/upload-artifact@v2
with:
name: Unit Test Results (${{ matrix.os }}-${{ matrix.python }})
path: dist/pytest-results.xml

publish-test-results:
name: Publish Unit Tests Results
needs: build
runs-on: ubuntu-latest
if: always()

steps:
- name: Download Artifacts
uses: actions/download-artifact@v2
with:
path: artifacts

- name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v1
with:
files: artifacts/**/*.xml

release:
needs: [build]
runs-on: ubuntu-latest
Expand All @@ -115,4 +154,4 @@ jobs:
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.pypi_token }}
run: pipx run twine upload dist/*
run: pipx run twine upload dist/*
6 changes: 6 additions & 0 deletions .github/workflows/docs.yml
Expand Up @@ -2,6 +2,12 @@ name: Docs CI

on:
push:
branches:
# Add more branches here to publish docs from other branches
- master
- main
tags:
- "*"
pull_request:

jobs:
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -13,6 +13,10 @@ Changed:

- `Remove python2 support <../../pull/64>`
- `Default DISP to TRUE for all In records <../../pull/74>`
- `Overhaul record value setting/getting <../../pull/60>`

Added:
- New longStringIn/longStringOut record types

3.2.1_ - 2021-11-25
-------------------
Expand Down
26 changes: 20 additions & 6 deletions docs/reference/api.rst
Expand Up @@ -267,7 +267,7 @@ All functions return a wrapped `ProcessDeviceSupportIn` or
mbbOut(name, *options, **fields)

Create ``mbbi`` and ``mbbo`` records. Up to 16 options can be specified as
either an option name or a tuple of tw fields. The name or first field of
either an option name or a tuple of two fields. The name or first field of
the tuple names the option, and the second optional field is the option
severity. For example::

Expand Down Expand Up @@ -308,13 +308,28 @@ All functions return a wrapped `ProcessDeviceSupportIn` or
if given, or defaults to ``'FLOAT'``.


The following function generates a specialised record.
The following functions generates specialised records.

.. function:: Action(name, **fields)

Creates a record (using `boolOut`) which will always call the `on_update`
method when processed. Used for action records. The `on_update` keyword
should always be passed.
method when processed. Used for action records. Either the `on_update` or
`on_update_name` keyword should always be passed.

.. function::
longStringIn(name, **fields)
longStringOut(name, **fields)

Creates a Waveform record specialised to contain Python (Unicode) strings.
Only accepts string values, and only returns string values. `initial_value`
and ``length`` keywords accepted as per Waveform records.

If no ``length`` or `initial_value` is provided a default length of 256 is used.

.. note::
If you wish to store byte strings, use a ``WaveformIn/Out`` record




The following functions manage record names. The record device name must be
Expand All @@ -328,8 +343,7 @@ record creation function.
Sets up the prefix part of the record name, referred to here as the "device"
part. This function must be called before creating any records.
Note that
only this function need be used, the three other functions below are
entirely optional.
only this function need be used, function below is entirely optional.

.. function:: UnsetDevice()

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
@@ -1,3 +1,3 @@
[build-system]
requires = ["setuptools", "wheel", "setuptools_dso>=2.1", "epicscorelibs>=7.0.6.99.1.0"]
requires = ["setuptools", "wheel", "setuptools_dso>=2.1", "epicscorelibs==7.0.6.99.1.0"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This strong tie to a very particular version of epicscorelibs rings an alarm bell for me. What's going on here? Surely we don't want this, do we?

Separately, the version number is in an alarmingly non-standard format!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This strong tie to a very particular version of epicscorelibs rings an alarm bell for me. What's going on here?

There is an ordering issue here. The build process is this:

  • deps from pyproject.toml are installed
  • python setup.py bdist_wheel is called to make a binary wheel, which is compiled against the exact version of epicscorelibs that has been installed, and records this fact in the wheel
  • the wheel is uploaded to PyPI
  • anyone using this version of the binary wheel must have this exact version of epicscorelibs installed too (which pip will do automatically)
  • anyone using the sdist is free to have whatever version of epicscorelibs they want

The >= that was there originally was correct, which would mean that pythonSoftIOC was compiled against the latest available epicscorelibs. Unfortunately in the tests we require p4p, which is also compiled against a particular version of epicscorelibs. If a new version of epicscorelibs is released, but p4p is not, then pip will fail to install dependencies. This is why we are using ==. Without ABI compliance for EPICS, I can't see a way around this, unless a release of p4p is made with every epicscorelibs release. Fortunately we don't care for aioca as we only need API compliance, not ABI compliance.

@mdavidsaver can you think of a way we can resolve this?

Separately, the version number is in an alarmingly non-standard format!

The first part is the EPICS release, but not sure about the other bits. Maybe @mdavidsaver could comment on this too?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a new version of epicscorelibs is released, but p4p is not

Sorry. I've been sloppy about this as I'm in the (so far long) process of introducing another dependency pvxslibs. I'll try to straighten this out. Though even if I do, there will always be some time when pypy.org has newer versions of parts of the dependency chain. Maybe only minutes, but never zero.

we only need API compliance, not ABI compliance.

Speaking strictly, API is important at build time (with a compiler), and ABI afterwards. So in an ideal world, pyproject.toml is expressing the build time dependency (BuildRequires in an RPM .spec file), while install_requires in setup.py expresses the runtime dependency (plain Requires in a .spec). As with a .spec runtime dependencies are partly generated. so...

pythonSoftIOC/setup.py

Lines 108 to 109 in 3866562

# Dependency version declared in pyproject.toml
epicscorelibs.version.abi_requires(),

So if epicscorelibs 7.0.6.99.1.0 is installed, this will expand to:

epicscorelibs >=7.0.6.99.1.0, <7.0.6.99.2

Separately, the version number is in an alarmingly non-standard format!

There are three pieces of information being encoded: upstream EPICS Base version (everything up to and including the .99), epicscorelibs ABI, and epicscorelibs revision. So the version range being emitted above allows for later revisions which (hopefully) don't include any ABI change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mdavidsaver can you think of a way we can resolve this?

If the problem is only with GHA, you might be able to pass --no-binary p4p to pip when installing the dev dependencies prior to running tests.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @mdavidsaver it looks like p4p 3.5.5 fixes the issue - I've restored the original epicscorelibs dependency in this commit and it seems to be working: adaa286

Spoke too soon, the Code CI works but the Docs CI breaks with this change... Investigating...

build-backend = "setuptools.build_meta:__legacy__"
6 changes: 3 additions & 3 deletions softioc/__init__.py
Expand Up @@ -18,10 +18,10 @@
# Need to do this before calling anything in device.py
iocshRegisterCommon()
for dbd in ('base.dbd', 'PVAServerRegister.dbd', 'qsrv.dbd'):
dbLoadDatabase(dbd, os.path.join(path.base_path, "dbd"), None)
dbLoadDatabase("devIocStats.dbd", os.path.dirname(__file__), None)
dbLoadDatabase(dbd, os.path.join(path.base_path, 'dbd'), None)
dbLoadDatabase('devIocStats.dbd', os.path.dirname(__file__), None)

if registerRecordDeviceDriver(pdbbase):
raise RuntimeError('Error registering')

__all__ = ["__version__"]
__all__ = ['__version__']
133 changes: 89 additions & 44 deletions softioc/builder.py
Expand Up @@ -7,7 +7,8 @@
InitialiseDbd()
LoadDbdFile(os.path.join(os.path.dirname(__file__), 'device.dbd'))

from . import pythonSoftIoc # noqa
from . import device, pythonSoftIoc # noqa

PythonDevice = pythonSoftIoc.PythonDevice()


Expand Down Expand Up @@ -64,7 +65,7 @@ def longOut(name, DRVL=None, DRVH=None, EGU=None, **fields):
'EI', 'NI', 'TE', 'EL', 'TV', 'TT', 'FT', 'FF'] # 8-15

# All the severity strings supported by <prefix>SV
_severityStrings = ["NO_ALARM", "MINOR", "MAJOR", "INVALID"]
_severityStrings = ['NO_ALARM', 'MINOR', 'MAJOR', 'INVALID']

# Converts a list of (option [,severity]) values or tuples into field settings
# suitable for mbbi and mbbo records.
Expand All @@ -78,7 +79,7 @@ def process_value(prefix, value, option, severity=None):
severity = _severityStrings[severity]
fields[prefix + 'SV'] = severity
# zip() silently ignores extra values in options, so explicitly check length
assert len(options) <= 16, "May not specify more than 16 enum values"
assert len(options) <= 16, 'May not specify more than 16 enum values'
for prefix, (value, option) in zip(_mbbPrefixes, enumerate(options)):
if isinstance(option, tuple):
# The option is tuple consisting of the option name and an optional
Expand Down Expand Up @@ -108,31 +109,28 @@ def Action(name, **fields):
return boolOut(name, always_update = True, **fields)


# Converts numpy character code to FTVL value.
NumpyCharCodeToFtvl = {
# The following type codes are supported directly:
'B': 'UCHAR', # ubyte
'b': 'CHAR', # byte
'H': 'USHORT', # ushort
'h': 'SHORT', # short
'i': 'LONG', # intc
'L': 'ULONG', # uint
'l': 'LONG', # int_
'f': 'FLOAT', # single
'd': 'DOUBLE', # float_
'S': 'STRING', # str_

# The following type codes are weakly supported by pretending that
# they're related types.
'?': 'CHAR', # bool_
'p': 'LONG', # intp
'I': 'ULONG', # uintc
'P': 'ULONG', # uintp

# The following type codes are not supported at all:
# q longlong Q ulonglong g longfloat
# F csingle D complex_ G clongfloat
# O object_ U unicode_ V void
# Converts numpy dtype name to FTVL value.
NumpyDtypeToDbf = {
'int8': 'CHAR',
'uint8': 'UCHAR',
'int16': 'SHORT',
'uint16': 'USHORT',
'int32': 'LONG',
'uint32': 'ULONG',
'float32': 'FLOAT',
'float64': 'DOUBLE',
}

# Coverts FTVL string to numpy type
DbfStringToNumpy = {
'CHAR': 'int8',
'UCHAR': 'uint8',
'SHORT': 'int16',
'USHORT': 'uint16',
'LONG': 'int32',
'ULONG': 'uint32',
'FLOAT': 'float32',
'DOUBLE': 'float64',
}


Expand All @@ -146,29 +144,49 @@ def _waveform(value, fields):
assert not value, 'Can\'t specify initial value twice!'
value = (fields.pop('initial_value'),)

# Datatype can be specified as keyword argument, taken from FTVL, or derived
# from the initial value
if 'datatype' in fields:
assert 'FTVL' not in fields, \
'Can\'t specify FTVL and datatype together'
datatype = fields.pop('datatype')
if datatype == int or datatype == 'int':
# Convert Python int to 32-bit integer, it's all we can handle
datatype = numpy.dtype('int32')
else:
datatype = numpy.dtype(datatype)
elif 'FTVL' in fields:
datatype = numpy.dtype(DbfStringToNumpy[fields['FTVL']])
else:
# No datatype specified, will have to infer from initial value
datatype = None

if value:
# If a value is specified it should be the *only* non keyword
# argument.
# If a value is specified it should be the *only* non keyword argument.
value, = value
value = numpy.array(value)
fields['initial_value'] = value

# Pick up default length and datatype from initial value
length = len(value)
FTVL = NumpyCharCodeToFtvl[value.dtype.char]
initial_value = device._require_waveform(value, datatype)
length = fields.pop('length', len(initial_value))

# Special case for [u]int64: if the initial value comes in as 64 bit
# integers we cannot represent that, so recast it as [u]int32
if datatype is None:
if initial_value.dtype == numpy.int64:
initial_value = numpy.require(initial_value, numpy.int32)
elif initial_value.dtype == numpy.uint64:
initial_value = numpy.require(initial_value, numpy.uint32)
else:
# No value specified, so require length and datatype to be specified.
initial_value = numpy.array([], dtype = datatype)
length = fields.pop('length')
FTVL = 'FLOAT'
datatype = initial_value.dtype

datatype = fields.pop('datatype', None)
if datatype is not None:
assert 'FTVL' not in fields, \
'Can\'t specify FTVL and datatype together'
FTVL = NumpyCharCodeToFtvl[numpy.dtype(datatype).char]
assert length > 0, 'Array cannot be of zero length'

fields['initial_value'] = initial_value
fields['_wf_nelm'] = length
fields['_wf_dtype'] = datatype

fields['NELM'] = length
fields.setdefault('FTVL', FTVL)
fields['FTVL'] = NumpyDtypeToDbf[datatype.name]


def Waveform(name, *value, **fields):
Expand All @@ -182,6 +200,32 @@ def WaveformOut(name, *value, **fields):
return PythonDevice.waveform_out(name, **fields)


def _long_string(fields):
if 'length' in fields:
length = fields.pop('length')
elif 'initial_value' in fields:
length = len(fields['initial_value'].encode(errors = 'replace')) + 1
else:
# Default length of 256
length = 256

fields.setdefault('initial_value', '')
fields['_wf_nelm'] = length
fields['_wf_dtype'] = numpy.dtype('uint8')

fields['NELM'] = length
fields['FTVL'] = 'UCHAR'


def longStringIn(name, **fields):
_long_string(fields)
return _in_record('long_stringin', name, **fields)

def longStringOut(name, **fields):
_long_string(fields)
return PythonDevice.long_stringout(name, **fields)



# ----------------------------------------------------------------------------
# Support routines for builder
Expand Down Expand Up @@ -227,6 +271,7 @@ def UnsetDevice():
'stringIn', 'stringOut',
'mbbIn', 'mbbOut',
'Waveform', 'WaveformOut',
'longStringIn', 'longStringOut',
'Action',
# Other builder support functions
'LoadDatabase',
Expand Down
4 changes: 3 additions & 1 deletion softioc/device.dbd
Expand Up @@ -10,5 +10,7 @@ device(waveform, INST_IO, devPython_waveform, "Python")
device(mbbi, INST_IO, devPython_mbbi, "Python")
device(mbbo, INST_IO, devPython_mbbo, "Python")

# Waveforms as "out" devices need special treatment.
# A variety of specialised versions of waveform need special treatment.
device(waveform, INST_IO, devPython_waveform_out, "PythonWfOut")
device(waveform, INST_IO, devPython_long_stringin, "PythonLongStringIn")
device(waveform, INST_IO, devPython_long_stringout, "PythonLongStringOut")