Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Bliss
bliss
Commits
3a75f49b
Commit
3a75f49b
authored
Jul 08, 2021
by
Perceval Guillou
Committed by
Cyril Guilloud
Jul 09, 2021
Browse files
update doc of presets
parent
843f98e6
Pipeline
#50265
passed with stages
in 9 minutes and 55 seconds
Changes
3
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
bliss/scanning/chain.py
View file @
3a75f49b
...
...
@@ -57,7 +57,8 @@ def join_tasks(greenlets, **kw):
class
AbstractAcquisitionObjectIterator
:
"""Iterate over an AcquisitionObject, yielding self.
"""
Iterate over an AcquisitionObject, yielding self.
"""
@
property
...
...
@@ -395,7 +396,8 @@ class AcquisitionObject:
self
.
_do_add_counter
(
counter
)
else
:
raise
RuntimeError
(
f
"Cannot add counter
{
counter
.
name
}
: acquisition controller mismatch
{
counter
.
_counter_controller
}
!=
{
self
.
device
}
"
f
"Cannot add counter
{
counter
.
name
}
: acquisition controller"
f
" mismatch
{
counter
.
_counter_controller
}
!=
{
self
.
device
}
"
)
def
get_iterator
(
self
):
...
...
@@ -406,7 +408,7 @@ class AcquisitionObject:
else
:
return
AcquisitionObjectIterator
(
self
)
# --------------------------- ACQ. CHAIN METHODS ------------------------------------------
---
# --------------------------- ACQ. CHAIN METHODS ------------------------------------------
def
has_reading_task
(
self
):
"""Returns True when the underlying device has a reading task.
...
...
@@ -437,7 +439,7 @@ class AcquisitionObject:
finally
:
gevent
.
killall
(
tasks
,
exception
=
StopTask
)
# --------------------------- OVERLOAD ACQ. CHAIN METHODS ---------------------------------
------------
# --------------------------- OVERLOAD ACQ. CHAIN METHODS ---------------------------------
def
acq_prepare
(
self
):
raise
NotImplementedError
...
...
@@ -1110,11 +1112,10 @@ class AcquisitionChain:
def
add_preset
(
self
,
preset
,
master
=
None
):
"""
Add a preset on a top-master.
If it None mean the first in the chain
Args:
preset
should be inherited for class Prese
t
master if None take the first top-master f
rom
the chain
preset
: a ChainPreset objec
t
master
:
if None
,
take the first top-master
o
f the chain
"""
if
not
isinstance
(
preset
,
ChainPreset
):
raise
ValueError
(
"Expected ChainPreset instance"
)
...
...
@@ -1236,22 +1237,27 @@ class ChainNode:
and
self
.
_parent_acq_params
!=
parent_acq_params
):
print
(
f
"=== ChainNode WARNING: try to set PARENT_ACQ_PARAMS again:
\n
Current
{
self
.
_parent_acq_params
}
\n
New
{
parent_acq_params
}
"
f
"=== ChainNode WARNING: try to set PARENT_ACQ_PARAMS again:
\n
"
f
"Current
{
self
.
_parent_acq_params
}
\n
New
{
parent_acq_params
}
"
)
if
force
or
self
.
_parent_acq_params
is
None
:
self
.
_parent_acq_params
=
parent_acq_params
def
set_parameters
(
self
,
acq_params
=
None
,
ctrl_params
=
None
,
force
=
False
):
""" Store the scan and/or acquisition parameters into the node.
These parameters will be used when the acquisition object is instantiated (see self.create_acquisition_object )
If the parameters have been set already, new parameters will be ignored (except if Force==True).
"""
Store the scan and/or acquisition parameters into the node.
These parameters will be used when the acquisition object
is instantiated (see self.create_acquisition_object )
If the parameters have been set already, new parameters will
be ignored (except if Force==True).
"""
if
acq_params
is
not
None
:
if
self
.
_acq_obj_params
is
not
None
and
self
.
_acq_obj_params
!=
acq_params
:
print
(
f
"=== ChainNode WARNING: try to set ACQ_PARAMS again:
\n
Current
{
self
.
_acq_obj_params
}
\n
New
{
acq_params
}
"
f
"=== ChainNode WARNING: try to set ACQ_PARAMS again:
\n
"
f
"Current
{
self
.
_acq_obj_params
}
\n
New
{
acq_params
}
"
)
if
force
or
self
.
_acq_obj_params
is
None
:
...
...
@@ -1260,7 +1266,8 @@ class ChainNode:
if
ctrl_params
is
not
None
:
if
self
.
_ctrl_params
is
not
None
and
self
.
_ctrl_params
!=
ctrl_params
:
print
(
f
"=== ChainNode WARNING: try to set CTRL_PARAMS again:
\n
Current
{
self
.
_ctrl_params
}
\n
New
{
ctrl_params
}
"
f
"=== ChainNode WARNING: try to set CTRL_PARAMS again:
\n
"
f
"Current
{
self
.
_ctrl_params
}
\n
New
{
ctrl_params
}
"
)
if
force
or
self
.
_ctrl_params
is
None
:
...
...
@@ -1279,13 +1286,18 @@ class ChainNode:
self
.
_counters
.
append
(
counter
)
def
_get_default_chain_parameters
(
self
,
scan_params
,
acq_params
):
""" Obtain the full acquisition parameters set from scan_params in the context of the default chain """
"""
Obtain the full acquisition parameters set from scan_params
in the context of the default chain
"""
return
self
.
controller
.
get_default_chain_parameters
(
scan_params
,
acq_params
)
def
get_acquisition_object
(
self
,
acq_params
,
ctrl_params
,
parent_acq_params
):
""" Return the acquisition object associated to this node
acq_params, ctrl_params and parent_acq_params have to be dicts (None not supported)
"""
Return the acquisition object associated to this node
acq_params, ctrl_params and parent_acq_params have to be
dicts (None not supported)
"""
return
self
.
controller
.
get_acquisition_object
(
...
...
@@ -1293,18 +1305,19 @@ class ChainNode:
)
def
create_acquisition_object
(
self
,
force
=
False
):
""" Create the acquisition object using the current parameters (stored in 'self._acq_obj_params').
Create the children acquisition objects if any are attached to this node.
- 'force' (bool): if False, it won't instanciate the acquisition object if it already exists, else it will overwrite it.
"""
Create the acquisition object using the current
parameters (stored in 'self._acq_obj_params').
Create the children acquisition objects if any are attached to this node.
- 'force' (bool): if False, it won't instanciate the acquisition
object if it already exists, else it will overwrite it.
"""
# --- Return acquisition object if it already exist and Force is False ----------------
------------
# --- Return acquisition object if it already exist and Force is False ----------------
if
not
force
and
self
.
_acquisition_obj
is
not
None
:
return
self
.
_acquisition_obj
# --- Prepare parameters --------------------------------------------------------------
---------------------------
# --- Prepare parameters --------------------------------------------------------------
if
self
.
_acq_obj_params
is
None
:
acq_params
=
{}
else
:
...
...
@@ -1324,7 +1337,7 @@ class ChainNode:
self
.
_parent_acq_params
.
copy
()
)
# <= IMPORTANT: pass a copy because the acq obj may pop on that dict!
# --- Create the acquisition object ---------------------------------------------------
----
# --- Create the acquisition object ---------------------------------------------------
acq_obj
=
self
.
get_acquisition_object
(
acq_params
,
ctrl_params
=
ctrl_params
,
parent_acq_params
=
parent_acq_params
)
...
...
bliss/scanning/scan.py
View file @
3a75f49b
...
...
@@ -428,6 +428,13 @@ class ScanPreset:
return
self
.
__new_data_callback
(
counter
,
sender
.
fullname
,
data
)
def
connect_data_channels
(
self
,
counters_list
,
callback
):
"""
Associate a callback to the data emission by the channels of a list of counters.
Args:
* counters_list: the list of counters to connect data channels to
* callback: a callback function
"""
nodes
=
self
.
acq_chain
.
get_node_from_devices
(
*
counters_list
)
for
i
,
node
in
enumerate
(
nodes
):
try
:
...
...
doc/docs/scan_engine_preset.md
View file @
3a75f49b
*Presets*
are used to set environnement for a scan. Typically to control:
# Preset
*Presets*
are used to customize a scan by performing extra actions
(eg. open/close a shutter) at special events like
`prepare()`
,
`start()`
and
`stop()`
.
BLISS standard presets are:
`ScanPreset`
,
`ChainPreset`
and
`ChainIterationPreset`
.
Typically they allow to control:
*
opening/closing of a shutter
*
detector cover removing/replacing
...
...
@@ -7,14 +17,113 @@
*
equipment protection (via data channels hook, see below)
*
...
A preset is a
*hook*
in a scan iteration. This hook can be set at different
levels depending on the need.
!!! Note
In the general case, DO NOT use a
`Preset`
to modify the acquisition
parameters (eg. acquisition time) of a device.
To modify the default acquisition parameters used by a device in
standard scans, use `DEFAULT_CHAIN.set_settings`, see [Default
chain](scan_default.md#default-chain).
## Quick overview
A preset is a
*hook*
introduced in the
[
acquisition
chain
](
scan_writing.md#the-acquisition-chain
)
of a scan thanks to the
`add_preset()`
method.
A preset has three methods (
`prepare()`
,
`start()`
,
`stop()`
) that user can
customize.
Bliss provides three kind of presets that act at different levels of the scan
chain:
`ScanPreset`
,
`ChainPreset`
and
`ChainIterationPreset`
.
**ScanPreset:**
(hook a whole scan)
*
`.prepare`
: called at scan prepare
*
`.start`
: called at scan start
*
`.stop`
: called at scan stop
```
python
DEMO
[
1
]:
s
=
loopscan
(
2
,
0.1
,
diode
,
run
=
False
)
DEMO
[
2
]:
p
=
MyScanPreset
()
# a ScanPreset object
DEMO
[
3
]:
s
.
add_preset
(
p
)
# using the scan object method
DEMO
[
4
]:
s
.
run
()
```
**ChainPreset:**
(hook a top-master)
*
`.prepare`
: called at TopMaster.prepare
*
`.start`
: called at TopMaster.start
*
`.stop`
: called at TopMaster.stop
Similar to
`ScanPreset`
if the chain has only one top-master (i.e. single
branch acq_chain)
```
python
DEMO
[
1
]:
s
=
loopscan
(
2
,
0.1
,
diode
,
run
=
False
)
DEMO
[
2
]:
p
=
MyChainPreset
()
# a ChainPreset object
DEMO
[
3
]:
s
.
acq_chain
.
add_preset
(
p
)
# using the chain object method
DEMO
[
4
]:
s
.
run
()
```
**ChainIterationPreset:**
(hook each iteration of a top-master)
*
`.prepare`
: called at prepare of step i (iteration i of a top-master)
*
`.start`
: called at start of step i (iteration i of a top-master)
*
`.stop`
: called at stop of step i (iteration i of a top-master)
```
python
DEMO
[
1
]:
s
=
loopscan
(
2
,
0.1
,
diode
,
run
=
False
)
DEMO
[
2
]:
p
=
MyIteratingChainPreset
()
# a ChainPreset object with
# a get_iterator method
DEMO
[
3
]:
s
.
acq_chain
.
add_preset
(
p
)
# using the chain object method
DEMO
[
4
]:
s
.
run
()
```
!!! note
Most of the time the acquisition chain has only one top-master (only
one acquisition chain branch) and in that case using a
`ScanPreset`
or a
`ChainPreset`
will produce the same result.
## Add presets to the default chain
It is possible to add a preset to all standard scans of Bliss (i.e:
`ct`
,
`loopscan`
,
`ascan`
, etc.).
```
python
DEMO
[
1
]:
p
=
MyChainPreset
()
DEMO
[
2
]:
DEFAULT_CHAIN
.
add_preset
(
p
)
# method of the default chain object
...
DEMO
[
9
]:
DEFAULT_CHAIN
.
remove_preset
(
p
)
```
## Multiple top-masters case
Keep in mind that the
[
acquisition chain
](
scan_writing.md#the-acquisition-chain
)
can have multiple
**branches**
(one per top-master).
The
`ChainPreset`
and
`ChainIterationPreset`
are associated to one top-master
(i.e: one acquisition chain branch).
That is why the
`add_preset()`
method of the acquisition chain takes an optional
argument
`master`
.
```
python
def
add_preset
(
self
,
preset
,
master
=
None
):
"""
Add a preset on a top-master.
Args:
preset: a ChainPreset object
master: if None, take the first top-master of the chain
"""
```
*
to hook a whole scan, it will inherit from
`ScanPreset`
*
to hook a part of the acquisition chain it will inherit from
`ChainPreset`
## ScanPreset
This is the simplest one.
This one
has 3 callback methods:
This is the simplest one.
It
has 3 callback methods:
*
`prepare()`
: called before all devices preparation
*
`start()`
: called before all devices starting
...
...
@@ -34,9 +143,11 @@ class Preset(ScanPreset):
print
(
f
"
{
scan
.
name
}
scan is stopped"
)
print
(
f
"Closing the shutter"
)
```
and it's usage:
```
python
DEMO
[
3
]:
p
=
Preset
()
DEMO
[
4
]:
s
=
loopscan
(
2
,
0.1
,
diode
,
run
=
False
)
DEMO
[
4
]:
s
=
loopscan
(
2
,
0.1
,
diode
,
run
=
False
)
DEMO
[
5
]:
s
.
add_preset
(
p
)
DEMO
[
6
]:
s
.
run
()
...
...
@@ -55,20 +166,35 @@ loopscan scan is stopped
Closing
the
shutter
```
###
Data channels hook
###
Data channels hook
The
`ScanPreset`
has a
`connect_data_channels`
method
,
to execute a callback
The
`ScanPreset`
has a
`connect_data_channels
()
`
method to execute a callback
when data is emitted from channels.
It is useful
in order
to protect some equipments
, f
or example
:
if the value
measured by a diode exceeds some threshold, the scan can stop or some
It is useful to protect some equipments
. F
or example
,
if the value
measured by a diode exceeds some threshold, the scan can
be
stop
ped
or some
attenuators can be activated.
The basic usage is to call the
`.connect_data_channels()`
method, from the
`.prepare()`
of
`ScanPreset`
. Arguments are:
The basic usage is to call the
`.connect_data_channels()`
method from the
`.prepare()`
of a
`ScanPreset`
.
The callback will receive the arguments:
`counter`
,
`channel_name`
and
`data`
.
```
python
class
ScanPreset
:
...
def
connect_data_channels
(
self
,
counters_list
,
callback
):
"""
Associate a callback to the data emission by the channels
of a list of counters.
Args:
* counters_list: the list of counters to connect data channels to
* callback: a callback function
"""
...
*
the list of counters to connect data channels to
*
a callback function
```
Example:
...
...
@@ -79,11 +205,9 @@ class MyScanPreset(ScanPreset):
self
.
diode
=
diode
def
prepare
(
self
,
scan
):
self
.
connect_data_channels
([
self
.
diode
,
...],
self
.
protect_my_detector
)
def
protect_my_detector
(
self
,
counter
,
channel_name
,
data
):
if
counter
==
diode
:
# assuming the counter has only 1 channel, no need to
...
...
@@ -98,14 +222,24 @@ If an exception is raised in the callback function, the scan will stop.
## ChainPreset
This hook is linked to a
*top-master*
of the acquisition chain. So the
callback method will be called during
`prepare`
,
`start`
and
`stop`
phases of this top-master. It has exactly the same behaviour than
the
`ScanPreset`
if the chain has
**only one**
top-master.
ChainPreset hook is linked to a
*top-master*
of the acquisition chain. So the
callback method will be called during
`prepare`
,
`start`
and
`stop`
phases of
this top-master. It has exactly the same behaviour than the
`ScanPreset`
if the
chain has
**only one**
top-master.
Example: In a loopscan, the only top master is a timer. So here it is the same
example as shown above with the ScanPreset, where a shutter is opened at the
beginning of the scan and closed at the end.
The only difference is that the
`add_preset()`
method is called on the
acquisition chain object instead of the scan object (
`s.acq_chain.add_preset`
instead of
`s.add_preset`
).
In the multiple top-masters case, the
`acq_chain.add_preset`
method takes an
optional
`master`
argument to specify the top-master that should be associated
to this preset (see
[
Multiple top-masters
case
](
scan_engine_preset.md#multiple-top-masters-case
)
)
i.e: In a loopscan, the sole top master is a timer, so here is the same simple
example where the need is to open a shutter at the beginning of the scan and to
close it at the end.
```
python
from
bliss.scanning.chain
import
ChainPreset
...
...
@@ -138,15 +272,17 @@ Took 0:00:36.189189
## ChainIterationPreset
Use this object when you want to set a hook on each
*iteration*
of a
top-master.
`ChainIterationPreset`
is
**yield**
from
`ChainPreset`
instance by
*get_iterator*
method. i.e here is an example where you want to
open/close the shutter for each point.
Use ChainIterationPreset to set a hook on each
*iteration*
of a
top-master.
`ChainIterationPreset`
is
**yield**
from
`ChainPreset`
instance by
the
`get_iterator`
method.
Example: to open and close the shutter at each iteration of the scan.
```
python
class
Preset
(
ChainPreset
):
class
Iterator
(
ChainIterationPreset
):
def
__init__
(
self
,
iteration_nb
):
def
__init__
(
self
,
iteration_nb
):
self
.
iteration
=
iteration_nb
def
prepare
(
self
):
print
(
f
"Preparing iteration
{
self
.
iteration
}
"
)
...
...
@@ -154,12 +290,15 @@ class Preset(ChainPreset):
print
(
f
"Starting, Opening the shutter iter
{
self
.
iteration
}
"
)
def
stop
(
self
):
print
(
f
"Stopped, closing the shutter, iter
{
self
.
iteration
}
"
)
def
get_iterator
(
self
,
acq_chain
):
def
get_iterator
(
self
,
acq_chain
):
iteration_nb
=
0
while
True
:
yield
Preset
.
Iterator
(
iteration_nb
)
iteration_nb
+=
1
```
```
python
DEMO
[
2
]:
p
=
Preset
()
DEMO
[
3
]:
s
=
loopscan
(
2
,
0.1
,
diode
,
run
=
False
)
...
...
@@ -183,28 +322,33 @@ Took 0:00:16.677241
```
!!! warning
In this example, you can see that
*data display*
and the
*
chain
iteration
*
are executed by two separated greenlets which are not
synchronised. This is not a problem but can be confusing if you think
that it's sequencial.
In this example,
*data display*
and the
*chain iteration*
are
executed by two separated greenlets which are not synchronised. This is not
a problem but can be confusing for the user.
In the multiple top-masters case, the
`acq_chain.add_preset`
method takes an
optional
`master`
argument to specify the top-master that should be associated
to this preset (see
[
Multiple top-masters
case
](
scan_engine_preset.md#multiple-top-masters-case
)
)
## To pause a scan
As
`Preset`
callbacks are executed synchronously, they can easily pause
a scan,
just by not returning imediatly from
*
prepare
*
,
*
start
*
or
*
stop
*
callback
methods.
As
`Preset`
callbacks are executed synchronously, they can easily pause
a scan,
just by not returning imediatly from
`
prepare
()`
,
`
start
()`
or
`
stop
()`
callback
methods.
For example, to pause a scan in case of beam loss, the condition has to be
checked in a loop.
As an example, wait to
start a scan
if
the beam is
not
present.
Example to delay
start
ing
a scan
until
the beam is present.
```
python
class
Preset
(
ScanPreset
):
def
__init__
(
self
,
diode
,
beam_trigger_value
):
def
__init__
(
self
,
diode
,
beam_trigger_value
):
self
.
_diode
=
diode
self
.
_beam_trigger_value
def
prepare
(
self
,
scan
):
def
prepare
(
self
,
scan
):
beam_value
=
self
.
_diode
.
read
()
while
beam_value
<
self
.
_beam_trigger_value
:
print
(
"Waiting for beam"
)
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment