-
Notifications
You must be signed in to change notification settings - Fork 12
Docs re-write, including for asyncio support #14
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
Changes from 13 commits
6d18b9a
f198e73
3708bea
f04dbc3
b9a991d
fcbcfcf
7a32999
b5c3e1b
4d7f891
ebe736b
457ea8d
06ca777
26cb480
338be4d
a873d0e
79e0cba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # Import the basic framework components. | ||
| from softioc import softioc, builder, asyncio_dispatcher | ||
| import asyncio | ||
|
|
||
| # Create an asyncio dispatcher, the event loop is now running | ||
| dispatcher = asyncio_dispatcher.AsyncioDispatcher() | ||
|
|
||
| # Set the record prefix | ||
| builder.SetDeviceName("MY-DEVICE-PREFIX") | ||
|
|
||
| # Create some records | ||
| ai = builder.aIn('AI', initial_value=5) | ||
| ao = builder.aOut('AO', initial_value=12.45, always_update=True, | ||
| on_update=lambda v: ai.set(v)) | ||
|
|
||
| # Boilerplate get the IOC started | ||
| builder.LoadDatabase() | ||
| softioc.iocInit(dispatcher) | ||
|
|
||
| # Start processes required to be run after iocInit | ||
| async def update(): | ||
| while True: | ||
| ai.set(ai.get() + 1) | ||
| await asyncio.sleep(1) | ||
|
|
||
| asyncio.run_coroutine_threadsafe(update(), dispatcher.loop) | ||
|
|
||
| # Finally leave the IOC running with an interactive shell. | ||
| softioc.interactive_ioc(globals()) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # Import the basic framework components. | ||
| from softioc import softioc, builder | ||
| import cothread | ||
|
|
||
| # Set the record prefix | ||
| builder.SetDeviceName("MY-DEVICE-PREFIX") | ||
|
|
||
| # Create some records | ||
| ai = builder.aIn('AI', initial_value=5) | ||
| ao = builder.aOut('AO', initial_value=12.45, always_update=True, | ||
| on_update=lambda v: ai.set(v)) | ||
|
|
||
| # Boilerplate get the IOC started | ||
| builder.LoadDatabase() | ||
| softioc.iocInit() | ||
|
|
||
| # Start processes required to be run after iocInit | ||
| def update(): | ||
| while True: | ||
| ai.set(ai.get() + 1) | ||
| cothread.Sleep(1) | ||
|
|
||
| cothread.Spawn(update) | ||
|
|
||
| # Finally leave the IOC running with an interactive shell. | ||
| softioc.interactive_ioc(globals()) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| from softioc import softioc | ||
| from cothread.catools import caget, caput, camonitor | ||
|
|
||
| print(caget("MY-DEVICE-PREFIX:AI")) | ||
| print(caget("MY-DEVICE-PREFIX:AO")) | ||
| print(caput("MY-DEVICE-PREFIX:AO", "999")) | ||
| print(caget("MY-DEVICE-PREFIX:AO")) | ||
|
|
||
| softioc.interactive_ioc(globals()) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| Create a Publishable IOC | ||
| ======================== | ||
|
|
||
| As seen in :doc:`../tutorials/creating-an-ioc`, a single Python script can be an IOC. | ||
| It is also possible (and the most common situation) to have an entire Python module | ||
| comprising an IOC. This guide explains both, as well as how to publish an IOC within | ||
| the DLS environment. | ||
|
|
||
| Single File IOC | ||
| ---------------- | ||
| An IOC that is entirely contained within a single Python source file can be used as an | ||
| IOC inside DLS simply by adding this shebang line:: | ||
|
|
||
| #!/dls_sw/prod/python3/RHEL7-x86_64/softioc/3.0b2/prefix/bin/pythonIoc | ||
|
|
||
|
|
||
| IOC entry point for a module | ||
| ------------------------------ | ||
| If your IOC is more complicated than one file, it is recommended to write a python | ||
| module (including docs/tests/etc.). The Panda Blocks Client will be an example of | ||
| this. | ||
|
|
||
|
|
||
| Make an IOC publishable at DLS | ||
| ------------------------------ | ||
| To make the IOC publishable, a makefile is required: | ||
|
|
||
| ``Makefile`` | ||
| This file is necessary in order to run ``dls-release.py``, and needs to have | ||
| both ``install`` and ``clean`` targets, but doesn't need to actually do | ||
| anything. Thus the following content for this file is enough:: | ||
|
|
||
| install: | ||
| clean: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| Read data from an IOC | ||
| ====================== | ||
|
|
||
| This guide explains how to read data from an IOC in a separate Python program. | ||
|
|
||
| .. note:: | ||
| Please ensure your firewall allows both TCP and UDP traffic on ports 5064 and 5065. | ||
| These are used by EPICS for channel access to the PVs. | ||
|
|
||
|
|
||
| To start, run the :mod:`cothread` IOC from :doc:`../tutorials/creating-an-ioc` or the | ||
|
AlexanderWells-diamond marked this conversation as resolved.
Outdated
|
||
| :mod:`asyncio` IOC from :doc:`use-asyncio-in-an-ioc` and leave it running at the | ||
| interactive shell. | ||
|
|
||
| We will read data from that IOC using this script: | ||
|
|
||
| .. literalinclude:: ../examples/example_read_from_ioc.py | ||
|
|
||
| .. note:: | ||
| You may see warnings regarding the missing "caRepeater" program. This is an EPICS tool | ||
| that is used to track when PVs start and stop. It is not required for this simple example, | ||
| and so the warning can be ignored. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to DiamondLightSource/aioca#12 (comment) then if you upgrade the epicscorelibs dependency to
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doing this update causes tests to fail due to a missing import. I also see the same error when just trying to start up my example IOCs: I can verify that this is entirely caused by changing the epicscorelibs dependency - swapping the required version back and reinstalling the pipenv makes the tests (and my example IOCs) work again.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops, you need to do |
||
|
|
||
| From the interactive command line you can now use the ``caget`` and ``caput`` functions to operate on | ||
| the PVs exposed in the IOC. Another interesting command to try is:: | ||
|
|
||
| camonitor("MY-DEVICE-PREFIX:AI", lambda val: print(val)) | ||
|
|
||
|
|
||
| You should observe the value of ``AI`` being printed out, once per second, every time the PV value | ||
| updates. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,33 @@ | ||
| Use `asyncio` in an IOC | ||
| ======================= | ||
|
|
||
| Write about the differences creating an IOC using `AsyncioDispatcher` | ||
| There are two libraries available for asynchronous operations in PythonIOC: | ||
| :mod:`cothread` and :mod:`asyncio`. This guide shows how to use the latter in | ||
| an IOC. | ||
|
|
||
| .. note:: | ||
| This page only explains the differences between using :mod:`cothread` and :mod:`asyncio`. | ||
| For more thorough explanation of the IOC itself see :doc:`../tutorials/creating-an-ioc` | ||
|
|
||
| .. literalinclude:: ../examples/example_asyncio_ioc.py | ||
|
|
||
|
|
||
| The ``dispatcher`` is created and passed to :func:`~softioc.softioc.iocInit`. This is what | ||
| allows the use of :mod:`asyncio` functions in this IOC. It contains a new event loop to handle | ||
| this. | ||
|
|
||
| The ``async update`` function will increment the value of ``ai`` once per second, | ||
| sleeping that coroutine between updates. | ||
| Note that we run this coroutine in the ``loop`` of the ``dispatcher``, and not in the | ||
| main event loop. | ||
|
|
||
| This IOC will, like the one in :doc:`../tutorials/creating-an-ioc`, leave an interactive | ||
| shell open. The values of the PVs can be queried using the methods defined in the | ||
| :mod:`softioc.softioc` module. | ||
|
|
||
|
|
||
| Asynchronous Channel Access | ||
| --------------------------- | ||
|
|
||
| PVs can be retrieved externally from a PV in an asynchronous manner by using the :py:mod:`aioca` module. | ||
| It provides ``await``-able implementations of ``caget``, ``caput``, etc. See that module for more information. |
Uh oh!
There was an error while loading. Please reload this page.