Items - Desy

Sardana/Spock/Taurus at DESY
Version July 6, 2015
Contents
1
The Introduction
1.1 General Remarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 News . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3 Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2
The first steps
2.1 Notions . . . . . . .
2.2 Configuration scripts
2.3 The very beginning .
2.4 Exploring the system
2.5 The first scan . . . .
3
4
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
IPython Features
3.1 Miscellaneous Items . . . . . . . . . . . . . . . . . .
3.1.1 TAB-Completion . . . . . . . . . . . . . . . .
3.1.2 Run a Python Script from within IPython . . .
3.1.3 Show the Local Variables of an IPython session
3.1.4 Display Information about an Object . . . . . .
3.1.5 Execute a Shell Command . . . . . . . . . . .
3.2 Command Shorthands . . . . . . . . . . . . . . . . . .
3.3 Startup Scripts . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7
7
8
8
.
.
.
.
.
9
9
9
9
11
14
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
18
18
18
18
18
19
19
19
20
Scans
4.1 Environment variables for scans . . . . . . . . . . . . . . . . . . . . . .
4.1.1 FioAdditions Example . . . . . . . . . . . . . . . . . . . . . . .
4.2 Scan Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2.1 Example: ascan . . . . . . . . . . . . . . . . . . . . . . . . . .
4.3 Configuring the MeasurementGroup . . . . . . . . . . . . . . . . . . . .
4.3.1 Configuring the MeasurementGroup with the ComponentSelector
4.3.2 Configuring the MeasurementGroup with expconf . . . . . . . .
4.3.3 Configuring the MeasurementGroup using macros from Spock . .
4.3.4 Configuring the MeasurementGroup using a script . . . . . . . .
4.3.5 Adding any Tango Attribute as Counter in the MeasurementGroup
4.3.6 Adding MCA RoIs as Counter in the MeasurementGroup . . . . .
4.4 SardanaMonitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.5 Hooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.6 General hooks and conditions . . . . . . . . . . . . . . . . . . . . . . . .
4.7 Absorber scans using general hooks and conditions . . . . . . . . . . . .
4.8 Repeating scan points, specific scan class (XMCD) . . . . . . . . . . . .
4.9 Repeating scan points conditionally, specific scan class . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
22
22
23
24
25
25
26
26
27
27
28
28
29
30
30
34
37
40
1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
6
Spock
5.1 Profiles . . . . . . . . . . . . . . . . . . . . .
5.2 Interrupting Macro Execution . . . . . . . . . .
5.2.1 Callback function . . . . . . . . . . .
5.3 Access to Tango Devices in Spock . . . . . . .
5.4 Environment Variables . . . . . . . . . . . . .
5.4.1 load env . . . . . . . . . . . . . . . . .
5.4.2 View Options . . . . . . . . . . . . . .
5.5 Get list of components: lsct, lsm, ls0d, ls1d, etc
5.6 Motor . . . . . . . . . . . . . . . . . . . . . .
5.7 Counter/Timer . . . . . . . . . . . . . . . . . .
5.8 IO Register . . . . . . . . . . . . . . . . . . .
5.9 Macros . . . . . . . . . . . . . . . . . . . . .
5.10 Run a sequence of macros . . . . . . . . . . .
5.11 time . . . . . . . . . . . . . . . . . . . . . . .
5.12 Debugging . . . . . . . . . . . . . . . . . . . .
5.12.1 Debug . . . . . . . . . . . . . . . . . .
5.12.2 edmac . . . . . . . . . . . . . . . . . .
5.12.3 www . . . . . . . . . . . . . . . . . .
5.13 The DOOR and MACRO SERVER proxies . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Scripting, Macros
6.1 Some remarks about macros . . . . . . . . . . . . . . . . . . . . . .
6.2 DESY Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2.1 e2lambda, lambda2e . . . . . . . . . . . . . . . . . . . . . .
6.2.2 mvsa: move-scan-analysis . . . . . . . . . . . . . . . . . . .
6.3 Beamline specific macros . . . . . . . . . . . . . . . . . . . . . . . .
6.3.1 Maia detector scan macro . . . . . . . . . . . . . . . . . . .
6.3.2 Regions scan with maia scan at each point . . . . . . . . . . .
6.3.3 Continuous scans with external trigger (Zebra & XIA & MCS)
6.3.4 Sweep Scans (p02) . . . . . . . . . . . . . . . . . . . . . . .
6.4 Example Macro Classes . . . . . . . . . . . . . . . . . . . . . . . .
6.4.1 Hello world . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.4.2 Parameter Types . . . . . . . . . . . . . . . . . . . . . . . .
6.4.3 Environment Variables . . . . . . . . . . . . . . . . . . . . .
6.4.4 A Loop of Scans . . . . . . . . . . . . . . . . . . . . . . . .
6.4.5 Access to Scan Data . . . . . . . . . . . . . . . . . . . . . .
6.4.6 Access to Scan Data, Modified . . . . . . . . . . . . . . . . .
6.4.7 Access to Scan Data from a post-acq Hook . . . . . . . . . .
6.4.8 Move a Motor Asynchronously . . . . . . . . . . . . . . . .
6.4.9 Read an ADC, return the result . . . . . . . . . . . . . . . . .
6.4.10 A Hooked Scan . . . . . . . . . . . . . . . . . . . . . . . . .
6.4.11 Move to the position calculated from the scan data . . . . . .
6.4.12 Mixed classes . . . . . . . . . . . . . . . . . . . . . . . . . .
6.4.13 Call on stop, also from parent class . . . . . . . . . . . . . .
6.5 Example Macro Functions . . . . . . . . . . . . . . . . . . . . . . .
6.5.1 Hello world . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.5.2 Environment Variables . . . . . . . . . . . . . . . . . . . . .
6.5.3 A Loop of Scans . . . . . . . . . . . . . . . . . . . . . . . .
6.5.4 Access to Scan Data . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
42
42
42
42
43
43
44
45
47
47
48
48
49
49
50
50
50
51
51
51
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
53
53
54
54
55
58
58
62
63
78
86
86
86
87
88
88
89
90
91
92
93
94
98
99
100
100
100
101
101
6.5.5
6.5.6
6.5.7
6.5.8
6.5.9
6.5.10
7
Access to Scan Data, modified . .
Move a Motor Asynchronously .
Read an ADC an return the result
IO Register . . . . . . . . . . . .
A Hooked Scan . . . . . . . . . .
Interactive Macros . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
The Sardana NeXus Recorder
7.1 Device Selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.2 Component Selector and MacroGUI integration in TaurusGUI . . . . . . .
7.3 The first NeXus scan . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.4 NeXus Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.5 NeXus Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.5.1 Components and data sources . . . . . . . . . . . . . . . . . . . .
7.5.2 NeXus file structure . . . . . . . . . . . . . . . . . . . . . . . . .
7.5.3 Preparation of Components . . . . . . . . . . . . . . . . . . . . . .
7.5.4 Data acquisition process . . . . . . . . . . . . . . . . . . . . . . .
7.5.5 Mandatory metadata . . . . . . . . . . . . . . . . . . . . . . . . .
7.6 Instruction for experts . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.6.1 NeXus Servers . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.6.2 Check if nxsconfig mysql database exists on TANGO HOST . . . .
7.6.3 Setting up NeXus servers . . . . . . . . . . . . . . . . . . . . . . .
7.6.4 Restarting NeXus servers . . . . . . . . . . . . . . . . . . . . . . .
7.6.5 Component Selector Configuration . . . . . . . . . . . . . . . . .
7.6.6 Creating a new TaurusGUI application with the Component Selector
7.7 Using the Selector Server without Sardana . . . . . . . . . . . . . . . . . .
7.7.1 Writing NeXus files configured by the Component Selector . . . . .
7.7.2 Writing NeXus files with CLIENT data fetched from Tango Servers
7.7.3 Writing NeXus files with dynamic components in C++ . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
102
103
103
104
104
107
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
108
108
110
111
111
112
112
113
115
116
118
118
118
119
119
120
120
121
121
122
123
125
8
Taurus Tools
136
8.1 taurusform . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
8.2 taurusimage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
8.3 taurus device, accessing MacroServer environment . . . . . . . . . . . . . . . . . 139
9
Diffractometer Software
9.1 Configuration . . . . . . . . . . . . . . . . . .
9.2 Macros . . . . . . . . . . . . . . . . . . . . .
9.2.1 add reflection . . . . . . . . . . . . . .
9.2.2 affine . . . . . . . . . . . . . . . . . .
9.2.3 br (move to reciprocal space coordinate)
9.2.4 ca, caa (hkl2Angles) . . . . . . . . . .
9.2.5 compute u . . . . . . . . . . . . . . .
9.2.6 freeze (psi) . . . . . . . . . . . . . . .
9.2.7 getmode, setmode . . . . . . . . . . .
9.2.8 hscan, kscan, lscan, hklscan . . . . . .
9.2.9 or0, or1, or swap . . . . . . . . . . . .
9.2.10 newcrystal, savecrystal . . . . . . . . .
9.2.11 pa (print diffractometer info) . . . . . .
9.2.12 printmove . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
141
141
141
141
142
142
143
143
144
144
144
146
147
147
148
9.2.13
9.2.14
9.2.15
9.2.16
9.2.17
9.2.18
setaz (reference vector) . . . . . . .
setlat . . . . . . . . . . . . . . . .
setor0, setor1 . . . . . . . . . . . .
setorn . . . . . . . . . . . . . . . .
th2th . . . . . . . . . . . . . . . .
wh (print positions and parameterst)
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
148
148
148
149
149
149
10 Tools
151
10.1 ECMonitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
10.2 MotorLogger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
11 Sardana configuration
11.1 Setting up the Pools and the MacroServers . . . . . . . . . . . . . . . . . .
11.1.1 Finishing the Sardana configuration: /online dir/SardanaConfig.py
11.2 TangoExplorer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.2.1 TangoExplorer: Pattern matching . . . . . . . . . . . . . . . . . .
11.3 Selecting the devices: online.xml syntax . . . . . . . . . . . . . . . . . . .
11.3.1 Detectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.3.2 Diffractometer . . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.3.3 Measurement Groups . . . . . . . . . . . . . . . . . . . . . . . . .
11.3.4 RoIs as counters . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.4 The configuration at P01 . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.5 The configuration at P02 . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.6 The configuration at P03 . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.7 The configuration at P03NANO . . . . . . . . . . . . . . . . . . . . . . .
11.8 The configuration at P04 . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.8.1 The configuration at P04EXP1 . . . . . . . . . . . . . . . . . . . .
11.8.2 The configuration at P04EXP2 . . . . . . . . . . . . . . . . . . . .
11.9 The configuration at P06 . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.10The configuration at P07EH2A . . . . . . . . . . . . . . . . . . . . . . . .
11.11The configuration at P08 . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.12The configuration at P09 . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.13The configuration at P10 . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.14The Configuration at P11 . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.15The Configuration at P64 . . . . . . . . . . . . . . . . . . . . . . . . . . .
11.16The Configuration at hascmexp . . . . . . . . . . . . . . . . . . . . . . . .
11.17Solution to possible problems . . . . . . . . . . . . . . . . . . . . . . . . .
12 Technical Details
12.1 The MacroServer Environment . . . .
12.2 The HasyUtils module . . . . . . . .
12.2.1 Door-MacroServer-Pool Class
12.3 Devices and Controllers . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13 Examples
13.1 Access the MacroServer Environment from Python
13.2 Fetch MacroServer Environment Variables (P10) .
13.3 Change the Attribute Configuration, %6.2f to %g .
13.4 Macro to setup the Lambda detector . . . . . . . .
13.5 Lambda Integration as VcExecutor (P01) . . . . .
13.6 Signal Generator as VcExecutor (with Offset, P09)
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
153
153
156
157
157
162
164
167
168
168
169
169
170
171
172
172
172
173
175
179
179
181
182
184
185
185
.
.
.
.
186
186
186
187
187
.
.
.
.
.
.
190
190
191
192
193
193
196
13.7 Pilatus Integration as Vc (obsolete) . . . . . . . . . . . . . . . . . . . . . . . . . . 197
13.8 Virtual Motor as VMExecutor . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
List of Figures
2.1
2.2
The SardanaMonitor widget . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
The SardanaMonitor graphics output . . . . . . . . . . . . . . . . . . . . . . . . .
16
17
4.1
4.2
4.3
4.4
The expconf widget . . . . . . . . . . . . . . . .
SardanaMonitor Widget . . . . . . . . . . . . . .
SardanaMonitor graphics output, one MCA only .
SardanaMonitor graphics output . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
26
29
30
31
7.1
7.2
7.3
7.4
7.5
7.6
7.7
7.8
7.9
Component Selector: Detectors . . . . .
Component Selector: Descriptions . . .
Component Selector: NeXus User Data
Data Flow for NeXus Data Writer . . .
Component for Pilatus1M detector . . .
Component Merging . . . . . . . . . .
Component Designer . . . . . . . . . .
Sequence diagram . . . . . . . . . . . .
Component Selector: Configuration . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
108
109
110
113
114
115
116
117
120
8.1
8.2
8.3
8.4
8.5
8.6
taurusform: device selector widget . . . .
taurusform: device widget . . . . . . . .
taurusform: the main widget . . . . . . .
TaurusImage Panel . . . . . . . . . . . .
taurusimage toolbar . . . . . . . . . . . .
TaurusImage Panel with all functionalities
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
136
137
138
139
139
140
11.1
11.2
11.3
11.4
11.5
11.6
11.7
11.8
TangoExplorer . . . . . . . . . . . . . . . . . . . . . . . . . . .
TangoExplorer searching servers . . . . . . . . . . . . . . . . . .
TangoExplorer: Motor position . . . . . . . . . . . . . . . . . . .
TangoExplorer: Multiple Action, read motor positions . . . . . .
TangoExplorer: Create Code . . . . . . . . . . . . . . . . . . . .
TangoExplorer: Multiple Action, write . . . . . . . . . . . . . . .
Astor: Sardana on hasgksspp07eh2a . . . . . . . . . . . . . . . .
Jive: The MacroServer PoolNames Property on hasgksspp07eh2a
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
158
159
160
161
162
163
176
177
6
Chapter 1
The Introduction
This note describes the DESY implementation of Sardana system.
1.1
General Remarks
This section introduces the main Sardana components and gives some related information.
• The DevicePool (Pool) is a Tango Server which uses controllers as an abstraction layer for
hardware access. Currently the following controller types are used at DESY: Motor, IORegister, CTExpChannel, ZeroDExpChannel, OneDExpChannel, PseudoMotor. The controller
code can be found in the sub-directories of /Poolcontrollers. The contents of these directories
is automatically loaded when the Pool server starts.
The Pool command CreateController creates a controller instance. The parameters are controller type, library name (Python script name), class name (defined in the Python script),
controller name and other items. Many controllers use RootDeviceName to select the Tango
servers to be operated by a controller instance. Examples for controller names (aliases) are
omsvme58 eh2a or omsvme58 eh2b. The controller name reflects the devices a controller is
talking to, e.g.: omsvme58 eh2a speaks to all motors of the OmsVme58 EH2A instance, like
p02/motor/eh2a.01, etc.. In this case the RootDeviceName is p02/motor/eh2a. This string
matches all Tango servers to be controlled by a omsvme58 eh2a.
After a controller has been created it can be populated with Pool devices. This is done with
the Pool command CreateElement. The parameters are the controller type, the controller
name, a channel number and the alias for the Pool device. The channel number starts at
1. It is used as an index for the list that was created with the RootDeviceName. There
is a one-to-one correspondance between Pool devices and Tango devices, e.g.: the Tango
device p02/motor/eh2a.01 is connected to the Pool device eh2a mot01. This is an alias which
happens to be identical with the Online device names.
• The MeasurementGroup (MG) selects the devices that are used during measurements. The
MG is a Tango server and it is part of the Pool. The attribute ElementList can be inspected
to find the selected devices. The attribute Configuration contains all information. It can be
inspected by ’chMntGrp.py -l’.
• The MacroServer executes Macros (python scripts) and has a Door as an access point.
Macroserver and Door are Tango servers. MacroServers are connected to a Pool.
The word Macro starts with a capital letter because it refers to Python code which has a welldefined structure. A Sardana Macro defines a class which inherits from the Macro superclass.
Tbe class name is used to refer to the Macro from Spock or from any other application.
7
Perhaps it is instructive to execute this litte exercise: Open a Door using ’Test device’ and
select the command RunMacro. Give it ’wa’ as input and execute it. The attribute ’Output’
contains the result.
• The IPython application Spock is a command line user interface which uses a Door to communicate with a MacroServer.
• The Taurus library allows to create graphical users interfaces for Sardana.
At PETRA III the Sardana system is an additional layer on top of the Tango Servers which has the
following consequences:
• Other clients like Online or Python scripts are independent of Sardana and remain operational.
• Device specific parameters, like slew rates or accelerations, are attributes of the Tango device
servers. They are not managed by Sardana. Therefore, the Pool and the MacroServer can be
created or deleted whithout any loss of information in this respect.
1.2
News
This chapter lists the most recent changes.
July 6, 2015
Latest LaTex run of this manual.
19.11.2007
First version of these pages. Still very preliminar.
1.3
Acknowledgments
The Sardana programm package with Taurus being the underlying toolkit library was created at
ALBA. Here are the links to the official documentation:
Sardana:
http://www.tango-controls.org/static/sardana/latest/doc/html/
֒→ development/
Taurus:
http://www.tango-controls.org/static/taurus/latest/doc/html/
֒→ index.html
The system is described in J. Klora, T. Coutinho et al., The architecture of the ALBA Control
System, Proceedings NOBUGS 2008.
We are very grateful for the support that we received from our colleagues at ALBA, esp. Tiago
Coutinho, Joerg Klora, Carlos Pascual, Sergi Rubio Manrique and Sergi Blanch.
Chapter 2
The first steps
2.1
Notions
The Sardana Device Pool is a Tango server establishing an abstraction layer for hardware access.
The Pool has Controllers for device classes: Motors, IORegisters, CTExpChannels (countertimer), etc. Controller instances are called Pool Devices. At DESY, Pool devices do not access
the hardware directly, instead they are connected to Tange servers.
The MeasurementGroup is a selection of devices used for scans. It is part of a Pool.
The MacroServer is a Tango server executing Python scripts, aka Macros, that access Pool devices
or other Tango servers. At DESY, there is a one-to-one relationship between MacroServers and
Pools.
Doors are sub-classes of MacroServes. They serve as entry points for Sardana applications like
spock.
2.2
Configuration scripts
Beginners should ignore the following references to configuration scripts. They are put to this
location to give users the chance to find them.
• A script which is invoked with every start of a spock session can be found here: 3.3.
• A script which is invoked with every SardanaStartup.py (or SardanaAIO.py) can be found
here: 11.1.1.
2.3
The very beginning
The status of the Sardana system can be inspected by:
$ SardanaStatus.py
Local DoorNames(s): [’p09/door/haso107d1.01’, ’p09/door/haso107d1.02’, ’p0
MacroServer
p09/macroserver/haso107d1.01
state ON
Related Pools:
--- Pool ( local) p09/pool/haso107d1
state ON
MG: [u’d1_t01’, u’d1_c01’, u’d1_c02’]
MacroServer
p09/macroserver/haso107d1.01
state ON
Related Pools:
9
--- Pool ( local) p09/pool/haso107d1
MG: [u’d1_t01’, u’d1_c01’, u’d1_c02’]
MacroServer
p09/macroserver/haso107d1.01
Related Pools:
--- Pool ( local) p09/pool/haso107d1
MG: [u’d1_t01’, u’d1_c01’, u’d1_c02’]
state ON
state ON
state ON
Use ’SardanaStatus.py -f’ to obtain more information about the pools.
If the last command generated some encouraging output, as in the example, you may proceed.
When starting Spock for the first time, you are prompted to specify a Door name:
$ spock
MainThread
INFO
2015-03-23 16:55:41,192 TaurusRootLogger: Using "Py
Profile ’spockdoor’ does not exist. Do you want to create one now ([y]/n)?
Available Door devices from haso107d1.desy.de:10000 :
p09/door/haso107d1.01
(running)
p09/door/haso107d1.02
(running)
p09/door/haso107d1.03
(running)
Door name from the list? p09/door/haso107d1.01
The question ’Do you want to create one now?’ has been answered with ’y’ and the Door name has
been typed in: p09/door/haso107d1.
There is an option that controls how motor positions are displayed. If ShowDial is set to False, the
DIAL is not displayed. At DESY the DIAL has no meaning. If the ShowCtrlAxis flag is set to
true, the pool device names are displayed together with the aliases when using wa or wm.
p02/door/exp.01 [1]: setvo ShowDial False
p02/door/exp.01 [1]: setvo ShowCtrlAxis True
A Spock session is terminated by Ctrl-D. By default the session is terminated without asking for
confirmation. The behaviour is controlled by a configuration variable. Here is an example for a configuration file (/home/<userName>/.config/ipython/profile_spockdoor/ipython_config
"""Settings for Spock session"""
#
#
#
#
#
#
Please do not delete the next lines has they are used to check the versio
number for possible upgrades
spock_creation_version = 1.5.0
door_name = haso107d1.desy.de:10000/p09/door/haso107d1.01
import PyTango.ipython
import sardana.spock.genutils
from sardana.spock.config import Spock
config = get_config()
config.Spock.macro_server_name = ’haso107d1.desy.de:10000/p09/macroserver/h
config.Spock.door_name = ’haso107d1.desy.de:10000/p09/door/haso107d1.01’
load_subconfig(’ipython_config.py’, profile=’default’)
sardana.spock.load_config(config)
# Put any additional environment here and/or overwrite default sardana conf
config.IPKernelApp.pylab = ’inline’
config.TerminalInteractiveShell.confirm_exit = False
The variable config.TerminalInteractiveShell.confirm_exit controls the behaviour.
2.4
Exploring the system
The command lsm displays a list of motors:
p02/door/exp.01 [1]: lsm
Name
Type
Controller
Axis
-------------------- ------- ------------------------- -----dac_tk
Motor
HasyDACCtrl
1
dcm_bragg
Motor
HasyFMBOxfDCMMotorCtrl
1
dcm_parallel
Motor
HasyFMBOxfDCMMotorCtrl
2
dcm_perp
Motor
HasyFMBOxfDCMMotorCtrl
3
eh2a_mot01
Motor
HasyMotorCtrl
17
eh2a_mot02
Motor
HasyMotorCtrl
18
eh2a_mot03
Motor
HasyMotorCtrl
19
...
oh1_mot16
Motor
HasyMotorCtrl
16
slt1offs
Motor
HasySltCtrl
2
slt1vgap
Motor
HasySltCtrl
1
slt2left
Motor
HasySltCtrl
3
slt2offs
Motor
HasySltCtrl
6
slt2right
Motor
HasySltCtrl
4
slt2vgap
Motor
HasySltCtrl
5
vfm_curvature
Motor
HasyAttributeMotorCtrl
4
vfm_ellipticity
Motor
HasyAttributeMotorCtrl
5
vfm_gp_curvature
Motor
HasyAttributeMotorCtrl
12
vfm_gp_ellipticity
Motor
HasyAttributeMotorCtrl
13
vfm_gp_tilt
Motor
HasyAttributeMotorCtrl
11
vfm_gp_z
Motor
HasyAttributeMotorCtrl
10
vfm_tilt
Motor
HasyAttributeMotorCtrl
3
vfm_z
Motor
HasyAttributeMotorCtrl
2
The command wa produces a list of motors and their positions:
p02/door/exp.01 [1]: wa
Positions on 2012-09-03 15:22:48.767801
dac_tk
1.0000000000000
dcm_bragg
2.6628801728890
dcm_parallel
-0.0017544755474
dcm_perp
-0.2732704541679
eh2a_mot01
eh2a_mot02
eh2a_mot03
eh2a_mot04
-5.0000000000000
7.8699990312894
180.0000000000000
0.0000000000000
.....
vfm_gp_ellipticity
vfm_gp_tilt
vfm_gp_z
vfm_tilt
-1100.0000150400265 6060.0000828568718 6740.0108461825912 11115.00167169903
vfm_z
11490.1076866492876
The command wm displays the position of a single motor:
p02/door/exp.01 [3]: wm oh1_mot01
oh1_mot01
User
High
0.8
Current
0.18
Low
-0.8
p02/door/exp.01 [4]:
This command can also be used to display the positions of several motors:
p02/door/exp.01 [4]: wm oh1_mot01 oh1_mot02 oh1_mot03
oh1_mot01 oh1_mot02 oh1_mot03
User
High
0.8
1
3
Current
0.18
-1.02 -1.67049997616
Low
-0.8
-0.68
-3
The command lsct displays the devices of the countertimer controller:
p02/door/exp.01 [7]: %lsct
Name
Type
Controller
Axis
---------- -------------- ----------------- -----eh2a_c01
CTExpChannel
HasySIS3820Ctrl
33
eh2a_c02
CTExpChannel
HasySIS3820Ctrl
34
eh2a_c03
CTExpChannel
HasySIS3820Ctrl
35
eh2a_c04
CTExpChannel
HasySIS3820Ctrl
36
eh2a_c05
CTExpChannel
HasySIS3820Ctrl
37
eh2a_c06
CTExpChannel
HasySIS3820Ctrl
38
eh2a_c07
CTExpChannel
HasySIS3820Ctrl
39
...
oh1_c31
CTExpChannel
HasySIS3820Ctrl
31
oh1_c32
CTExpChannel
HasySIS3820Ctrl
32
oh1_t01
CTExpChannel
HasyDGG2Ctrl
1
oh1_t02
CTExpChannel
HasyDGG2Ctrl
2
lsmeas displays the measurement groups:
p02/door/exp.01 [8]: lsmeas
Active
Name
Timer Experim. channels
-------- ------ ---------- -------------------mg1
eh2a_t01 eh2a_t01, eh2a_c01
The command lsmac displays the available macros:
p02/door/exp.01 [3]: lsmac
Name
---------------- --------------------------------------------------------put
/usr/local/Python26/lib/python2.6/site-packages/sardana
get
/usr/local/Python26/lib/python2.6/site-packages/sardana
sar_demo
/usr/local/Python26/lib/python2.6/site-package
clear_sar_demo
/usr/local/Python26/lib/python2.6/site-package
load_env
/usr/local/Python26/lib/python2.6/site-packag
lsenv
/usr/local/Python26/lib/python2.6/site-packag
usenv
/usr/local/Python26/lib/python2.6/site-packag
dumpenv
/usr/local/Python26/lib/python2.6/site-packag
senv
/usr/local/Python26/lib/python2.6/site-packag
relmac
/usr/local/Python26/lib/python2.6/site-packages/
udefelem
/usr/local/Python26/lib/python2.6/site-packages/
...
mvr
/usr/local/Python26/lib/python2.6/site-packages/sa
settimer
/usr/local/Python26/lib/python2.6/site-packages/sa
set_lim
/usr/local/Python26/lib/python2.6/site-packages/sa
wa
/usr/local/Python26/lib/python2.6/site-packages/sa
uct
/usr/local/Python26/lib/python2.6/site-packages/sa
set_user_pos
/usr/local/Python26/lib/python2.6/site-packages/sa
wm
/usr/local/Python26/lib/python2.6/site-packages/sa
set_pos
/usr/local/Python26/lib/python2.6/site-packages/sa
_wm
/usr/local/Python26/lib/python2.6/site-packages/sa
mv
/usr/local/Python26/lib/python2.6/site-packages/sa
umv
/usr/local/Python26/lib/python2.6/site-packages/sa
umvr
/usr/local/Python26/lib/python2.6/site-packages/sa
pwa
/usr/local/Python26/lib/python2.6/site-packages/sa
set_lm
/usr/local/Python26/lib/python2.6/site-packages/sa
pwm
/usr/local/Python26/lib/python2.6/site-packages/sa
mstate
/usr/local/Python26/lib/python2.6/site-packages/sa
ct
/usr/local/Python26/lib/python2.6/site-packages/sa
report
/usr/local/Python26/lib/python2.6/site-packages/sa
You may retrieve information about the macros by, e.g.:
p02/door/exp.01
Type:
Base Class:
String Form:
Namespace:
File:
Definition:
[4]: ct?
Magic function
<type ’instancemethod’>
<bound method MatplotlibMTShell.ct of <IPython.Shell.Matplo
IPython internal
/usr/local/Python26/lib/python2.6/site-packages/sardana/spo
ct(shell, parameter_s=’’, name=’ct’)
Docstring:
Syntax:
ct <integ_time> ->
Count for the specified time on the active measurement group
Parameters:
integ_time : (Float) Integration time
Result:
2.5
The first scan
Before we can start a scan, we have to set some environment variables: ScanDir, ScanFile, ActiveMntGrp and possibly JsonRecorder (for the SardanaMonitor, see below). Here is an example,
of how the file name is selected.
p09/door/exp.01 [11]: senv ScanFile tst.fio
ScanFile = tst.fio
p09/door/exp.01 [12]: senv ScanDir /home/p09user/temp
ScanDir = /home/p09user/temp
p09/door/exp.01 [13]: senv JsonRecorder True
JsonRecorder = True
p09/door/exp.01 [14]: senv ActiveMntGrp mg1
ActiveMntGrp = mg1
The command lsenv displays all environment variables:
p09/door/exp.01 [15]: lsenv
Name
Value
Type
-------------- --------------------- -----ScanID
26
int
ActiveMntGrp
mg1
str
_ViewOptions
{’ShowDial’: False}
dict
JsonRecorder
True
bool
ScanFile
tst.fio
str
ScanDir
/home/p09user/temp
str
The command lsmeas displays the contents of the MeasurementGroup:
p02/door/exp.01 [24]: lsmeas
Active
Name
Timer Experim. channels
-------- ------ ---------- ----------------------------------------------mg1
eh2a_t01 eh2a_t01, sis3302_01, eh2a_vfc01, eh2a_vfc02, e
*
exp_petra
We open an additional xterm to launch a tool that displays graphics, Figure 2.5 shows the widget:
$ SardanaMonitor.py -d firstDoor
The command line argument ’-d firstDoor’ selects the first door.
This is the scan command:
p02/door/exp.01 [25]: ascan ch2_dmy01 0 1 10 0.1
Operation will be saved in None (fio)
Scan #38 started at Wed Nov 14 16:01:33 2012. It will take at least 0:00:01
Moving to start positions...
#Pt No
dt
ch2_dmy01 eh2a_t01 sis3302_01 eh2a_vfc01 eh2a_vfc02
0
0.063441
0
0.1
(2048,)
0.292209
0.283309
1
0.356575
0.1
0.1
(2048,)
0.292259
0.283359
2
0.549326
0.2
0.1
(2048,)
0.292309
0.283409
3
0.740467
0.3
0.1
(2048,)
0.292359
0.283459
4
0.940038
0.4
0.1
(2048,)
0.292409
0.283509
5
1.13406
0.5
0.1
(2048,)
0.292453
0.283553
6
1.32566
0.6
0.1
(2048,)
0.292565
0.283609
7
1.51697
0.7
0.1
(2048,)
0.292657
0.283657
8
1.70895
0.8
0.1
(2048,)
0.292703
0.283703
9
1.89342
0.9
0.1
(2048,)
0.292761
0.283761
10
2.10928
1
0.1
(2048,)
0.292807
0.283807
Operation saved in /home/p02user/temp/tst_00038.fio (fio)
Scan #38 ended at Wed Nov 14 16:01:36 2012, taking 0:00:02.280033. Dead tim
The graphics output can be found in figure 2.5.
Figure 2.1: The SardanaMonitor widget
Figure 2.2: The SardanaMonitor graphics output
Chapter 3
IPython Features
IPython is an interactive shell for Python. Spock is an IPython implementation to operate the Pool
and the Macroserver.
3.1
Miscellaneous Items
3.1.1 TAB-Completion
You may start to type the name of an IPython object then use TAB and IPython tries to complete
the name. The TAB may also be used to display the members of an object, e.g.:
p09/door/exp.01 [27]: exp_mot01.<TAB>
Display all 199 possibilities? (y or n)
3.1.2 Run a Python Script from within IPython
The following lines are from an IPython session. First the contents of demo.py is displayed, then
the file is executed and it is shown that the variable ’a’ is in the IPython namespace (even without
’run -i’).
In [5]: !cat demo.py
#!/usr/bin/env python
a = 12
In [6]: run demo.py
In [7]: a
Out[7]: 12
3.1.3 Show the Local Variables of an IPython session
p09/door/exp.01 [20]: a = 12
p09/door/exp.01 [21]: whos
Variable
Type
Data/Info
---------------------------a
int
12
18
3.1.4 Display Information about an Object
p09/door/exp.01 [26]: lsenv?
p09/door/exp.01 [27]: exp_dmy01??
3.1.5 Execute a Shell Command
p09/door/exp.01 [28]: ! hostname
haso107klx
p09/door/exp.01 [38]: a = ! hostname
p09/door/exp.01 [39]: a[0]
Result [39]: ’haso107klx’
3.2
Command Shorthands
Here is the whole sequence of how to use a IPython variable as a shorthand for commands:
p09/door/exp.01 [1]: wm exp_mot01 exp_mot02 exp_mot03 exp_mot04
exp_mot01 exp_mot02 exp_mot03 exp_mot04
User
High
500
200
500
180
Current
1.0
-160.002
362.0
-1.0
Low
-500
-200
-500
-180
p09/door/exp.01 [2]: macro slt1 1
Macro ‘slt1‘ created. To execute, type its name (without quotes).
Macro contents:
_ip.magic("wm exp_mot01 exp_mot02 exp_mot03 exp_mot04")
The first line ’wm exp mot01 exp mot02 exp mot03 exp mot04’ displays the position of four motors. The command ’macro slt1 1’ gives the command on line 1 the name slt1. Such a definition
may comprise more than on command line. Use macro? for the syntax.
The variable slt1 may be stored for being used in other Spock sessions:
p09/door/exp.01 [3]: store slt1
Stored ’slt1’ (Macro)
p09/door/exp.01 [4]: store
Stored variables and their in-db values:
__spock_store
-> {’database_list’: {’haso107klx’: None}}
__tango_store
-> {’database_list’: {’haso107klx.desy.de’: None}
slt1
-> IPython.macro.Macro(u’_ip.magic("wm exp_mot01
The command ’store’ tells you which IPython variables have been defined.
A variable can be deleted by:
p09/door/exp.01 [5]: store -d slt1
p09/door/exp.01 [6]: store
Stored variables and their in-db values:
__spock_store
-> {’database_list’: {’haso107klx’: None}}
__tango_store
-> {’database_list’: {’haso107klx.desy.de’: None}
3.3
Startup Scripts
The directory ˜/.config/ipython/profile_spockdoor/startup contains Python scripts
that are executed during the start of Spock. They are executed in lexical order. For Spock sessions
using the secondDoor profile, the startup directory is ˜/.config/ipython/profile_secondDoor/sta
The following example, 00-start.py, shows how the automatic session logging is enabled and how
the motor attributes are logged:
#!/usr/bin/env python
#
# file name ˜/.config/ipython/profile_spockdoor/startup/00-start.py
#
import os, time, atexit
#
# the exit handler copies the log file to /online_dir
# saving the old version
#
def _spockExitHandler():
if os.path.exists( ’/online_dir/ipython_log.py’):
os.system( ’vrsn -nolog -s /online_dir/ipython_log.py’)
#
# get the ipython shell
#
ip = get_ipython()
#
# -o log include output
# -r raw log ’wm exp_dmy01’ instead of get_ipython().magic(u’wm exp_dmy01’
# -t with timestamp
#
if os.path.exists( "%s/ipython_log.py" % os.environ[’PWD’]):
os.system( "vrsn -nolog -s %s/ipython_log.py" % os.environ[’PWD’])
os.system( "/bin/rm %s/ipython_log.py" % os.environ[’PWD’])
ip.magic( ’%logstart -o -r -t’)
#
# store the current motor attributes in a new version of /online_dir/MotorL
#
# -x execute
# -q quiet
# -c the name of the calling program is written to the log file
#
os.system(’MotorLogger.py -x -q -c spock’)
os.system( "ln -sf %s/ipython_log.py /online_dir/ipython_log.py" % os.envir
#
# write some text to the log file
#
# ip.logger.log_write( unicode( "# some text \n"))
#
# register exit handler
#
atexit.register( _spockExitHandler)
Chapter 4
Scans
This chapter lists some features that can be used in the scans.
4.1
Environment variables for scans
All scan commands in this chapter require a few environment variables to be set:
p17/door/exp.01 [8]: senv ActiveMntGrp mg_haspp17
ActiveMntGrp = mg_haspp17
p17/door/exp.01 [11]: senv ScanDir /home/p17user/temp
ScanDir = /home/p17user/temp
p17/door/exp.01 [12]: senv ScanFile FirstTest.fio
ScanFile = FirstTest.fio
p17/door/exp.01 [13]: senv JsonRecorder True
JsonRecorder = True
p09/door/exp.01 [7]: senv FlagFioWriteMotorPositions True
FlagFioWriteMotorPositions = True
ActiveMntGrp The active measurement group. More information about measurement groups
can be found in 4.3.
ScanDir The directory where the output files are written.
ScanFile The prefix of the output file name. The extension .fio selects the FIO recorder.
Otherwise the data is written in Spec (SPEC, Certified Scientific Software, www.certif.com)
format.
JsonRecorder If enabled, allows the data to be sent to the spectraDoor.
FlagFioWriteMotorPositions If true, the motor positions will be written to the .fio files.
FioAdditions The name of a Python script which generates output being stored in the header
of the .fio files. The output is a json-encoded list of strings or a dictionary. Lists are stored
in the comment section, the dictionaries in the parameter section. This feature is disabled
by deleting the environment variable FioAdditions or by setting it to None. Example can be
found here 4.1.1.
22
4.1.1 FioAdditions Example
To inject some data into the .fio header, the FioAdditions environment variable has to point to a
Python file generating json-encoded output.
p09/door/haso107d1.01 [4]: lsenv
Name
---------------------------- --------------------------------------------...
FioAdditions
/onli
...
The output of FioAdditions can be
• a list of strings
• a dictionary
• a list containing a list of strings and a dictionary
• a list containing a list of strings
• a list containing a dictionary
Lists of strings are stored in the comment section of the header, dictionaries in the parameter section. Here is an example for a FioAdditions file:
#!/usr/bin/env python
#
# this script generated json-encoded output to be filled into the
# header of a .fio file.
# allowed combinations: list, dict, [list], [dict], [list, dict], [dict, li
#
import json
argout = []
dct = {}
dct[’par1’] = "value1"
dct[’par2’] = "value2"
argout.append( dct)
lst = []
lst.append( ’line1’)
lst.append( ’line2’)
argout.append( lst)
print json.dumps( argout)
4.2
Scan Commands
• ascan, a2scan, a3scan, a4scan, amultiscan Absolute motor scans on a 1D path. The scan
interval is specified by supplying a start and end position for each motor. After a scan has
ended the motor(s) remain at the final scan position.
p09/door/exp.01 [37]: ascan exp_dmy01 0 1 20 0.1
p09/door/exp.01 [38]: a2scan exp_dmy01 0 1 exp_dmy02 5 7 20 0.1
’ascan’: motor exp dmy01 is scanned from 0 to 1, the number of intervalls is 20, the sample
time is 0.1.
’a2scan’: motor exp dmy01 is scanned from 0 to 1 while exp dmy02 is scanned from 5 to 7.
monitor (preset) mode: The monitor counts are specified as a negative sample time. A value
of -1 corresponds to 1000000 monitor counts.
• dscan, d2scan, d3scan, d4scan, dmultiscan Relatine motor scans on a 1D path. The scan
interval is specified by supplying the offset for the start and end position relative to the current
motor position. After a scan has ended the motor(s) move back to the before-scan position(s).
p09/door/exp.01 [37]: dscan exp_dmy01 -1 1 20 0.1
• timescan Scan without motor.
p09/door/exp.01 [37]: timescan 0 10 20 0.1
The dummy scan functionality can also be performed using a dummy motor.
• fscan. N-dimensional scan along user defined paths. The motion path for each motor is
defined through the evaluation of a user-supplied function that is evaluated as a function of
the independent variables.
– Independent variables are supplied through the indepvar string. The syntax for indepvar
is ”x=expresion1,y=expresion2,...”.
– If no indep vars need to be defined, write ”!” or ”*” or ”None”
– Motion path for motor is generated by evaluating the corresponding function ’func’.
– Count time is given by integ time. If integ time is a scalar, then the same integ time is
used for all points. If it evaluates as an array (with same length as the paths), fscan will
assign a different integration time to each acquisition point.
– If integ time is positive, it specifies seconds and if negative, specifies monitor (preset)
counts (times 10**6).
IMPORTANT Notes:
– No spaces are allowed in the indepvar string.
– All funcs must evaluate to the same number of points
EXAMPLE: fscan x=[1,3,5,7,9],y=arange(5) motor1 x**2 motor2 sqrt(y*x-3) 0.1
Arguments: independent variables, list of motor and path curves, integration time.
• mesh A 2D grid scan.
4.2.1
Example: ascan
The command ascan executes a motor scan using absolute positions.
The command for performing the scan looks like:
p17/door/exp.01 [31]: ascan exp_dmy01 0 1 2 0.1
Operation will be saved in None (fio)
Scan #384 started at Tue Sep 11 13:13:24 2012. It will take at least 0:00:0
Moving to start positions...
#Pt No
dt
exp_dmy01 exp_t01
exp_c01
0
0.0107629
0
0.1
29
1
0.130456
0.5
0.1
14
2
0.250917
1
0.1
5
Operation saved in /home/p17user/temp/FirstTest_00384.fio (fio)
Scan #384 ended at Tue Sep 11 13:13:25 2012, taking 0:00:00.375624. Dead ti
exp dmy is the motor to be scanned, 0 and 1 are the limits, 2 is the no. of intervals and 0.1 is the
sample time.
If ScanFile has the extension .dat, data are saved using the spec format:
p17/door/exp.01 [32]: senv ScanFile FirstTest.dat
ScanFile = FirstTest.dat
p17/door/exp.01 [33]: ascan exp_dmy01 0 1 2 0.1
Operation will be saved in /home/p17user/temp/FirstTest.dat (Spec)
Scan #385 started at Tue Sep 11 13:15:21 2012. It will take at least 0:00:0
Moving to start positions...
#Pt No
dt
exp_dmy01 exp_t01
exp_c01
0
0.0105131
0
0.1
1
1
0.143438
0.5
0.1
0
2
0.266759
1
0.1
0
Operation saved in /home/p17user/temp/FirstTest.dat (Spec)
Scan #385 ended at Tue Sep 11 13:15:21 2012, taking 0:00:00.380875. Dead ti
The file FirstTest.dat contains:
#S 385 ascan exp_dmy01 0.0 1.0 2 0.1
#U p17user
#D 1347362121.0
#C Acquisition started at Tue Sep 11 13:15:21 2012
#N 5
#L Pt_No dt exp_dmy01 exp_t01 exp_c01
0 0.0105130672455 0.0 0.1 1.0
1 0.143438100815 0.5 0.1 0.0
2 0.266759157181 1.0 0.1 0.0
#C Acquisition ended at Tue Sep 11 13:15:21 2012
4.3
Configuring the MeasurementGroup
Among other things, the MeasurementGroup (MG) contains a list of devices and the integration
time. This information is used by the general scan class to execute measurements. The SardanaStartup.py creates a MG in the Pool. The name is mg_<nodeName>.
The Spock command lsmeas displays the available MGs:
p02/door/haspp02ch1.01 [1]: lsmeas
Active
Name
Timer Experim. channels
-------- --------------- --------- ------------------mg_haspp02ch1
oh1_t01 oh1_t01, eh1a_c01
The MG can be configured in several ways. The following sections explain how this is done.
4.3.1 Configuring the MeasurementGroup with the ComponentSelector
The recommended way to select devices for a measurement is the ComponentSelector:
p02user@haspp02ch1:˜$ nxscomp_selector
The ComponentSelector takes NeXus components into account. Details can be found in section
7.1. It is planned to create a command line interface with the same functionality.
4.3.2 Configuring the MeasurementGroup with expconf
The MG can be configured by a Taurus widget which was also created at ALBA. It is invoked from
the Spock command line: expconf, see figure 4.1. Items can be selected or de-selected by the +
and - buttons. So far expconf cannot configure SCAs.
p09/door/exp.01 [6]: %expconf
Figure 4.1: The expconf widget
4.3.3 Configuring the MeasurementGroup using macros from Spock
The MG can be configured from Spock using macros.
defmeas Create a new measurement group. First channel in channel list MUST be an internal sardana channel. At least one channel MUST be a Counter/Timer (by default, the first
Counter/Timer in the list willbecome the master).
udefmeas Deletes an existing measurement group.
change mg Changes an existing measurement group. Usage:
p09/door/exp.01 [6]: %change_mg -a <addflag> -g <mgName> -t <timer> -e
– All options are optional (except timer if -a is False (clear and create MG)
– If not MG name is done, the active one is changed.
– If not addFlag (true or false) is given, the MG will be cleared (addFlag False by default)
and created with the given elements.
– ”Elements in a list have to be given separated by ,: eg. -c exp ct01,exp ct02, black
spaces are not allowed.
4.3.4 Configuring the MeasurementGroup using a script
The script SardanaChMg.py configures the measurement group, e.g.
SardanaChMg.py -g mg haspp02ch2 -t exp t01 -c exp c01,exp adc01,exp vfc01
-m exp mca01
If there is only one local MeasurementGroup, it can be omitted.
If you want to add a new measurement group the option -p, with the name of the pool in wich the
MeasurementGroup will be added, has to be used, e.g.
SardanaChMg.py -p my/pool/device -g mg notexisting -t exp t01 -c exp c01,exp
-m exp mca01
-c A comma-separated list of counters (countertimer devices in Sardana-speak). Counters are
e.g.: -c exp c01,exp adc01,exp vfc01,exp petra,exp pilatus,sca exp mca01 0 8191. Notice
that there are no blanks near the commas.
The name sca exp mca01 0 8191 specifies a SCA. A corresponding Pool controller which
drives a single device is created by this command. The user has to ensure that each MCA
that is used by a SCA is selected by the -m option, see below. For sca exp mca01 0 8191
we have to supply a -m exp mca01. The last two numbers (0, 8191) indicate the limits of the
RoI (minimum, maximum) used by the SCA.
-e A comma-separated list of extra timers. They are operated in parallel to the master timer.
This parameter is optional.
-g The name of the MeasurementGroup.
-m A comma-separated list of MCAs, e.g.: -m exp mca01,exp mca02,exp mca03
-n A comma-separated list of counters which are used during the measurement but not displayed.
-p Selects the Pool. This parameter is optional. If the corresponding MeasurementGroup
exists, the Pool is found by the script.
-q Selects Pilatus detectors, e.g. pilatus300k.
-t Selects the master timer, mandatory.
4.3.5 Adding any Tango Attribute as Counter in the MeasurementGroup
Any Tango attribute from any Tango device can be added as a counter in the MeasurementGroup,
in this way the value of the attribute will be read and displayed/stored at each point of the scan.
expconf or the Spock macros can be used for creating a MeasurementGroup with any of these, so
called, ’external’ channels or adding them to an existing one.
In expconf one have to select the item ’Other...’ in the menu with all the available channels, this will
open a device tree with all the tango devices in the TANGO HOST. If the attribute does not belong
to a Tango device with the same TANGO HOST you will not see the device and correspondingly
the attibute in the tree, but you can still use this tool for adding it. The way is clicking in the button
’+’ below the window with the device tree. This will create a line with ’(Empty)’ in the window
below. By double clicking in this line you will be able to edit it, so write there the complete adress
of the attribute, ej.:
haspp09mono:10000/p09/lks336tempctrl/exp.01/OutputA
Using the spock macros for adding this attribute you will give as name for this ’counter’ the tango
device name plus attribute name. You have to specify the TANGO HOST if this is different than
the one in which you are working. Ex.
p09/door/haso111tb [1]: defmeas mgtest exp_t01 haspp09mono:10000/p09/lks336
Other way is to add this attribute permanently in Sardana as a PseudoCounter, that will be included
in the Measurement Group as any other counter. The corresponding entry in the online.xml file is:
<device>
<name>anyname_attributename</name>
<type>counter</type>
<module>tangoattributectctrl</module>
<device>my/tango/device</device>
<control>tango</control>
<hostname>hasppXY:10000</hostname>
</device>
where ¡name¿ has to be composed by any name (ex. ’anyname’) followed by and the name of the
attribute to be read, and ¡device¿ is the name of the device this attribute belongs to. ¡module¿ has
to be ’tangoattributectctrl’.
4.3.6 Adding MCA RoIs as Counter in the MeasurementGroup
As described in section 11.3.4 the counts in defined RoIs can be used as counters in Sardana. These
counters can be introduced in the measurement group like any other counter, the only important
point that has to be taken into account is that, in addition to the counters with the counts, an
additional counter (so called ’master’) has to be also included in the Measurement Group. The
name of this counter starts with ’mca8715roi’ and ends with ’ master’, between these two parts the
last field of the tango device of the corresponding mca device is added. For example, for adding a
counter from the mca device ’test/mca/ex.01’, the master counter will appears in Sardana with the
name ’mca8715roiex.01 master’.
4.4
SardanaMonitor
The SardanaMonitor displays the data 0d aund 1d that are created during scans. It is started with
the command:
$ SardanaMonitor.py -d firstDoor
$ SardanaMonitor.py -d secondDoor
$ SardanaMonitor.py -d thirdDoor
The command line argumnet ’-d firstDoor’ selects the first door.
Figure 4.4 shows the SardanaMonitor widget.
Figure 4.2: SardanaMonitor Widget
The SSA button launches a simple-scan-analysis on the column that corresponds the SignalCounter
(MacroServer environment variable).
The check-button DisplayAll determines whether one or all MCAs are displayed. Figure 4.4 shows
the graphics output, if at most one MCA is displayed. The MCA uses the bottom half of the graphics
window.
Figure 4.4 shows the case, if DisplayAll is selected. The graphics window is divided in equally
sized parts for counters and MCAs.
Notice that it is possible to specify whether counters are displayed during the scans. See section
??.
Figure 4.3: SardanaMonitor graphics output, one MCA only
4.5
Hooks
Section 6.5.9 shows how specific hooks can be used to customize scans. Section 4.6 explains how
to define hooks that are used for all scans. Section 4.7 says how general hooks can controll an
absorber.
4.6
General hooks and conditions
It is possible to define certain functions which are executed for each scan. The following features
are included: hooks, repeat-a-point-in-condition and on stop. The code below, $HOME/sardanaMacros/gen
(4.6), is a template. This is the Spock interface:
• gf status gives an overview
p09/door/haso107d1.01 [1]: %gf_status
general features status:
general hooks feature is disabled
general condition feature is disabled
general on_stop feature is disabled
• gh enable, gh disable, gh isEnabled, gh setSelector, gh getSelector control the general
hooks feature:
p09/door/haso107d1.01 [1]: %gh_enable
enable general hooks feature
Figure 4.4: SardanaMonitor graphics output
p09/door/haso107d1.01 [2]: %gh_disable
disable general hooks feature
p09/door/haso107d1.01 [3]: %gh_isEnabled
general hooks feature is disabled
p09/door/haso107d1.01 [4]: %gh_setSelector absorber
gh_setSelector to absorber
p09/door/haso107d1.01 [5]: %gh_getSelector
selector absorber
p09/door/haso107d1.01 [16]: %gh_list
#!/usr/bin/env python
"""
The general hooks and condition function for controlling an absorber.
The output of the SignalCounter has to be between countsMin and countsM
Otherwise the absorber is moved by delta, in the range between absMin a
Environment variables:
SignalCounter
absInfo, e.g.: senv absInfo ’absorber:d1_mot01,absDelta:0.1,absMin:0,
"""
...
• gc enable, gc disable, gc isEnabled, gc setSelector, gc getSelector control the general
repeat-a-point-on-condition feature. The macro ppDiff removes the doubles from the scan
data. It is executed after the scan is finished.
p09/door/haso107d1.01 [6]: %gc_enable
enable general conditions feature
p09/door/haso107d1.01 [7]: %gc_disable
disable general conditions feature
p09/door/haso107d1.01 [8]: %gc_isEnabled
general conditions feature is disabled
p09/door/haso107d1.01 [9]: %gc_setSelector align
condition-selector to align
p09/door/haso107d1.01 [10]: %gc_getSelector
condition-selector align
• gs enable, gs disable, gs isEnabled, gs setSelector, gs getSelector control the general on stop
feature.
p09/door/haso107d1.01 [11]: %gs_enable
enable general on_stop feature
p09/door/haso107d1.01 [12]: %gs_disable
disable general on_stop feature
p09/door/haso107d1.01 [13]: %gs_isEnabled
general on_stop feature is enabled
p09/door/haso107d1.01 [14]: %gs_setSelector align
on_stop selector to align
p09/door/haso107d1.01 [15]: %gs_getSelector
on_stop selector align
These macros are part of handleGeneralFunctions.py which is distributed at each beamline.
Notice that it is possible to set selectors. The selector is a string meant to be used by the functions.
#!/usr/bin/env python
#
# file name: $HOME/sardanaMacros/general_functions.py
#
# this file contains the general hook functions and the
# condition function
#
# the hooks are used in gscan.py
# the hooks are enabled/disabled by the macros defined in handleGeneralFunc
#
import PyTango
def gh_pre_scan(pmacro):
#
# this is how the selector could be used
#
if __builtins__.has_key( ’gh_selector’):
if __builtins__[ ’gh_selector’] == "align":
pmacro.output( "general-pre-scan hook for %s" % __builtins__[ ’
elif __builtins__[ ’gh_selector’] == "absorber":
pmacro.output( "general-pre-scan hook for %s" % __builtins__[ ’
else:
pmacro.output( "general-pre-scan hook, %s" % __builtins__[ ’gh_
else:
pmacro.output( "general-pre-scan hook, no selector")
def gh_pre_move(pmacro):
pmacro.output( "Scan pre move")
def gh_post_move(pmacro):
pmacro.output( "Scan post move")
def gh_pre_acq(pmacro):
pmacro.output( "Scan pre acq")
def gh_post_acq(pmacro):
pmacro.output( "Scan post acq")
def gh_post_scan(pmacro):
pmacro.output( "Scan post scan")
# Function for checking if scan point has to be repeated
def check_condition(pmacro):
import random
pmacro.output("Scan check_condition")
if random.random() > 0.5:
pmacro.output("Repeat point")
return 1
else:
pmacro.output("DO NOT Repeat point")
return 0
#
# the general stop function
#
def general_on_stop(pmacro):
if __builtins__.has_key( ’gs_selector’):
#
# == ’scan’ if we are in a scan, prepared in gscan.py
#
if __builtins__[ ’gs_selector’] == "scan":
pmacro.output( "general_on_stop for %s" % __builtins__[ ’gs_sel
#
# == ’general’ for all other macros, mv, wa, etc.
#
elif __builtins__[ ’gs_selector’] == "general":
pmacro.output( "general on_stop for %s" % __builtins__[ ’gs_sel
else:
pmacro.output( "general on_stop, %s" % __builtins__[ ’gs_select
else:
pmacro.output("General on_stop is called without selector")
4.7
Absorber scans using general hooks and conditions
The following code in an implementation of absorber scans using general hooks and conditions.
The procedure is controlled by the environment variables SignalCounter and absInfo and by the
hooks selector absorber.
#!/usr/bin/env python
"""
The general hooks and condition function for controlling an absorber.
The output of the SignalCounter has to be between countsMin and countsMax.
Otherwise the absorber is moved by delta, in the range between absMin and a
Environment variables:
SignalCounter
absInfo, e.g.: senv absInfo ’absorber:d1_mot01,absDelta:0.1,absMin:0,absM
"""
import PyTango
import HasyUtils
def prepare_absorber_scan( pmacro):
#
# this index is incremented during the scan
#
pmacro.output( "prepare_absorber_scan: ")
pmacro.indexScan = 1
pmacro.signalCounter = pmacro.getEnv( ’SignalCounter’)
if not pmacro.signalCounter:
pmacro.output( "prepare_absorber_scan: no SignalCounter")
return 0
pmacro.signalCounterDevName = HasyUtils.getDeviceNameByAlias( pmacro.si
absInfo = pmacro.getEnv( ’absInfo’)
pmacro.absDct = {}
for pair in absInfo.split(’,’):
k,v = pair.split( ’:’)
pmacro.absDct[k] = v
for i in [’absorber’, ’absDelta’, ’countsMin’, ’countsMax’, ’absMin’, ’
if i not in pmacro.absDct.keys():
pmacro.output( "prepare_absorber_scan: absInfo does not contain
return 0
for i in [’absDelta’, ’countsMin’, ’countsMax’, ’absMin’, ’absMax’]:
pmacro.absDct[ i] = float( pmacro.absDct[ i])
try:
pmacro.absorberProxy = PyTango.DeviceProxy( pmacro.absDct[ ’absorbe
except:
pmacro.output( "prepare_absorber_scan: failed to create a proxy to
return 0
def prepare_scan( pmacro):
#
# this is how the selector is used
#
if __builtins__.has_key( ’gh_selector’):
if __builtins__[ ’gh_selector’] == "absorber":
prepare_absorber_scan( pmacro)
else:
pass
def getSignal( pmacro):
"""
extract the current value of SignalCounter
"""
#
# if signalCounter hasn’t been found in prepare_absorber_scan, return 0
#
if not pmacro.signalCounter:
return 0
lst = pmacro.data.records
if len(lst) == 0:
pmacro.output( "getSignal: no data")
return None
flag = 0
for k in lst[-1].data.keys():
if k.find( pmacro.signalCounterDevName) >= 0:
return float( lst[-1].data[k])
pmacro.output( "getSignal: failed to find data for %s" % pmacro.signalC
return None
def check_condition_absorber( pmacro):
signal = getSignal( pmacro)
if signal is None:
pmacro.output("check_condition_absorber: no data")
return 0
#
# signal is too big, move absorber into the beam
#
if signal > pmacro.absDct[ ’countsMax’]:
pmacro.output( "signal %g > max %g" % (signal, pmacro.absDct[ ’coun
if (pmacro.absorberProxy.Position + pmacro.absDct[ ’absDelta’]) > p
pmacro.output( "check_condition: absorber at the limit %g " % p
return None
pmacro.output( "check_condition: move %s to %g" %
(pmacro.absDct[ ’absorber’], pmacro.absorberProxy.Posi
pmacro.mvr( pmacro.absDct[ ’absorber’], pmacro.absDct[ ’absDelta’])
argout = 1
#
# signal is too small, move absorber out of the beam
#
elif signal < pmacro.absDct[ ’countsMin’]:
pmacro.output( "signal %g < min %g" % (signal, pmacro.absDct[ ’coun
if (pmacro.absorberProxy.Position - pmacro.absDct[ ’absDelta’]) < p
pmacro.output( "check_condition: absorber at the limit %g " % p
return None
pmacro.output( "check_condition: move %s to %g" %
(pmacro.absDct[ ’absorber’], pmacro.absorberProxy.Posi
pmacro.mvr( pmacro.absDct[ ’absorber’], -pmacro.absDct[ ’absDelta’]
argout = 1
#
# signal is ok
#
else:
pmacro.output( "signal %g [%g, %g] ok" % (signal, pmacro.absDct[ ’c
argout = 0
return argout
#
#--#
# the hooks
#
def gh_pre_scan(pmacro):
pmacro.output( "general-pre-scan hook ")
if not hasattr( pmacro, ’indexScan’):
prepare_scan(pmacro)
def gh_pre_move(pmacro):
pmacro.indexScan = pmacro.indexScan + 1
pmacro.output( "Scan pre move, indexScan %d" % pmacro.indexScan)
def gh_post_move(pmacro):
pmacro.output( "Scan post move")
def gh_pre_acq(pmacro):
pmacro.output( "Scan pre acq")
def gh_post_acq(pmacro):
pmacro.output( "Scan post acq")
def gh_post_scan(pmacro):
pmacro.output( "Scan post scan")
#
# the condition function
#
def check_condition(pmacro):
pmacro.output("Scan check_condition")
#
# indexScan is checked to see whether everything has been prepared
#
if not hasattr( pmacro, ’indexScan’):
prepare_scan(pmacro)
if
__builtins__.has_key( ’gh_selector’) and \
__builtins__[ ’gh_selector’] == "absorber":
return check_condition_absorber( pmacro)
return 0
#
# the general stop function
#
def general_on_stop(pmacro):
if __builtins__.has_key( ’gs_selector’):
#
# == ’scan’ if we are in a scan, prepared in gscan.py
#
if __builtins__[ ’gs_selector’] == "scan":
pmacro.output( "general_on_stop for %s" % __builtins__[ ’gs_sel
#
# == ’general’ for all other macros, mv, wa, etc.
#
elif __builtins__[ ’gs_selector’] == "general":
pmacro.output( "general on_stop for %s" % __builtins__[ ’gs_sel
else:
pmacro.output( "general on_stop, %s" % __builtins__[ ’gs_select
else:
pmacro.output("General on_stop is called without selector")
4.8
Repeating scan points, specific scan class (XMCD)
This section describes how to run a scan repeating the measurement at each point a given number
of times. The user can define what should be done before any measurement is performed.
The general macro for repeating points is called:
ascan_repeat
and is available at each beamline.
The arguments of this macro are the same as for ascan except the last one, which indicates the
number of times should be measured at each point. Ex.
p09/door/haso111tb [68]: %ascan_repeat exp_mot01 1 10 5 1 2
ScanDir is not defined. This operation will not be stored persistently. Use
Scan #12 started at Thu Mar 5 09:59:25 2015. It will take at least 0:02:23
Moving to start positions...
#Pt No
exp_mot01 exp_t01
Position
dt
0
1
1
1
1.1593
1
1
1
1
3.43973
2
3
1
3
5.93795
3
3
1
3
8.43999
4
5
1
5
10.9357
5
5
1
5
13.4416
6
6
1
6
15.9429
7
6
1
6
18.4389
8
8
1
8
20.9424
9
8
1
8
23.4402
10
10
1
10
25.9409
11
10
1
10
28.441
Scan #12 ended at Thu Mar 5 09:59:55 2015, taking 0:00:29.792336.Dead time
where each point is repeated twice.
The actions to be performed between the measurements at each point have to be defined in a hook of
the ascan repeat macro. A template macro showing hos to implement this, ascan repeat example,
is provided with the rest of the macros, and can be found in the file scan repeatpoints user.py in
the macros directory.
"""
Macro library containning user macros using
Available Macros are:
ascan_repeat_example
"""
__all__ = ["ascan_repeat_example"]
__docformat__ = ’restructuredtext’
import os
import copy
import datetime
import numpy
the scan_repeat macro.
from
from
from
from
sardana.macroserver.msexception import UnknownEnv
sardana.macroserver.macro import *
sardana.macroserver.scan import *
sardana.util.motion import Motor, MotionPath
rep_counter = 0
class HookPars:
pass
def hook_pre_acq(self, hook_pars):
global rep_counter
self.output(str(rep_counter))
# Implement actions depending on the rep_counter value: 0 -> first time
if rep_counter % 2 == 0:
self.output("switch magnet to ’North’")
else:
self.output("switch magnet to ’South’")
if rep_counter < (hook_pars.nb_repeat - 1):
rep_counter = rep_counter + 1
else:
rep_counter = 0
class ascan_repeat_example(Macro):
param_def = [
[’motor’,
[’start_pos’,
[’final_pos’,
[’nr_interv’,
[’integ_time’,
[’nb_repeat’,
]
Type.Moveable,
None, ’Moveable to move’],
Type.Float,
None, ’Scan start position’],
Type.Float,
None, ’Scan final position’],
Type.Integer, None, ’Number of scan intervals’],
Type.Float,
None, ’Integration time’],
Type.Integer, None, ’Number of repetitions per point’
def run(self, motor, start_pos, final_pos, nr_interv, integ_time, nb_re
macro, pars = self.createMacro(’ascan_repeat’, motor, start_pos, fi
# parameters for scan hook function
hook_pars = HookPars()
hook_pars.nb_repeat = nb_repeat
f = lambda : hook_pre_acq(self, hook_pars)
macro.hooks = [
(f, ["pre-acq"]),
]
self.runMacro(macro)
In the function hook pre acq the users can define any action, based on the number of the repeatition
is needed (variable rep counter).
4.9
Repeating scan points conditionally, specific scan class
This section explains how one can define an scan in which points can be repeated depending on a
defined condition.
The condition has to be defined in a function called check condition belonging to the scan macro.
In the example below the function check condition does not contain any condition, this is the part
that has to be implemented for each specific case.
#!/usr/bin/env python
"""
Macro library containing an example of an scan macro with a check condi
"""
__all__ = ["ascan_checkcondition_example"]
__docformat__ = ’restructuredtext’
import os
from sardana.macroserver.macro import *
from sardana.macroserver.scan import *
import sys
sys.path.append("/usr/share/pyshared/sardana/sardana-macros/DESY_general")
from scan_checkifrepetition import aNscanCheck
class ascan_checkcondition_example(aNscanCheck, Macro):
"""Do an absolute scan of the specified motor checking a condition for
repeating points. """
param_def = [
[’motor’,
[’start_pos’,
[’final_pos’,
[’nr_interv’,
[’integ_time’,
]
Type.Moveable,
None, ’Moveable to move’],
Type.Float,
None, ’Scan start position’],
Type.Float,
None, ’Scan final position’],
Type.Integer, None, ’Number of scan intervals’],
Type.Float,
None, ’Integration time’]
def prepare(self, motor, start_pos, final_pos, nr_interv, integ_time,
**opts):
self._prepare([motor], [start_pos], [final_pos], nr_interv, integ_t
def check_condition(self):
check_flag = 0
# Define here your condition.
# Set check_flag to 1 if the condition requires repetion of the poi
return check_flag
Chapter 5
Spock
Spock uses the IPython magic command system to implement commands. This section gives some
explanations.
5.1
Profiles
Spock is communicating to a Door. A Door knows the MacroServer to talk to and the MacroServer
has a Pool assigned to it. It has already been explained in ?? how Spock is started for the first time.
The user selects a Door and the file ˜/.config/profile_spockdoor/ipython_config.py
keeps track of the selection. From then on, every session that is invoked by the simple command
’spock’ uses this profile with the corresponding Door.
To start a spock session which communicates with a different Door use the command:
$ spock --profile=secondDoor
A new profile is created and you are prompted again for a Door name. In the future you may use
one of these commands without further questions:
$ spock
$ spock --profile=secondDoor
5.2
Interrupting Macro Execution
CTRL-C stops the execution of a macro. If this macro implies the movement of any motor, first of
all the movements are stopped and then the macro. Even if the macro is paused it will be stopped.
5.2.1
Callback function
By default when a macro is interrupted by Ctrl-C the function Macro.on stop is called. If you
want the macro to perform any specific action when interrupted, you can overwritte the default
Macro.on stop function implementing on stop in your own macro.
One can force the macro to stop at any specific point of the execution after a Ctrl-C is done. You
need to put a call to the function self.checkPoint() at the point where you want the macro to be
stopped after the Ctrl-C. Usually this is not needed to be used.
42
5.3
Access to Tango Devices in Spock
The Pool device proxies (motors, counters, timers, ...) are directly accessible in spock. With the
general ipython command whos the list of these devices is displayed. In this way one can access
all attributes and commands of the devices. Writing the name of the device and pressing ’TAB’ the
list of attributes and commands of this device is displayed.
For example, for reading the attribute position of a motor (motoms1) or changing it (moving the
motor):
p09/door/exp.01 [29]: exp_dmy01.position
Result [29]: 3.0
p09/door/exp.01 [30]: exp_dmy01.position = 4
p09/door/exp.01 [31]: exp_dmy01.position
Result [31]: 4.0
5.4
Environment Variables
Environment variables transfer information between the client (Spock) and the MacroServer. Once
an environment variable is defined it remains valid for the next Spock sessions.
An environment variable is set with the senv command:
p09/door/exp.01 [2]: senv ScanFile "FirstTest.fio"
ScanFile = FirstTest.fio
The command lsenv lists all available environment variables:
p09/door/exp.01 [1]: lsenv
Name
֒→
֒→ Value
Type
--------------------֒→ -------------------------------------------------------------------֒→ -----ScanID
֒→
int
֒→ 279
ScanFile
֒→
֒→ FirstTest.fio
str
PreScanSnapshot
֒→
֒→ []
list
JsonRecorder
֒→
֒→ True
bool
ActiveMntGrp
֒→
֒→ mg1
str
var
֒→
֒→ 123
int
DataCompressionRank
֒→
֒→ 0
int
ScanHistory
[{’endts’: 1346249829.4525681, ’title’:
֒→ ’ascan exp_mot65 0.0 0.1 5 0.1’, ’ [...]
list
ScanDir
֒→
֒→ /home/kracht/temp
str
The command usenv deletes an environment variable:
p09/door/exp.01 [4]: senv var 123
var = 123
p09/door/exp.01 [5]: usenv var
Success!
Here is the complete list of commands related to environment variables:
• senv. Defines an environment variable. Here are examples of how different types are defined.
p09/door/exp.01 [16]: senv scalarVar 12
scalarVar = 12
p09/door/exp.01 [17]: senv listVar "[ 1, ’a’]"
listVar = [1, ’a’]
p09/door/exp.01 [18]: senv dictVar {\"position\":2.2,\"comment
֒→ \":\"It works\",\"mylist\":[1,2,4,5]}
dictVar = {’comment’: ’It, works’, ’position’: 2.2, ’mylist’: [1,
֒→ 2, 4, 5]}
Section 6.5.2 explains how they are used in a macro.
• lsenv Lists the environment. If the name of a macro is given as an argument, it lists the
environment variables used by the given macro.
• usenv Deletes an environment variable.
• dumpenv Dumps the complete environment, including variables that are visible to a specific
macro.
• load env Loads environment variables from an .xml file. See the section 5.4.1 for details.
5.4.1 load env
The command load env sets environment variables defined in the config env.xml file. This file has
to be in the directory where the macroserver is running. The variables needed for the auto beamshutter,
auto filter and exafs are formattd in a different way. All the other variables are defined in the miscellaneous part of the tree.
<?xml version="1.0" encoding="UTF-8"?>
<sw>
<feature>
<name>auto_filter</name>
<parameters>
<FilterMax>100</FilterMax>
<FilterMin>10</FilterMin>
<FilterAbsorber>motoms2</FilterAbsorber>
<AutoFilter>False</AutoFilter>
</parameters>
</feature>
<feature>
<name>auto_beamshutter</name>
<parameters>
<BeamshutterSignal>counter1</BeamshutterSignal>
<BeamshutterLimit>10</BeamshutterLimit>
<BeamshutterTime>5</BeamshutterTime>
<AutoBeamshutter>False</AutoBeamshutter>
</parameters>
</feature>
<feature>
<name>exafs</name>
<parameters>
<ExafsIntTimes>1, 2, 3, 4</ExafsIntTimes>
</parameters>
</feature>
<miscellaneous>
<name>miscellaneous</name>
<TestEnvironment>1, 2, 3, 4</TestEnvironment>
<TestEnvironment2>hallo</TestEnvironment2>
</miscellaneous>
</sw>
There is no error if any of the features or variables for the auto filter, auto beamshutter or exafs are
not defined. The old settings are kept.
For the miscellaneous part, the tag (for example: TestEnvironment) would be the name of the
variable in spock and the text (for the TestEnvironment tag: 1, 2, 3, 4) would be the value of this
variable.
5.4.2 View Options
The View Options are environment variables that modifies the output of the macros displaying
information about the motors: wa and wm.
Up to now there are three View Options:
• ShowCtrlAxis. If set to True the name of the controller and axis number is shown together
with the motor name. Ex. with value True:
p06/door/haspp06ctrl [2]: wm mi_mot01
mi_mot01
omsvme58_mi.1
User
High
Current
Low
808451
None
805964
• PosFormat. Determines the number of decimal digits in the display of the motor position.
Ex. with value 4:
p06/door/haspp06ctrl [3]: wm mi_mot02
mi_mot02
omsvme58_mi.2
User
High
201
Current
199.0000
Low
-1
• ShowDial If set to True the Dial position (Sardana calibrated) is shown together with the
User one. For DESY motors only the User position is used, since the calibration is done in
the Tango Servers of the motors. This option should be set always to False.
They are displayed with the macro lsenv, together with all the other environment variables:
p09/door/haspp09.01 [1]: lsenv
Name
--------------------- ---------------------------------------------------ScanHistory
[{’deadtime’: 0.23218011856079102, ’title’: ’ascan
EnergyMotorName
Psi
_ViewOptions
{’PosFormat’: 4, ’ShowDial’
JsonRecorder
LogMacroMode
DataCompressionRank
ScanID
NeXusSelectorDevice
DiffracDevice
scalarVar
FlagDisplayAll
ScanFile
PreScanSnapshot
ActiveMntGrp
LogMacroPath
LksController
ScanDir
or with the specific macro lsvo:
p09/door/haspp09.01 [2]: lsvo
View option
Value
-------------- -------
PosFormat
ShowDial
ShowCtrlAxis
4
False
True
They are set with the macro setvo:
p09/door/haspp09.01 [3]: setvo PosFormat 4
p09/door/haspp09.01 [4]: setvo ShowDial False
p09/door/haspp09.01 [4]: setvo ShowCtrlAxis True
5.5
Get list of components: lsct, lsm, ls0d, ls1d, etc
• ls0d. Lists all 0D experiment channels.
• ls1d. Lists all 1D experiment channels.
• ls2d. Lists all 2D experiment channels.
• lsa. Lists all existing objects.
• lscom. Lists all communication channels.
• lsct. Lists all Counter/Timers.
• lsctrllib. Lists all existing controller classes.
• lsdef. List all macro definitions.
• lsexp. Lists all experiment channels.
• lsior. Lists all IORegisters.
• lsm. Lists all motors.
• lsmeas. List existing measurement groups.
• lspc. Lists all pseudo counters.
• lspm. Lists all pseudo motors.
5.6
Motor
• adjust lim Copies the limits of all motors from the Tango servers to the Pool devices. Nothing is done if UnitLimitMax/UnitLimitMin are not writable (like it happens for many virtual
motors). It should not be need, the limits are correctly set with the set lim macro.
• adjust lim single Copies the limits of one motor from the Tango servers to the Pool devices.
Nothing is done if UnitLimitMax/UnitLimitMin are not writable (like it happens for many
virtual motors). It should not be need, the limits are correctly set with the set lim macro.
• mv Move motor(s) to the specified position(s). Arguments: list of motor/position pairs.
• mvr Move motor(s) relative to the current position(s). Arguments: list of motor/displacement
pairs.
• set lim Sets software limits of the motor. Arguments: motor name, lower limit, upper limit.
• set pos Sets the position of the motor to the specified value. Arguments: Motor name, value
to be set as current position.
• umv Move motor(s) to the specified position(s) and update. Arguments: list of motor/position pairs.
• umvr Move motor(s) relative to the current position(s) and update. Arguments: list of motor/displacement pairs.
• wa Show all motor positions.
• wm Show motor positions. Arguments: list of motors to show.
Desy specific macros:
• hasy set lim Same as set lim.
• hasy adjust limits. Same as adjust lim.
• hasy wm Shows current position and UnitLimitMax/UnitLimitMin attribute values.
5.7
Counter/Timer
• ct. Count for the specified time on the active measurement group. Arguments: integration
time.
• settimer. Defines the timer channel for the active measurement group. Arguments: timer
name.
• uct. Count on the active measurement group and update. Arguments: integration time.
5.8
IO Register
These are the IO register related macros:
• lsioreg Lists all existing input/output registers.
• read ioreg Reads an output register. Argument: name of the output register.
• write ioreg Writes a value to an input register. Argument: name of the input register.
This is how they are used from the Spock command line:
p09/door/haso107klx [3]: %read_ioreg exp_in01
Reading exp_in01 register
1
p09/door/haso107klx [4]: %write_ioreg exp_out01 0
Writing 0 to exp_out01 register
p09/door/haso107klx [5]: %read_ioreg exp_in01
Reading exp_in01 register
0
This is how I/O register are operated using the proxies:
p09/door/haso107klx [8]: exp_out01.value = 1
p09/door/haso107klx [9]: exp_in01.value
Result [9]: 1
p09/door/haso107klx [10]: exp_out01.value = 0
p09/door/haso107klx [11]: exp_in01.value
Result [11]: 0
5.9
Macros
The section 6 explains how user macros are created. Here we list a few commands that are related
to macros.
• lsmac Lists all macros.
• prdef Returns the the macro code for the given macro name. Arguments: macro name.
• relmac Reloads the given macro. Attention: All macros contained in the file containing the
macro will also be reloaded. Arguments: macro name.
• relmaclib Reloads the given python file containing macros. This command is used, if a new
macro has been added to the Python file. Arguments: the Python file name to be reloaded
(with or without extension).
• addmaclib Loads a new macro library.
Sections 6 contains further details about Macros.
5.10
Run a sequence of macros
The macro run seq allows to run a sequency of macros listed in an file. The name of the file
containing the macro list has to be given as argument to the macro. The macros are listed in exactly
the same way they are typed in the spock command line. Ex.:
p09/door/exp [1]: run_seq seq_file.txt
whith seq file.txt:
wh
ascan exp_mot01 1.1 1.2 10 1
mv exp_mot02 2.1 3.2
mv exp_mot03 10 20
5.11
time
The command time measures the execution time of a macro.
p09/door/exp.01 [35]: time ascan exp_dmy01 1 2 9 0.1
Operation will be saved in None (fio)
Scan #430 started at Tue Sep 11 17:52:38 2012. It will take at
֒→ least 0:00:01
Moving to start positions...
#Pt No
dt
exp_dmy01 exp_t01
exp_c01
exp_c02
֒→ exp_petra
0
0.0122399
1
0.1
9
225
֒→ -22
1
0.153181
1.11111
0.1
4
226
֒→ -18
2
0.282741
1.22222
0.1
1
201
֒→ -18
3
0.43391
1.33333
0.1
0
190
֒→ -18
4
0.567145
1.44444
0.1
206
142
֒→ -18
5
0.71935
1.55556
0.1
214
101
֒→ -18
6
0.881734
1.66667
0.1
127
62
֒→ -18
7
1.03534
1.77778
0.1
44
35
֒→ -18
8
1.18797
1.88889
0.1
13
21
֒→ -22
9
1.31583
2
0.1
1
10
֒→ -22
Operation saved in /home/kracht/temp/FirstTest_00430.fio (fio)
Scan #430 ended at Tue Sep 11 17:52:39 2012, taking
֒→ 0:00:01.459326. Dead time 31.5% (motion dead time 6.6%)
CPU times: user 0.02 s, sys: 0.00 s, total: 0.02 s
Wall time: 1.52 s
5.12
Debugging
5.12.1 Debug
The command debug sets the debug mode.
p09/door/exp.01 [40]: debug
debug mode is off
p09/door/exp.01 [41]: debug on
debug mode is now on
p09/door/exp.01 [42]: debug off
debug mode is now off
5.12.2 edmac
Returns the contents of the macro file which contains the macro code for the macro name given as
an argument.
5.12.3 www
What went wrong.
p09/door/exp.01 [58]: www?
Type:
Magic function
Base Class:
<type ’instancemethod’>
String Form:
<bound method MatplotlibMTShell.www of <IPython.
֒→ Shell.MatplotlibMTShell object at 0xabff04c>>
Namespace:
IPython internal
File:
/usr/local/Python26/lib/python2.6/site-packages/
֒→ sardana/spock/magic.py
Definition:
www(self, parameter_s=’’)
Docstring:
What went wrong. Reads the stream. If no stream is specified,
֒→ it reads
’debug’ stream. Valid values are output, critical, error,
֒→ warning, info,
debug, result
5.13
The DOOR and MACRO SERVER proxies
This is how to use the DOOR proxy:
p09/door/exp.01 [24]: type(DOOR)
Result [24]: <class ’PyTango._PyTango.DeviceProxy’>
p09/door/exp.01 [25]: DOOR.RunMacro(["lsenv"])
Result [25]: [’<sequence><macro name="lsenv" id="-8"
֒→ macro_line="lsenv()"/></sequence>’]
p09/door/exp.01 [26]:
Name
֒→
Value
Type
---------------- ---------------------------------- -----ScanID
26
int
scalarVar
12
int
FlagDisplayAll
True
bool
dictVar
{’key2’: ’val2’, ’key1’: ’val1’}
dict
ScanFile
tst.fio
str
ActiveMntGrp
mg1
str
_ViewOptions
{’ShowDial’: False}
dict
JsonRecorder
True
bool
FlagDisplayAl
False
bool
var_out
test
str
ScanDir
/home/kracht/temp
And this is the MACRO SERVER proxy:
p09/door/exp.01 [34]: MACRO_SERVER.DoorList
Result [34]: (’p09/door/exp.01’,)
str
Chapter 6
Scripting, Macros
Sardana uses Macros for scripting. Macros can be implemented as functions or a classes. The
function syntax looks more convenient. This chapter gives examples for both types.
6.1
Some remarks about macros
• Macro code is stored in the directory /home/pXXuser/sardanaMacros. This directory is
in the search path of the MacroServer. All modules (aka Python files) in this directory are
automatically loaded by the MacroServer.
• A newly created Python script containing Macros is loaded into the MacroServer by restarting
it:
$ SardanaRestartMacroServer.py -x
• A newly created Python script containing Macros can also be loaded into the MacroServer
by:
p09/door/exp.01 [8]: %addmaclib hello_world
• Macro code that has been changed has to be reloaded by relmac or relmaclib. Lets’s assume
that the Macro hello world is contained in the Python file /home/pXXuser/sardanaMacros/funnyStuff.py and that this file is loaded into the MacroServer. After funnyStuff.py has been
edited the following commands are equivalent:
p09/door/exp.01 [8]: %relmac hello_world
p09/door/exp.01 [9]: %relmaclib funnyStuff.py
Notice that both commands reload all Macros contained in funnyStuff.py.
Notice that also new Macros which have been added to funnyStuff.py are made available.
• Use pylint to check the syntax of macro modules:
$ pylint -E fileName.py
The ’-E’ option displays errors only. Unfortunately Sardana macros do not pass general
syntax checks. Errors of the kind
53
E: 17,23:laser_scan: Instance of ’TypeNames’ has no ’Moveable’ member
E: 18,23:laser_scan: Instance of ’TypeNames’ has no ’Float’ member
cannot be avoided and have to be ignored. Still pylint -E is useful. If you find other errors
like
E: 26,29:laser_scan.run: Undefined variable ’A’
you have to fix them.
• If a Macro is interrupted by Ctrl-C, the on stop function is invoked.
• Macros can be implemented as functions or as classes. Since the function syntax seems to
be more convenient, this chapter starts with examples that demonstrate the functions syntax.
Those who are brave enough to use classes find examples in the last section of this chapter.
• The complemete Macro API can be found here:
http://www.tango-controls.org/static/sardana/latest/doc/html/developmen
6.2
DESY Macros
The macros listed below are available at all beamlines.
6.2.1 e2lambda, lambda2e
class e2lambda(Macro):
""" returns the wavelength [Angstr.]: 12398.424/energy"""
param_def = [
[’energy’, Type.Float, None, ’Energy[eV]’],
]
def run(self, energy):
wavelength = 12398.424/energy
self.output( "Lambda: %g" % wavelength)
class lambda2e(Macro):
""" returns the energy [eV]: 12398.424/wavelength"""
param_def = [
[’wavelength’, Type.Float, None, ’Wavelength[Angstr.]’],
]
def run(self, wavelength):
energy = 12398.424/wavelength
self.output( "Energy: %g" % energy)
6.2.2 mvsa: move-scan-analysis
The following macro moves a motor to a peak position. The position is determined by analysing
the most recent .fio file.
#!/usr/bin/env python
#
from sardana.macroserver.macro import *
from sardana.macroserver.macro import macro
import PyTango
import time
import numpy as np
import HasyUtils
class mvsa(Macro):
"""
Reads the most recent output file and moves a motor
to the maximum of the column defined by SignalCounter.
Used environment variables:
ScanDir, ScanFile, ScanID -> file name
ScanHistory
-> motor name and scan type
SignalCounter
-> counter name
’mvsa show’ shows the results, no move """
param_def = [
[’mode’, Type.String , ’peak’, "Options: ’show’,’peak’,’cms’,’cen’
[’interactiveFlag’, Type.Integer , 1, " ’1’ query before move (def.
]
interactive = True
def getFullPathName( self):
’’’
constructs the full file name using ScanDir, ScanFile and ScanID
’’’
temp = "%05d" % int(self.getEnv( "ScanID"))
#
# sometimes ScanFile is a one-element list instead of an array
#
var = self.getEnv( "ScanFile")
lst = []
if type(var).__name__ == ’list’:
lst = var[0].split(".")
else:
lst = var.split(".")
argout = self.getEnv( ’ScanDir’) + "/" + lst[0] + "_" + temp + "."
return argout
def getMotorInfo( self, xpos):
"""
finds the relevant motors in the ScanHistory and
uses the target position of the first motor to calculate
the target positions of the other motors
"""
#
# d2scan exp_dmy01 -1.0 1.0 exp_dmy02 -2.0 2.0 11 0.1
#
self.output( "Cmd: %s " % self.getEnv( "ScanHistory")[-1][’title’])
lst = self.getEnv( "ScanHistory")[-1][’title’].split()
argout = []
if lst[0].lower() == "ascan":
dct = {}
dct[ ’motorName’] = lst[1]
dct[ ’proxy’] = PyTango.DeviceProxy( dct[’motorName’])
dct[ ’targetPos’] = xpos
argout.append( dct)
elif lst[0].lower() == "dscan":
dct = {}
dct[ ’motorName’] = lst[1]
dct[ ’proxy’] = PyTango.DeviceProxy( dct[’motorName’])
dct[ ’targetPos’] = xpos
argout.append( dct)
elif lst[0].lower() == "a2scan":
dct = {}
dct[ ’motorName’] = lst[1]
dct[ ’proxy’] = PyTango.DeviceProxy( dct[’motorName’])
dct[ ’targetPos’] = xpos
startPos = float( lst[2])
endPos
= float( lst[3])
ratio = (xpos - startPos)/(endPos - startPos)
argout.append( dct)
dct = {}
dct[ ’motorName’] = lst[4]
dct[ ’proxy’] = PyTango.DeviceProxy( dct[’motorName’])
startPos = float( lst[5])
endPos
= float( lst[6])
dct[ ’targetPos’] = startPos + (endPos - startPos)*ratio
argout.append( dct)
elif lst[0].lower() == "d2scan":
dct = {}
dct[ ’motorName’] = lst[1]
dct[ ’proxy’] = PyTango.DeviceProxy( dct[’motorName’])
dct[ ’targetPos’] = xpos
startPos = dct[’proxy’].Position + float(lst[2])
endPos
= dct[’proxy’].Position + float(lst[3])
ratio = (xpos - startPos)/(endPos - startPos)
argout.append( dct)
dct = {}
dct[ ’motorName’] = lst[4]
dct[ ’proxy’] = PyTango.DeviceProxy( dct[’motorName’])
startPos = dct[’proxy’].Position + float( lst[5])
endPos
= dct[’proxy’].Position + float(lst[6])
dct[ ’targetPos’] = startPos + (endPos - startPos)*ratio
argout.append( dct)
else:
return None
return argout
def run(self, mode, interactiveFlag):
signalCounter = self.getEnv( "SignalCounter")
#
# mvsa only for ascan, dscan, a2scan, d2scan
#
scanType = self.getEnv( "ScanHistory")[-1][’title’].split()[0]
if not scanType.lower() in [’ascan’, ’dscan’, ’a2scan’, ’d2scan’]:
self.output( "mvsa: scanType %s not in [’ascan’, ’dscan’, ’a2sc
return
fileName = self.getFullPathName()
a = HasyUtils.fioReader( fileName)
message = ’undefined’
flagFound = False
for col in a.columns:
if col.name == signalCounter:
message, xpos, xpeak, xcms, xcen = HasyUtils.fastscananalys
if mode.lower() == ’show’:
#
# par-3: flag-non-background-subtraction
#
ssaDct = HasyUtils.ssa( np.array(col.x), np.array(col.y
flagFound = True
break
if not flagFound:
self.output( "Column %s not found in %s" % ( signalCounter, fil
for col in a.columns:
self.output( "%s" % col.name)
return
if message != ’success’:
self.output( "mvsa: failed to find the maximum for %s" % ( a.fi
self.output( "mvsa: reason %s" % ( message))
return
if mode.lower() == ’show’:
self.output( "File name: %s " % fileName)
self.output( "fsa: message %s" % (message))
self.output(
self.output(
self.output(
self.output(
self.output(
return
"fsa:
"fsa:
"ssa:
"ssa:
"ssa:
xpos %g" % (xpos))
xpeak %g, cms %g cen
status %d, reason %d"
xpeak %g, cms %g midp
l_back %g, r_back %g"
%g" % ( xpeak, xcms, x
% (ssaDct[’status’], s
%g" % (ssaDct[’peak_x’
% (ssaDct[’l_back’], s
motorArr = self.getMotorInfo( xpos)
#
# prompt the user for confirmation, unless we have an uncoditional
#
if interactiveFlag == 1:
self.output( "File name: %s " % fileName)
for elm in motorArr:
self.output( "Move %s from %g to %g" % ( elm[ ’motorName’],
answer = self.input( "Exec move(s) [Y/N], def. ’N’: ")
if not (answer.lower() == "yes" or answer.lower() == "y"):
self.output( "Motor(s) not moved!")
return
for elm in ( motorArr):
elm[ ’proxy’].write_attribute( "Position", elm[ ’targetPos’])
moving = True
while moving:
moving = False
for elm in ( motorArr):
if elm[ ’proxy’].State() == PyTango.DevState.MOVING:
moving = True
break
time.sleep( 0.1)
for elm in ( motorArr):
self.output( "Motor %s is now at %g" % ( elm[ ’motorName’], elm
return
6.3
Beamline specific macros
6.3.1 Maia detector scan macro
Macro performing an scan using the Maia detector.
#!/bin/env python
import PyTango
import time
from sardana.macroserver.macro import macro, Type
__all__ = ["maia_scan"]
counter = 0
class HookPars:
pass
def hook_pre_move(self, hook_pars):
global counter
PetraCurrentDevice = PyTango.DeviceProxy("petra/globals/keyword")
while PetraCurrentDevice.BeamCurrent.value < 15.:
self.output("Petra Current below 15 mA. Current Value " + str(Petra
time.sleep(1.)
m = self.macros
if counter%2==0:
self.output("Axis 0 to right: " + str(hook_pars.mot0_right))
m.mv(hook_pars.mot0,hook_pars.mot0_right)
else:
self.output("Axis 0 to left: " + str(hook_pars.mot0_left))
m.mv(hook_pars.mot0,hook_pars.mot0_left)
if counter!=0:
self.output(str(float(counter)/float(hook_pars.pixel_size1)*100.) +
counter = counter + 1
@macro( param_def = [
[’mot0’, Type.Moveable,
None, ’Internal motor’],
[’pixel_origin0’, Type.Float,
None, ’Pixel origin internal motor
[’scan_range0’, Type.Float,
None, ’Scan range internal motor’],
[’pixel_pitch0’, Type.Float, None, ’Pixel pitch internal motor’],
[’scan_dwell’, Type.Float,
None, ’Scan time per pixel’],
[’mot1’, Type.Moveable,
None, ’External motor’],
[’pixel_origin1’, Type.Float,
None, ’Pixel origin external motor
[’scan_range1’, Type.Float,
None, ’Scan range external motor’],
[’pixel_pitch1’, Type.Float, None, ’Pixel pitch external motor’],
[’info_comment’, Type.String, " ", ’Comment to be written in the m
def maia_scan( self, mot0, pixel_origin0, scan_range0, pixel_pitch0, scan_d
""" scans with maia detector """
maia_logger = self.getEnv(’MaiaLoggerDevice’)
maia_scan = self.getEnv(’MaiaScanDevice’)
maia_processing = self.getEnv(’MaiaProcessingDevice’)
maia_axis0 = self.getEnv(’MaiaAxis0Device’)
maia_axis1 = self.getEnv(’MaiaAxis1Device’)
energy_device = self.getEnv(’EnergyDevice’)
keithley_device = self.getEnv(’KeithleyDevice’)
maia_sample = self.getEnv(’MaiaSampleDevice’)
MaiaLogger=PyTango.DeviceProxy(maia_logger)
MaiaScan=PyTango.DeviceProxy(maia_scan)
MaiaProcessing=PyTango.DeviceProxy(maia_processing)
MaiaAxis0=PyTango.DeviceProxy(maia_axis0)
MaiaAxis1=PyTango.DeviceProxy(maia_axis1)
EnergyDevice = PyTango.DeviceProxy(energy_device)
KeithleyDevice = PyTango.DeviceProxy(keithley_device)
mono_energy=EnergyDevice.position/1000.
keithley_gain=1.E9/10**(KeithleyDevice.Gain)
# Move motors to the start positions
startpos0 = pixel_origin0 - pixel_pitch0
macro_mov_starpos0 , pars = self.createMacro(’mv’, mot0, startpos0)
self.runMacro(macro_mov_starpos0)
startpos1 = pixel_origin1
macro_mov_starpos1 , pars = self.createMacro(’mv’, mot1, startpos1)
self.runMacro(macro_mov_starpos1)
# Send values to the maia detector
ScanInfo=str(’"{energy:’+str(mono_energy)+’,IC_sensitivity:’+str(keithl
pixel_size0 = (int)(scan_range0/pixel_pitch0)
pixel_size1 = (int)(scan_range1/pixel_pitch1)
MaiaAxis1.PositionScale = pixel_pitch1
MaiaAxis0.position = pixel_origin0 - pixel_pitch0
MaiaAxis1.position = pixel_origin1
MaiaAxis0.ScanSize = pixel_size0
MaiaAxis1.ScanSize = pixel_size1
MaiaAxis0.PixelPitch = pixel_pitch0
MaiaAxis1.PixelPitch = pixel_pitch1
MaiaAxis0.PixelOrigin = pixel_origin0
MaiaAxis1.PixelOrigin = pixel_origin1
MaiaScan.ScanDwell = scan_dwell
MaiaScan.ScanInfo = ScanInfo
# Set mot0 to the desired slew rate
# SlewDouble is still not accesible in the sardana motor
mot0_tango = PyTango.DeviceProxy(mot0.TangoDevice)
mot0_old_slewrate=mot0_tango.SlewDouble
mot0_desired_slewrate = pixel_pitch0/scan_dwell
mot0_tango.SlewDouble = mot0_desired_slewrate
starttime=time.time()
MaiaLogger.NewRun()
MaiaProcessing.PixelEnable = 1
MaiaLogger.NewRun()
MaiaScan.ScanNew = 1
# Start the scan
global counter
counter = 0
mot0_left = pixel_origin0 + pixel_size0*pixel_pitch0
mot0_right = pixel_origin0
start_pos = pixel_origin1
final_pos = pixel_origin1 + scan_range1
nr_interv = (int)(scan_range1/pixel_pitch1)
integ_time = 1.
macro,pars = self.createMacro(’ascan’, mot1, start_pos, final_pos, nr_i
hook_pars = HookPars()
hook_pars.mot0 = mot0
hook_pars.mot0_left = mot0_left
hook_pars.mot0_right = mot0_right
hook_pars.MaiaScan = MaiaScan
hook_pars.pixel_size1 = pixel_size1
hook_pars.starttime = starttime
f = lambda : hook_pre_move( self, hook_pars)
macro.hooks = [
(f, ["pre-move"]),
]
self.runMacro(macro)
# After finishing the scan
MaiaLogger.EndRun()
MaiaProcessing.PixelEnable = 0
mot0_tango.SlewDouble = mot0_old_slewrate
6.3.2 Regions scan with maia scan at each point
Macro for performing an energy scan with different regions (changing step size and integration
time). At each point of the energy scan a maia scan is performed.
#!/bin/env python
import PyTango
import time
from sardana.macroserver.macro import macro, Type, ParamRepeat
__all__ = ["maia_regions_scan"]
class HookPars:
pass
def hook_post_move(self, hook_pars):
macro,pars = self.createMacro(’maia_scan’, hook_pars.mot0, hook_pars.p
self.runMacro(macro)
@macro([
[’mot0’, Type.Moveable,
None, ’Internal motor’],
[’pixel_origin0’, Type.Float,
None, ’Pixel origin internal motor
[’scan_range0’, Type.Float,
None, ’Scan range internal motor’],
[’pixel_pitch0’, Type.Float, None, ’Pixel pitch internal motor’],
[’mot1’, Type.Moveable,
None, ’External motor’],
[’pixel_origin1’, Type.Float,
None, ’Pixel origin external motor
[’scan_range1’, Type.Float,
None, ’Scan range external motor’],
[’pixel_pitch1’, Type.Float, None, ’Pixel pitch external motor’],
[’info_comment’, Type.String, " ", ’Comment to be written in the m
["scan_regions",
ParamRepeat([’start_point’, Type.Float, None, ’start position’],
[’stop_point’, Type.Float, None, ’end position’],
[’nb_interv’, Type.Integer, None, ’number of intervals
[’integration_time’, Type.Float, None, ’integration ti
None, ’List of motor and path curves’]
])
def maia_regions_scan( self, *list_parameters):
""" region scans with maia scans """
energy_motor = self.getObj("energy_all")
nb_scans = len(list_parameters) - 9
# Start the scan
for i in range(0,nb_scans):
macro,pars = self.createMacro(’ascan’, energy_motor, list_parameter
hook_pars = HookPars()
hook_pars.mot0 = list_parameters[0]
hook_pars.pixel_origin0 = list_parameters[1]
hook_pars.scan_range0 = list_parameters[2]
hook_pars.pixel_pitch0 = list_parameters[3]
hook_pars.scan_dwell = list_parameters[i + 9][3]
hook_pars.mot1 = list_parameters[4]
hook_pars.pixel_origin1 = list_parameters[5]
hook_pars.scan_range1 = list_parameters[6]
hook_pars.pixel_pitch1 = list_parameters[7]
hook_pars.info_comment = list_parameters[8]
f = lambda : hook_post_move( self, hook_pars)
macro.hooks = [
(f, ["post-move"]),
]
self.runMacro(macro)
6.3.3 Continuous scans with external trigger (Zebra & XIA & MCS)
Macros for performing continuos scans taking data with the XIA and/or SIS3820 MCS triggered by
the Zebra. The data is saved in NeXus files using XMLConfigServer und TangoDataServer devices.
Macros for setting the requiered spock enviroment variables are also provided.
#!/bin/env python
"""continuous scan macros"""
__all__ = ["cscan_zebra_mcs", "cscan_zebra_mcs_xia", "cscan_zebra_xia", "cs
import PyTango
from sardana.macroserver.macro import *
from sardana.macroserver.macro import macro
import json
import time
import pytz
import datetime
import numpy
class cscan_zebra_mcs(Macro):
"""Perfoms a continuous scan with the zebra triggering the mcs"""
param_def = [
[’motor’,
[’start_pos’,
[’final_pos’,
[’nb_triggers’,
[’trigger_interval’,
[’nb_mcs_channels’,
]
Type.Motor,
Type.Float,
Type.Float,
Type.Integer,
Type.Float,
Type.Integer,
None,
None,
None,
None,
None,
None,
’Motor to move’],
’Scan start position’],
’Scan final position’],
’Nb of triggers generated b
’Time between consecutive t
’Nb of mcs channels to reco
result_def = [ [ "result", Type.String, None, "the cscan object" ]]
def run(self, motor, start_pos, final_pos, nb_triggers, trigger_interva
zebra_device_name
= self.getEnv(’ZebraDevice’)
mcs_device_name
= self.getEnv(’MCSDevice’)
nexusconfig_device_name = self.getEnv(’NeXusConfigDevice’)
# clas
nexuswriter_device_name = self.getEnv(’NeXusWriterDevice’) # class
zebra_device
= PyTango.DeviceProxy(zebra_device_name)
mcs_device
= PyTango.DeviceProxy(mcs_device_name)
motor_device
= PyTango.DeviceProxy(motor.name)
self.nexusconfig_device = PyTango.DeviceProxy(nexusconfig_device_na
self.nexuswriter_device = PyTango.DeviceProxy(nexuswriter_device_na
# Set the zebra device
zebra_device.NbTriggers = nb_triggers
zebra_device.TriggerInterval = trigger_interval
# Set MCS
mcs_device.NbAcquisitions = 0 # Continuous mode
mcs_device.NbChannels
= nb_mcs_channels
nb_mcs_taken_triggers = nb_triggers
# Clear and setup mcs
mcs_device.ClearSetupMCS()
# Compute motor slewrate for the continuous scan
old_slewrate = motor_device.Velocity
scan_slewrate = abs((final_pos - start_pos)/motor_device.Step_per_u
motor_device.Velocity = int(scan_slewrate)
# Move motor to start position minus an offset
pos_offset = 0.1
motor_device.write_attribute( "position", (start_pos - pos_offset))
while motor_device.State() == PyTango.DevState.MOVING:
time.sleep(1)
# Start motor movement to final position (the macro mv can not be u
motor_device.write_attribute( "position", (final_pos + pos_offset))
# Check when the motor is in the start_position for starting the tr
while(motor_device.position < start_pos):
time.sleep(0.001)
# Open the nexus file for saving data
self._openNxFile()
self._openNxEntry(motor.name, start_pos, final_pos, nb_mcs_channels
# Start zebra triggering
zebra_device.Arm = 1
# Check when the triggering is done
while zebra_device.State() == PyTango.DevState.MOVING:
time.sleep(1)
# Reset motor slewrate
motor_device.Velocity = old_slewrate
# Read MCS
mcs_device.ReadMCS()
# MCS Data
mcs_data = mcs_device.CountsArray
# Zebra encoder Data
zebra_data = zebra_device.EncoderSpectrum
# Prepare and write the data to the NeXus File
hshMain = {}
hshRecord = {}
hshMain[’data’] = hshRecord
for i in range(0, nb_mcs_taken_triggers):
# numpy data are not json serializable. They have to be
# transformed to native types
hshRecord["encoder_pos"] = numpy.asscalar(zebra_data[i])
for j in range(0, nb_mcs_channels):
if j < 9:
data_name = "counts_mcs_ch0" + str(j+1)
else:
data_name = "counts_mcs_ch" + str(j+1)
hshRecord[data_name] = numpy.asscalar(mcs_data[i][j])
self._sendRecordToNxWriter(hshMain)
# Close NeXus file
self._closeNxEntry()
self._closeNxFile()
result = "None"
return result
def _openNxFile(self):
cscan_id = self.getEnv(’CScanID’)
fileNameNx = self.getEnv(’CScanFileName’) + "_" + str(cscan_id) +
self.setEnv("CScanID", cscan_id + 1)
self.nexuswriter_device.Init()
self.nexuswriter_device.FileName = str(fileNameNx)
self.nexuswriter_device.OpenFile()
return 1
def _openNxEntry(self, motor_name, start_pos, final_pos, nb_mcs_channel
self._sendGlobalDictionaryBefore(motor_name, start_pos, final_pos,
self.nexusconfig_device.Open()
cmps = self.nexusconfig_device.AvailableComponents()
cmp_list = []
if "zebra_mcs" not in cmps:
self.output("_openNxEntry: zebra_mcs not in configuration serve
else:
cmp_list.append("zebra_mcs")
for i in range (1,nb_mcs_channels + 1):
if i < 10:
cmp_ch = "mcs_ch0" + str(i)
else:
cmp_ch = "mcs_ch" + str(i)
if cmp_ch not in cmps:
self.output("_openNxEntry: %s not in configuration server"
else:
cmp_list.append(cmp_ch)
self.nexusconfig_device.CreateConfiguration(cmp_list)
xmlconfig = self.nexusconfig_device.XMLString
self.nexuswriter_device.TheXMLSettings = str(xmlconfig)
self.nexuswriter_device.OpenEntry()
return 1
def _closeNxFile(self):
self.nexuswriter_device.CloseFile()
return 1
def _closeNxEntry(self):
self._sendGlobalDictionaryAfter()
self.nexuswriter_device.CloseEntry()
return 1
def _sendGlobalDictionaryBefore(self, motor_name, start_pos, final_pos,
hsh = {}
hshSub = {}
hshSub[’motor_name’] = str(motor_name)
hshSub[’start_pos’] = start_pos
hshSub[’final_pos’] = final_pos
hshSub[’nb_triggers’] = nb_triggers
hshSub[’sample_time’] = trigger_interval
amsterdam = pytz.timezone(’Europe/Amsterdam’)
fmt = ’%Y-%m-%dT%H:%M:%S.%f%z’
starttime = amsterdam.localize(datetime.datetime.now())
hshSub[’start_time’] = str(starttime.strftime(fmt))
hsh[’data’] = hshSub
self._setParameter( hsh)
return 1
def _sendGlobalDictionaryAfter(self):
hsh = {}
hshSub = {}
amsterdam = pytz.timezone(’Europe/Amsterdam’)
fmt = ’%Y-%m-%dT%H:%M:%S.%f%z’
starttime = amsterdam.localize(datetime.datetime.now())
hshSub[’end_time’] = str(starttime.strftime(fmt))
hshSub[’comments’] = "some comment"
hsh[’data’] = hshSub
self._setParameter( hsh)
return 1
def _setParameter(self, hsh):
jsondata = json.dumps( hsh)
self.nexuswriter_device.TheJSONRecord = str(jsondata)
return 1
def _sendRecordToNxWriter(self, hsh):
mstr = json.dumps( hsh)
self.nexuswriter_device.Record(mstr)
return 1
class cscan_zebra_mcs_senv(Macro):
""" Sets default environment variables """
def run(self):
self.setEnv("MCSDevice", "haso107klx:10000/p09/mcs/exp.01")
self.output("Setting MCSDevice to ")
self.setEnv("ZebraDevice", "haso111n:10000/test/zebra/01")
self.output("Setting ZebraDevice to haso111n:10000/test/zebra/01")
self.setEnv("CScanFileName", "test_cscan_output")
self.output("Setting CScanFileName to test_cscan_output")
self.setEnv("NeXusConfigDevice", "haso111tb:10000/test/xmlconfigser
self.output("Setting NeXusConfigDevice to haso111tb:10000/test/xmlc
self.setEnv("NeXusWriterDevice", "haso111tb:10000/test/tangodataser
self.output("Setting NeXusWriterDevice to haso111tb:10000/test/tang
self.setEnv("CScanID", 0)
self.output("Setting CScanID to 0")
class cscan_zebra_mcs_xia(Macro):
"""Perfoms a continuous scan with the zebra triggering the mcs and the
param_def = [
[’motor’,
[’start_pos’,
[’final_pos’,
[’nb_triggers’,
[’trigger_interval’,
[’nb_mcs_channels’,
]
Type.Motor,
Type.Float,
Type.Float,
Type.Integer,
Type.Float,
Type.Integer,
None,
None,
None,
None,
None,
None,
’Motor to move’],
’Scan start position’],
’Scan final position’],
’Nb of triggers generated b
’Time between consecutive t
’Nb of mcs channels to reco
result_def = [ [ "result", Type.String, None, "the cscan object" ]]
def run(self, motor, start_pos, final_pos, nb_triggers, trigger_interva
zebra_device_name
= self.getEnv(’ZebraDevice’)
mcs_device_name
= self.getEnv(’MCSDevice’)
xia_device_name
= self.getEnv(’XIADevice’)
nexusconfig_device_name = self.getEnv(’NeXusConfigDevice’)
# clas
nexuswriter_device_name = self.getEnv(’NeXusWriterDevice’) # class
zebra_device
= PyTango.DeviceProxy(zebra_device_name)
mcs_device
= PyTango.DeviceProxy(mcs_device_name)
xia_device
= PyTango.DeviceProxy(xia_device_name)
motor_device
= PyTango.DeviceProxy(motor.name)
self.nexusconfig_device = PyTango.DeviceProxy(nexusconfig_device_na
self.nexuswriter_device = PyTango.DeviceProxy(nexuswriter_device_na
# Set the zebra device
zebra_device.NbTriggers = nb_triggers
zebra_device.TriggerInterval = trigger_interval
# Set MCS
mcs_device.NbAcquisitions = 0 # Continuous mode
mcs_device.NbChannels
= nb_mcs_channels
nb_mcs_taken_triggers = nb_triggers
# Clear and setup mcs
mcs_device.ClearSetupMCS()
# Set XIA
xia_device.MappingMode = 1
xia_device.GateMaster = 1
xia_device.NumberMcaChannels = 2048
xia_device.NumMapPixels = nb_triggers
xia_device.NumMapPixelsPerBuffer = -1
xia_device.MaskMapChannels = 1 # change if one wants to work with m
nb_xia_channels = xia_device.NumberMcaChannels
xia_device.StartMapping()
# Compute motor slewrate for the continuous scan
old_slewrate = motor_device.Velocity
scan_slewrate = abs((final_pos - start_pos)/motor_device.Step_per_u
motor_device.Velocity = int(scan_slewrate)
# Move motor to start position minus an offset
pos_offset = 0.1
motor_device.write_attribute( "position", (start_pos - pos_offset))
while motor_device.State() == PyTango.DevState.MOVING:
time.sleep(1)
# Start motor movement to final position (the macro mv can not be u
motor_device.write_attribute( "position", (final_pos + pos_offset))
# Check when the motor is in the start_position for starting the tr
while(motor_device.position < start_pos):
time.sleep(0.001)
# Open the nexus file for saving data
self._openNxFile()
self._openNxEntry(motor.name, start_pos, final_pos, nb_mcs_channels
# Start zebra triggering
zebra_device.Arm = 1
# Check when the triggering is done
while zebra_device.State() == PyTango.DevState.MOVING:
time.sleep(1)
# Check that the XIA is done
while xia_device.State() == PyTango.DevState.MOVING:
time.sleep(1)
# Reset motor slewrate
motor_device.Velocity = old_slewrate
# Read MCS
mcs_device.ReadMCS()
# MCS Data
nb_mcs_taken_triggers = mcs_device.AcquiredTriggers
nb_mcs_channels = mcs_device.NbChannels
mcs_data = mcs_device.CountsArray
# XIA data is in the attribute Buffer
time.sleep(1)
xia_buffer = xia_device.Buffer
# Zebra encoder Data
zebra_data = zebra_device.EncoderSpectrum
# Prepare and write the data to the NeXus File
hshMain = {}
hshRecord = {}
hshMain[’data’] = hshRecord
for i in range(0, nb_mcs_taken_triggers):
# numpy data are not json serializable. They have to be
# transformed to native types
hshRecord["encoder_pos"] = numpy.asscalar(zebra_data[i])
for j in range(0, nb_mcs_channels):
if j < 9:
data_name = "counts_mcs_ch0" + str(j+1)
else:
data_name = "counts_mcs_ch" + str(j+1)
hshRecord[data_name] = numpy.asscalar(mcs_data[i][j])
xia_spec = []
for j in range (0, xia_spec_length):
xia_spec.append(numpy.asscalar(xia_buffer[i * xia_spec_leng
hshRecord["xia"] = xia_spec
self._sendRecordToNxWriter(hshMain)
# Close NeXus file
self._closeNxEntry()
self._closeNxFile()
result = "None"
return result
def _openNxFile(self):
cscan_id = self.getEnv(’CScanID’)
fileNameNx = self.getEnv(’CScanFileName’) + "_" + str(cscan_id) +
self.setEnv("CScanID", cscan_id + 1)
self.nexuswriter_device.Init()
self.nexuswriter_device.FileName = str(fileNameNx)
self.nexuswriter_device.OpenFile()
return 1
def _openNxEntry(self, motor_name, start_pos, final_pos, nb_mcs_channel
self._sendGlobalDictionaryBefore(motor_name, start_pos, final_pos,
self.nexusconfig_device.Open()
cmps = self.nexusconfig_device.AvailableComponents()
cmp_list = []
if "zebra" not in cmps:
self.output("_openNxEntry: zebra not in configuration server")
else:
cmp_list.append("zebra")
for i in range (1,nb_mcs_channels + 1):
if i < 10:
cmp_ch = "mcs_ch0" + str(i)
else:
cmp_ch = "mcs_ch" + str(i)
if cmp_ch not in cmps:
self.output("_openNxEntry: %s not in configuration server"
else:
cmp_list.append(cmp_ch)
# Add component for the XIA. The Data will be directly readout from
if "xia" not in cmps:
self.output("_openNxEntry: xia not in configuration server")
else:
cmp_list.append("xia")
self.nexusconfig_device.CreateConfiguration(cmp_list)
xmlconfig = self.nexusconfig_device.XMLString
self.nexuswriter_device.TheXMLSettings = str(xmlconfig)
self.nexuswriter_device.OpenEntry()
return 1
def _closeNxFile(self):
self.nexuswriter_device.CloseFile()
return 1
def _closeNxEntry(self):
self._sendGlobalDictionaryAfter()
self.nexuswriter_device.CloseEntry()
return 1
def _sendGlobalDictionaryBefore(self, motor_name, start_pos, final_pos,
hsh = {}
hshSub = {}
hshSub[’motor_name’] = str(motor_name)
hshSub[’start_pos’] = start_pos
hshSub[’final_pos’] = final_pos
hshSub[’nb_triggers’] = nb_triggers
hshSub[’sample_time’] = trigger_interval
amsterdam = pytz.timezone(’Europe/Amsterdam’)
fmt = ’%Y-%m-%dT%H:%M:%S.%f%z’
starttime = amsterdam.localize(datetime.datetime.now())
hshSub[’start_time’] = str(starttime.strftime(fmt))
hsh[’data’] = hshSub
xia_fake_spec = [0] * xia_spec_length # faking a xia spectrum for s
hsh[’xia’] = xia_fake_spec
self._setParameter( hsh)
return 1
def _sendGlobalDictionaryAfter(self):
hsh = {}
hshSub = {}
amsterdam = pytz.timezone(’Europe/Amsterdam’)
fmt = ’%Y-%m-%dT%H:%M:%S.%f%z’
starttime = amsterdam.localize(datetime.datetime.now())
hshSub[’end_time’] = str(starttime.strftime(fmt))
hshSub[’comments’] = "some comment"
hsh[’data’] = hshSub
self._setParameter( hsh)
return 1
def _setParameter(self, hsh):
jsondata = json.dumps( hsh)
self.nexuswriter_device.TheJSONRecord = str(jsondata)
return 1
def _sendRecordToNxWriter(self, hsh):
mstr = json.dumps( hsh)
self.nexuswriter_device.Record(mstr)
return 1
class cscan_zebra_mcs_xia_senv(Macro):
""" Sets default environment variables """
def run(self):
self.setEnv("MCSDevice", "haso107klx:10000/p09/mcs/exp.01")
self.output("Setting MCSDevice to ")
self.setEnv("ZebraDevice", "haso111n:10000/test/zebra/01")
self.output("Setting ZebraDevice to haso111n:10000/test/zebra/01")
self.setEnv("XIADevice", "hasp029rack:10000/test/xia/01")
self.output("Setting XIADevice to hasp029rack:10000/test/xia/01")
self.setEnv("CScanFileName", "test_cscan_output")
self.output("Setting CScanFileName to test_cscan_output")
self.setEnv("NeXusConfigDevice", "haso111tb:10000/test/xmlconfigser
self.output("Setting NeXusConfigDevice to haso111tb:10000/test/xmlc
self.setEnv("NeXusWriterDevice", "haso111tb:10000/test/tangodataser
self.output("Setting NeXusWriterDevice to haso111tb:10000/test/tang
self.setEnv("CScanID", 0)
self.output("Setting CScanID to 0")
class cscan_zebra_xia(Macro):
"""Perfoms a continuous scan with the zebra triggering the xia"""
param_def = [
[’motor’,
[’start_pos’,
Type.Motor,
Type.Float,
None, ’Motor to move’],
None, ’Scan start position’],
[’final_pos’,
Type.Float,
None, ’Scan final position’],
[’nb_triggers’,
Type.Integer, None, ’Nb of triggers generated b
[’trigger_interval’, Type.Float,
None, ’Time between consecutive t
]
result_def = [ [ "result", Type.String, None, "the cscan object" ]]
def run(self, motor, start_pos, final_pos, nb_triggers, trigger_interva
zebra_device_name
= self.getEnv(’ZebraDevice’)
xia_device_name
= self.getEnv(’XIADevice’)
nexusconfig_device_name = self.getEnv(’NeXusConfigDevice’)
# clas
nexuswriter_device_name = self.getEnv(’NeXusWriterDevice’) # class
zebra_device
= PyTango.DeviceProxy(zebra_device_name)
xia_device
= PyTango.DeviceProxy(xia_device_name)
motor_device
= PyTango.DeviceProxy(motor.name)
self.nexusconfig_device = PyTango.DeviceProxy(nexusconfig_device_na
self.nexuswriter_device = PyTango.DeviceProxy(nexuswriter_device_na
self._openNxFile()
# Set the zebra device
zebra_device.NbTriggers = nb_triggers
zebra_device.TriggerInterval = trigger_interval
# Set XIA
xia_device.MappingMode = 1
xia_device.GateMaster = 1
xia_device.NumberMcaChannels = 2048
xia_device.NumMapPixels = nb_triggers
xia_device.NumMapPixelsPerBuffer = -1
xia_device.MaskMapChannels = 1 # change if one wants to work with m
nb_xia_channels = xia_device.NumberMcaChannels
xia_device.StartMapping()
# Compute motor slewrate for the continuous scan
old_slewrate = motor_device.Velocity
scan_slewrate = abs((final_pos - start_pos)/motor_device.Step_per_u
motor_device.Velocity = scan_slewrate
# Move motor to start position minus an offset
pos_offset = 0.1
motor_device.write_attribute( "position", (start_pos - pos_offset))
while motor_device.State() == PyTango.DevState.MOVING:
time.sleep(1)
# Start motor movement to final position (the macro mv can not be u
motor_device.write_attribute( "position", (final_pos + pos_offset))
# Check when the motor is in the start_position for starting the tr
while(motor_device.position < start_pos):
time.sleep(0.001)
# Open the nexus file for saving data
self._openNxFile()
self._openNxEntry(motor.name, start_pos, final_pos, nb_triggers, tr
# Start zebra triggering
zebra_device.Arm = 1
# Check when the triggering is done
while zebra_device.State() == PyTango.DevState.MOVING:
time.sleep(1)
# Check that the XIA is done
while xia_device.State() == PyTango.DevState.MOVING:
time.sleep(1)
# Reset motor slewrate
motor_device.Velocity = old_slewrate
# XIA data is in the attribute Buffer
xia_buffer = xia_device.Buffer
# Zebra encoder Data
zebra_data = zebra_device.EncoderSpectrum
# Prepare and write the data to the NeXus File
hshMain = {}
hshRecord = {}
hshMain[’data’] = hshRecord
xia_spec_length = xia_device.NumberMcaChannels
for i in range(0, nb_triggers):
# numpy data are not json serializable. They have to be
# transformed to native types
hshRecord["encoder_pos"] = numpy.asscalar(zebra_data[i])
xia_spec = []
for j in range (0, xia_spec_length):
xia_spec.append(numpy.asscalar(xia_buffer[i * xia_spec_leng
hshRecord["xia"] = xia_spec
self._sendRecordToNxWriter(hshMain)
# Close NeXus file
self._closeNxEntry()
self._closeNxFile()
result = "None"
return result
def _openNxFile(self):
cscan_id = self.getEnv(’CScanID’)
fileNameNx = self.getEnv(’CScanFileName’) + "_" + str(cscan_id) + "
self.setEnv("CScanID", cscan_id + 1)
self.nexuswriter_device.Init()
self.nexuswriter_device.FileName = str(fileNameNx)
self.nexuswriter_device.OpenFile()
return 1
def _openNxEntry(self, motor_name, start_pos, final_pos, nb_triggers, t
self._sendGlobalDictionaryBefore(motor_name, start_pos, final_pos,
self.nexusconfig_device.Open()
cmps = self.nexusconfig_device.AvailableComponents()
cmp_list = []
if "zebra" not in cmps:
self.output("_openNxEntry: zebra not in configuration server")
else:
cmp_list.append("zebra")
# Add component for the XIA.
if "xia" not in cmps:
self.output("_openNxEntry: xia not in configuration server")
else:
cmp_list.append("xia")
self.nexusconfig_device.CreateConfiguration(cmp_list)
xmlconfig = self.nexusconfig_device.XMLString
self.nexuswriter_device.TheXMLSettings = str(xmlconfig)
self.nexuswriter_device.OpenEntry()
return 1
def _closeNxFile(self):
self.nexuswriter_device.CloseFile()
return 1
def _closeNxEntry(self):
self._sendGlobalDictionaryAfter()
self.nexuswriter_device.CloseEntry()
return 1
def _sendGlobalDictionaryBefore(self, motor_name, start_pos, final_pos,
hsh = {}
hshSub = {}
hshSub[’motor_name’] = str(motor_name)
hshSub[’start_pos’] = start_pos
hshSub[’final_pos’] = final_pos
hshSub[’nb_triggers’] = nb_triggers
hshSub[’sample_time’] = trigger_interval
amsterdam = pytz.timezone(’Europe/Amsterdam’)
fmt = ’%Y-%m-%dT%H:%M:%S.%f%z’
starttime = amsterdam.localize(datetime.datetime.now())
hshSub[’start_time’] = str(starttime.strftime(fmt))
hsh[’data’] = hshSub
self._setParameter( hsh)
return 1
def _sendGlobalDictionaryAfter(self):
hsh = {}
hshSub = {}
amsterdam = pytz.timezone(’Europe/Amsterdam’)
fmt = ’%Y-%m-%dT%H:%M:%S.%f%z’
starttime = amsterdam.localize(datetime.datetime.now())
hshSub[’end_time’] = str(starttime.strftime(fmt))
hshSub[’comments’] = "some comment"
hsh[’data’] = hshSub
self._setParameter( hsh)
return 1
def _setParameter(self, hsh):
jsondata = json.dumps( hsh)
self.nexuswriter_device.TheJSONRecord = str(jsondata)
return 1
def _sendRecordToNxWriter(self, hsh):
mstr = json.dumps( hsh)
self.nexuswriter_device.Record(mstr)
return 1
class cscan_zebra_xia_senv(Macro):
""" Sets default environment variables """
def run(self):
self.setEnv("ZebraDevice", "haso111n:10000/test/zebra/01")
self.output("Setting ZebraDevice to haso111n:10000/test/zebra/01")
self.setEnv("XIADevice", "hasp029rack:10000/test/xia/01")
self.output("Setting XIADevice to hasp029rack:10000/test/xia/01")
self.setEnv("CScanFileName", "test_cscan_output")
self.output("Setting CScanFileName to test_cscan_output")
self.setEnv("NeXusConfigDevice", "haso111tb:10000/test/xmlconfigser
self.output("Setting NeXusConfigDevice to haso111tb:10000/test/xmlc
self.setEnv("NeXusWriterDevice", "haso111tb:10000/test/tangodataser
self.output("Setting NeXusWriterDevice to haso111tb:10000/test/tang
self.setEnv("CScanID", 0)
self.output("Setting CScanID to 0")
Notice that mot1.write attribute( ”position”, target) has been used to move the motor. We use
this syntax and not mot1.position = target because the latter form was partially not supported.
6.3.4 Sweep Scans (p02)
#!/bin/env python
"""sweep scan p02"""
__all__ = ["sweep_p02","sweep_senv"]
import PyTango
from sardana.macroserver.macro import *
from sardana.macroserver.macro import macro
import json
import time
import pytz
import datetime
import numpy
import math
angleOffsets = {
"polar_angle_channel_1"
"polar_angle_channel_2"
"polar_angle_channel_3"
"polar_angle_channel_4"
:
:
:
:
0.,
1.,
2.,
3.,
"polar_angle_channel_5" : 4.,
"polar_angle_channel_6" : 5.,
"polar_angle_channel_7" : 6.,
"polar_angle_channel_8" : 7.,
"polar_angle_channel_9" : 8.,
"polar_angle_channel_10" : 9.
}
class sweep_p02(Macro):
"""Perfoms a sweep scan. Counters are read during the sweep (they are a
param_def = [
[’motor’,
[’sweep_start_pos’,
[’sweep_distance’,
[’sweep_offset’,
[’nb_intervals’,
[’sample_time’,
]
Type.Motor,
Type.Float,
Type.Float,
Type.Float,
Type.Integer,
Type.Float,
None,
None,
None,
None,
None,
None,
’Sweep
’Sweep
’Sweep
’Sweep
’Sweep
’Sweep
motor’],
start position’],
distance’],
offset’],
number of interval
sample time’]
def run(self, motor, sweep_start_pos, sweep_distance, sweep_offset, nb_
sweep_counters_prefix = self.getEnv(’SweepCountersPrefix’)
sweep_counters = []
for i in range(0,9):
counter_name = sweep_counters_prefix + "0" + str(i+1)
sweep_counters.append(counter_name)
counter_name = sweep_counters_prefix + "10"
sweep_counters.append(counter_name)
counter_name = sweep_counters_prefix + "32"
clock_name = counter_name
sweep_counters.append(counter_name)
db = PyTango.Database()
hshCounters = {}
for counter in sweep_counters:
hshCounters[counter] = {}
hshCounters[counter][’name’] = counter
full_sardana_name = db.get_device_alias(counter)
# The properties can not be found using the alias name
b = db.get_device_property( full_sardana_name, [’__SubDevic
dName = b[’__SubDevices’][0]
# The device to the Tango device and not to the Sardana dev
# writing in the Counts attribute
hshCounters[counter][’proxy’] = PyTango.DeviceProxy(dName)
nexusconfig_device_name = self.getEnv(’NeXusConfigDevice’)
# clas
nexuswriter_device_name = self.getEnv(’NeXusWriterDevice’) # class
self.nexusconfig_device = PyTango.DeviceProxy(nexusconfig_device_na
self.nexuswriter_device = PyTango.DeviceProxy(nexuswriter_device_na
motor_device
= PyTango.DeviceProxy(motor.name)
# For the timer one has to use the corresponding tango device and n
# because it can not be started outside a measurement group
timer_device_name = self.getEnv(’SweepTimerTangoDevice’)
timer_device
= PyTango.DeviceProxy(timer_device_name)
slew_orig = motor_device.Velocity
self.sweep_motor = motor.name
self.sweep_start = sweep_start_pos
self.sweep_distance = sweep_distance
self.sweep_offset = sweep_offset
self.nb_intervals = nb_intervals
self.sample_time = sample_time
# Compute sweep velocity
sweep_time = nb_intervals*sample_time
sweep_full_distance = sweep_distance + 2*sweep_offset
move_time_orig = math.fabs( (sweep_full_distance * motor_device.St
self.output("sweep_time %g move_time_orig %g " % (sweep_time, move_
slew_sweep = int(sweep_full_distance/sweep_time)
if slew_sweep > slew_orig or slew_sweep < motor_device.Base_rate:
raise ValueError("slew_sweep %d out of range [%d,%d]" % (slew_s
self._openNxFile()
self._openNxEntry( "ten_channel_detector")
#
# Move the motor to the start position
#
self.output("p02sweep: moving %s to start position %g" % (motor.nam
motor_device.write_attribute( "position", (sweep_start_pos - sweep_
while motor_device.State() == PyTango.DevState.MOVING:
time.sleep(1)
#
# Reset the counters
#
for cnt in hshCounters.keys():
hshCounters[cnt][’proxy’].Counts = 0
#
# Start the timer
#
timer_device.SampleTime = 2000
timer_device.Start()
time_start_timer = time.time()
#
# Start the sweep
#
sweep_final_pos = sweep_start_pos + sweep_distance + sweep_offset
self.output("Sweeping from %g to %g" % (motor_device.position, swee
motor_device.write_attribute( "position", sweep_final_pos)
time_overhead = 0.
hshCounterReadings = {}
for cnt in hshCounters.keys():
hshCounterReadings[cnt] = {}
hshCounterReadings[cnt][’counts’] = 0
hshCounterReadings[cnt][’counts_old’] = 0
pos_old = motor_device.position
hshMain = {}
hshRecord = {}
hshMain[’data’] = hshRecord
count = 1
time_cycle_start = time.time()
time_total_start = time.time()
while( motor_device.state() == PyTango.DevState.MOVING):
if sample_time > time_overhead:
time.sleep(sample_time - time_overhead)
time_overhead_start = time.time()
pos_before = motor_device.position
#
# read all counters
#
for cnt in hshCounters.keys():
self.output(cnt)
hshCounterReadings[cnt][’counts’] = hshCounters[cnt][’proxy
self.output(hshCounterReadings[cnt][’counts’])
#
# clear the clock because we might run out of 32 bit
#
hshCounters[clock_name][’proxy’].Counts = 0
hshCounterReadings[clock_name][’counts_old’] = 0
#
# the position is the mean value - before and after the counter
#
pos = (pos_before + motor_device.position)/2.
if hshCounterReadings[clock_name][’counts’] == hshCounterReadin
self.output("No clock counts")
break
corr = 1000000.*sample_time/(hshCounterReadings[clock_name]["co
for mot in sorted( angleOffsets.keys()):
hshRecord[mot] = pos + angleOffsets[mot]
for cnt in sorted(hshCounters.keys()):
temp = corr*(hshCounterReadings[cnt]["counts"] - hshCounter
if temp < 0:
temp = 0
hshRecord[hshCounters[cnt][’name’]] = temp
hshCounterReadings[cnt][’counts_old’] = hshCounterReadings[
hshRecord[’correction’] = corr
hshRecord[’delta_position’] = pos - pos_old
pos_old = pos
self._sendRecordToNxWriter( hshMain)
time_cycle = time.time() - time_cycle_start
time_cycle_start = time.time()
self.output("%d Ovrhd %g, timeCycle %g, corr %g" % (count, time
count += 1
if (count % 20) == 0:
self.output(" %d motor at %g " % (count, motor_device.posit
#
# make sure that the timer does not expire
#
if (time.time() - time_start_timer) > 1900:
timer_device.Stop()
timer_device.SampleTime = 2000
timer_device.Start()
time_start_timer = time.time()
time_overhead = time.time() - time_overhead_start
self.output("sweep ended, total time %gs (%gs) " % ((time.time() self._closeNxEntry()
self._closeNxFile()
def _openNxFile(self):
sweep_id = self.getEnv(’SweepID’)
fileNameNx = self.getEnv(’SweepFileName’) + "_" + str(sweep_id) + "
self.setEnv("SweepID", sweep_id + 1)
self.nexuswriter_device.Init()
self.output(fileNameNx)
self.nexuswriter_device.FileName = str(fileNameNx)
self.nexuswriter_device.OpenFile()
return 1
def _openNxEntry(self, componentName):
self._sendGlobalDictionaryBefore()
self.nexusconfig_device.Open()
cmps = self.nexusconfig_device.AvailableComponents()
cmp_list = []
if componentName not in cmps:
self.output("_openNxEntry: %s not in configuration server", com
else:
cmp_list.append(componentName)
self.nexusconfig_device.CreateConfiguration(cmp_list)
xmlconfig = self.nexusconfig_device.XMLString
self.nexuswriter_device.XMLSettings = str(xmlconfig)
try:
self.nexuswriter_device.OpenEntry()
except:
pass
return 1
def _closeNxFile(self):
self.nexuswriter_device.CloseFile()
return 1
def _closeNxEntry(self):
self._sendGlobalDictionaryAfter()
self.nexuswriter_device.CloseEntry()
return 1
def _sendGlobalDictionaryBefore(self):
hsh = {}
hshSub = {}
hshSub[’sweep_motor’] = self.sweep_motor
hshSub[’sweep_start’] = self.sweep_start
hshSub[’sweep_distance’] = self.sweep_distance
hshSub[’sweep_offset’] = self.sweep_offset
hshSub[’interval’] = self.nb_intervals
hshSub[’sample_time’] = self.sample_time
amsterdam = pytz.timezone(’Europe/Amsterdam’)
fmt = ’%Y-%m-%dT%H:%M:%S.%f%z’
starttime = amsterdam.localize(datetime.datetime.now())
hshSub[’start_time’] = str(starttime.strftime(fmt))
try:
title = self.getEnv(’SweepTitle’)
except:
title = ""
hshSub[’title’] = title
try:
sample_name = self.getEnv(’SweepSampleName’)
except:
sample_name = ""
hshSub[’sample_name’] = sample_name
try:
chemical_formula = self.getEnv(’SweepChemicalFormula’)
except:
chemical_formula = ""
hshSub[’chemical_formula’] = chemical_formula
try:
beamtime_id = self.getEnv(’SweepBeamtimeId’)
except:
beamtime_id = ""
hshSub[’beamtime_id’] = beamtime_id
hsh[’data’] = hshSub
self._setParameter( hsh)
return 1
def _sendGlobalDictionaryAfter(self):
hsh = {}
hshSub = {}
amsterdam = pytz.timezone(’Europe/Amsterdam’)
fmt = ’%Y-%m-%dT%H:%M:%S.%f%z’
starttime = amsterdam.localize(datetime.datetime.now())
hshSub[’end_time’] = str(starttime.strftime(fmt))
hshSub[’comments’] = "some comment"
hsh[’data’] = hshSub
self._setParameter( hsh)
return 1
def _setParameter(self, hsh):
jsondata = json.dumps( hsh)
self.nexuswriter_device.JSONRecord = str(jsondata)
return 1
def _sendRecordToNxWriter(self, hsh):
mstr = json.dumps( hsh)
self.nexuswriter_device.Record(mstr)
return 1
class sweep_senv(Macro):
""" Sets default environment variables """
def run(self):
file_name = "/home/tnunez/sweep_files/test_sweep_output"
self.setEnv("SweepFileName", file_name)
self.output("Setting SweepFileName to %s" % file_name)
nexus_config_device = "haso111tb:10000/test/xmlconfigserver/01"
self.setEnv("NeXusConfigDevice", nexus_config_device)
self.output("Setting NeXusConfigDevice to %s" % nexus_config_devic
nexus_writer_device = "haso111tb:10000/test/tangodataserver/01"
self.setEnv("NeXusWriterDevice", nexus_writer_device)
self.output("Setting NeXusWriterDevice to %s" % nexus_writer_device
self.setEnv("SweepID", 0)
self.output("Setting SweepID to 0")
sweep_counters_prefix = "exp_c"
self.setEnv("SweepCountersPrefix", sweep_counters_prefix)
self.output("Setting SweepCountersPrefix to %s" % sweep_counters_pr
sweep_timer_tango_device = "haso111tb:10000/p09/dgg2/exp.01"
self.setEnv("SweepTimerTangoDevice", sweep_timer_tango_device)
self.output("Setting SweepTimerTangoDevice to %s" % sweep_timer_tan
Notice that mot1.write attribute( ”position”, target) has been used to move the motor. We use
this syntax and not mot1.position = target because the latter form was partially not supported.
6.4
Example Macro Classes
This sections gives examples for Macro classes.
6.4.1 Hello world
Here is the corresponding code as a Macro class:
#!/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/hello_world_class.py
#
"""this module contains the hello world macro"""
__all__ = ["hello_world_class"]
from sardana.macroserver.macro import *
from sardana.macroserver.macro import macro
class hello_world_class(Macro):
"""The hello world macro as a class."""
def run(self):
self.output( "Hello World (Class)")
6.4.2 Parameter Types
The following macro deomonstrates using different parameter types.
#!/bin/env python
#
__all__ = ["parameterTypes"]
from sardana.macroserver.macro import Macro, Type
#
# An excerpt of dir(Type):
# [’Acquirable’,
# ’Boolean’,
# ’CTExpChannel’,
# ’ComChannel’,
# ’Door’,
# ’Env’,
# ’ExpChannel’,
# ’Float’,
# ’IORegister’,
# ’Instrument’,
# ’Integer’,
# ’MacroServer’,
# ’MeasurementGroup’,
# ’Motor’,
# ’MotorGroup’,
# ’MotorParam’,
# ’Moveable’,
# ’OneDExpChannel’,
# ’Pool’,
# ’PseudoCounter’,
# ’PseudoMotor’,
# ’String’,
# ’TangoDevice’,
# ’TwoDExpChannel’,
# ’ZeroDExpChannel’,
# ...]
#
class parameterTypes(Macro):
"""pass various types of parameters to a macro"""
param_def = [ ["parMot", Type.Moveable, None, "Input parameter: A motor
["parStr", Type.String, "undefined", "Input parameter: A
["parInt", Type.Integer, 12345, "Input parameter: An inte
["parFloat", Type.Float, 12345., "Input parameter: An flo
]
def run(self, parMot, parStr, parInt, parFloat):
self.output( "parMot
%g (position)" % parMot.position)
self.output( "parStr
%s" % parStr.lower())
self.output( "parInt
%d" % parInt)
self.output( "parFloat %g" % parFloat)
return
6.4.3 Environment Variables
#!/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/environment_class.py
"""this module contains some demo code"""
__all__ = ["environment_class"]
from sardana.macroserver.macro import *
from sardana.macroserver.macro import macro
class environment_class(Macro):
"""reads/write environment variables"""
def run(self):
#
# spock> senv scalarVar 12
#
scalarVar = self.getEnv( ’scalarVar’)
self.output( "the value of scalarVar is %s " % scalarVar)
#
# spock> senv dictVar "{ ’key1’: ’val1’, ’key2’: ’val2’}"
#
dictVar = self.getEnv( ’dictVar’)
self.output( "--- %s" % dictVar)
for key in dictVar.keys():
self.output( "key %s -> %s" % (key, dictVar[key]))
self.setEnv( ’var_out’, ’test’)
self.output( "defined var_out")
6.4.4 A Loop of Scans
#!/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/LoopOfScans_class.py
#
"""this module contains some demo code"""
__all__ = ["LoopOfScans_class"]
from sardana.macroserver.macro import Macro, Type
class LoopOfScans_class(Macro):
"""executes a loop of scans"""
param_def = [
[ "mot1", Type.Moveable, None, "the inner loop motor" ],
[ "mot2", Type.Moveable, None, "the outer loop motor" ],
[ "delta", Type.Float, None, "a floating point number" ],
[ "np", Type.Integer, None, "the no. of points" ],
[ "comment", Type.String, None, "a comment line" ],
]
def run(self, mot1, mot2, delta, np, comment):
self.output( "Comment: %s " % comment)
posOld = mot2.position
for i in range(0, np):
newPos = posOld + i*delta
self.output( "%s is moving to %g" % (mot2.name, newPos))
self.mv(mot2, newPos)
self.output( "%s is at %g" % (mot2.name, mot2.position))
self.ascan( mot1, 0, 0.1, 5, 0.1)
self.mv( mot2, posOld)
6.4.5 Access to Scan Data
#!/usr/bin/env python
# file name: /home/pXXuser/sardanaMacros/scanData.py
"""the demo for a data scan"""
__all__ = ["scanData"]
from sardana.macroserver.macro import *
from sardana.macroserver.macro import macro
class scanData(Macro):
"""An example on how to look at the data after a scan has been executed
param_def = [
[’motor’,
Type.Motor,
[’start_pos’, Type.Float,
[’final_pos’, Type.Float,
[’nr_interv’, Type.Integer,
[’integ_time’, Type.Float,
]
def run(self, motor, start_pos,
None,
None,
None,
None,
None,
’Motor to move’],
’Scan start position’],
’Scan final position’],
’Number of scan intervals’],
’Integration time’]
final_pos, nr_interv, integ_time):
ascan, pars = self.createMacro("ascan",motor, start_pos, final_pos,
self.runMacro(ascan)
sigGen = []
for elm in ascan.data.records:
sigGen.append( elm.data[’hasppXX:10000/expchan/vc_sig_gen/1’])
self.output( "The Signal Generator output ")
self.output( sigGen)
#
#
#
#
#
#
#
#
#
to understand how the data is packed you may use the following print
self.output(
self.output(
self.output(
self.output(
self.output(
dir(ascan))
dir(ascan.data))
dir(ascan.data.records))
dir(ascan.data.records[0]))
ascan.data.records[0].data)
6.4.6 Access to Scan Data, Modified
#!/usr/bin/env python
# file name: /home/pXXuser/sardanaMacros/scanDataMod.py
#
"""the demo for a data scan"""
__all__ = ["scanDataMod"]
from sardana.macroserver.macro import *
from sardana.macroserver.macro import macro, Type
class scanDataMod(Macro):
"""An example on how to look at the data after a scan has been executed
def run(self):
a = self.ascan("exp_dmy01", 0, 1, 10, 0.1)
for elm in a.data.records:
self.output( elm.data[’hasppXX:10000/expchan/vc_sig_gen/1’])
#
#
#
#
#
#
#
#
#
to understand how the data is packed you may use the following print
self.output(
self.output(
self.output(
self.output(
self.output(
dir(a))
dir(a.data))
dir(a.data.records))
dir(a.data.records[0]))
a.data.records[0].data)
6.4.7 Access to Scan Data from a post-acq Hook
Here is an example of how to access scan data from the post-acq hook:
#!/usr/bin/env python
# file name: /home/pXXuser/sardanaMacros/hookedScan.py
"""the demo for a hooked scan"""
__all__ = ["hookedScan"]
import PyTango
from sardana.macroserver.macro import *
from sardana.macroserver.macro import macro
class hookedScan(Macro):
"""An example on how to attach hooks to the various hook points of a sc
param_def = [
[’motor’,
Type.Motor,
None, ’Motor to move’],
[’start_pos’, Type.Float,
None, ’Scan start position’],
[’final_pos’, Type.Float,
None, ’Scan final position’],
[’nr_interv’, Type.Integer, None, ’Number of scan intervals’],
[’integ_time’, Type.Float,
None, ’Integration time’]
]
def hook_post_acq(self):
self.info("--- post-acq hook")
lst = self.ascan.data.records
if len(lst) == 0:
return
for k in lst[-1].data.keys():
self.output( " %s -> %s " % (k, str( lst[-1].data[k])))
def run(self, motor, start_pos, final_pos, nr_interv, integ_time):
ascan, pars = self.createMacro( "ascan", motor, start_pos, final_po
self.ascan = ascan
ascan.hooks = [(self.hook_post_acq, ["post-acq"])]
self.runMacro(ascan)
The corresponding Spock output:
p09/door/haso107d1.01 [65]: %hookedScan exp_dmy01 0 10 2 0.1
Operation will be saved in /home/kracht/temp/tst_[ScanId].fio (fio)
Scan #92 started at Tue May 26 11:19:15 2015. It will take at least 0:00:02
Moving to start positions...
#Pt No
exp_dmy01
d1_t01
sig_gen
dt
--- post-acq hook
0
0
0.1
0.865465 0.865937
--- post-acq hook
haso107d1:10000/expchan/vc_sig_gen/1 -> 0.865465447158
haso107d1:10000/expchan/dgg2_d1_01/1 -> 0.1
exp_dmy01 -> 0.0
point_nb -> 0
timestamp -> 0.865936994553
1
5
0.1
0.295375 1.60491
--- post-acq hook
haso107d1:10000/expchan/vc_sig_gen/1 -> 0.2953754684
haso107d1:10000/expchan/dgg2_d1_01/1 -> 0.1
exp_dmy01 -> 5.0
point_nb -> 1
timestamp -> 1.60490703583
2
10
0.1
0.0936115
2.4303
Operation saved in /home/kracht/temp/tst_00092.fio (fio)
Scan #92 ended at Tue May 26 11:19:17 2015, taking 0:00:02.696336.Dead time
6.4.8 Move a Motor Asynchronously
Here is an example for executing an asynchronous move by a Macro class.
#!/usr/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/MoveMotorSync_class.py
#
"""this module contains some demo code"""
__all__ = ["MoveMotorAsync_class"]
from sardana.macroserver.macro import Macro, Type
import time
import PyTango
class MoveMotorAsync_class(Macro):
"""moves a motor to a target position, asynchronously"""
param_def = [
[ "mot1", Type.Moveable, None, "the motor to be moved" ],
[ "target", Type.Float, None, "the target position" ],
]
def run(self, mot1, target):
self.output( dir(mot1))
self.output( "MoveMotorAsync: %s is at %g %s " %
(mot1.name, mot1.position, mot1.state))
mot1.write_attribute( "position", target)
while mot1.state == PyTango.DevState.MOVING:
time.sleep(0.1)
self.output( "MoveMotorAsync: %s now at %g %s " %
(mot1.name,
mot1.position,
mot1.state))
self.output( "MoveMotorAsync: %s finally at %g %s " %
(mot1.name,
mot1.position,
mot1.state))
Notice that mot1.write attribute( ”position”, target) has been used to move the motor. We use
this syntax and not mot1.position = target because the latter form was partially not supported.
6.4.9 Read an ADC, return the result
The following Macro reads an ADC, displays the value and returns the result. It makes use of the
fact that the adc object is derived from a DeviceProxy which allows the user to access the device
commands and attributes directly.
#!/usr/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/ReadAdc_class.py
"""this module contains some demo code"""
__all__ = ["ReadAdc_class"]
from sardana.macroserver.macro import Macro, Type
class ReadAdc_class(Macro):
"""reads an ADC values, displays the result and returns it"""
param_def = [[ "adc", Type.ZeroDExpChannel, None, "an ADC" ]]
result_def = [[ "result", Type.Float, None, "the ADC reading" ]]
def run(self, adc):
result = float(adc.CurrentValue)
self.output( "%s reading %g " % (adc.name, result))
return result
This macros can be invoked from Spock in two ways:
p09/door/exp.01 [24]: ReadAdc exp_adc01
exp_adc01 reading 9
Result [24]: 9.0
p09/door/exp.01 [25]: a = %ReadAdc exp_adc01
exp_adc01 reading -8
p09/door/exp.01 [26]: a
Result [26]: -8.0
6.4.10 A Hooked Scan
Hooks allows users to inject extra code at certain positions of the scanning procedure. The following example shows how this can be done using Macro classes.
#!/usr/bin/env python
# file name: /home/pXXuser/sardanaMacros/hooked_scan_class.py
"""the demo for a hooked scan"""
from __future__ import print_function
__all__ = ["hooked_scan_class"]
import PyTango
from sardana.macroserver.macro import *
from sardana.macroserver.macro import macro
class hooked_scan_class(Macro):
"""An example on how to attach hooks to the various hook points of a sc
param_def = [
[’motor’,
Type.Motor,
None,
[’start_pos’, Type.Float,
None,
[’final_pos’, Type.Float,
None,
[’nr_interv’, Type.Integer, None,
[’integ_time’, Type.Float,
None,
]
def hook_pre_scan(self):
self.info("\t pre-scan hook")
def hook_pre_acq(self):
self.info("\t pre-acq hook")
def hook_post_acq(self):
self.info("\t post-acq hook")
def hook_pre_move(self):
self.info("\t pre-move hook")
’Motor to move’],
’Scan start position’],
’Scan final position’],
’Number of scan intervals’],
’Integration time’]
def hook_post_move(self):
self.info("\t post-move hook")
def hook_post_scan(self):
self.info("\t post-scan hook")
def run(self, motor, start_pos, final_pos, nr_interv, integ_time):
ascan, pars = self.createMacro( "ascan", motor, start_pos, final_po
ascan.pre_scan_hooks = [self.hook_pre_scan]
ascan.post_scan_hooks = [self.hook_post_scan]
ascan.hooks = [
(self.hook_pre_acq, ["pre-acq"]),
(self.hook_post_acq, ["post-acq"]),
(self.hook_pre_move, ["pre-move"]),
(self.hook_post_move, ["post-move"]),
]
self.runMacro(ascan)
6.4.11 Move to the position calculated from the scan data
This file is a example how to move a motor after performing one scan to the position determined
by the counts of a given counter.
The scan has to be performed from a macro which is calling the ascan macro, in other case the scan
data are not available for the further analysis. Other possibility would be to read the id of the scan
and get the scan data from a nexus file, but for the Petra experiments we are not using the nexus
implementation in Sardana.
The commands to use in spock would be:
p09/door/haso111tb [70]: %make_scan exp_mot01 39 40 5 1
Operation will be saved in /home/tnunez/test.txt (Spec)
Scan #136 started at Fri Apr 26 15:11:11 2013. It will take at least 0:00:0
Moving to start positions...
#Pt No
dt
exp_mot01 exp_t01
exp_c01
0
1.69902
39
1
3
1
9.71302
39.2
1
4
2
17.71
39.4
1
5
3
25.7155
39.6
1
6
4
33.715
39.8
1
5
5
41.723
40
1
4
Operation saved in /home/tnunez/test.txt (Spec)
Scan #136 ended at Fri Apr 26 15:11:57 2013, taking 0:00:45.615066. Dead ti
p09/door/haso111tb [71]: %ana_func exp_c01
Position to move
39.6
p09/door/haso111tb [72]:
The motor exp mot01 is scaned and at the end of the scan it is moved to the position determined
from the counts of the exp c01 counter.
The code of the macros ist:
#!/usr/bin/env python
"""the demo for moving a motor after an scan"""
from __future__ import print_function
__all__ = ["analysis_scan"]
import PyTango
from sardana.macroserver.macro import *
from sardana.macroserver.macro import macro
import json
ascan = 0
gmotor = 0
class make_scan(Macro):
"""Perfoms an scan keeping the data for further analysis/moves"""
param_def = [
[’motor’,
Type.Motor,
None, ’Motor to move’],
[’start_pos’, Type.Float,
None, ’Scan start position’],
[’final_pos’, Type.Float,
None, ’Scan final position’],
[’nr_interv’, Type.Integer, None, ’Number of scan intervals’],
[’integ_time’, Type.Float,
None, ’Integration time’]
]
result_def = [ [ "result", Type.String, None, "the ascan object" ]]
def run(self, motor, start_pos, final_pos, nr_interv, integ_time):
global ascan
global gmotor
gmotor = motor
ascan, pars= self.createMacro("ascan",motor, start_pos, final_pos,
self.runMacro(ascan)
result = " "
for elm in ascan.data.records:
result = result + str(elm.data)
return result
param_def = [
[’channel’,
Type.String,
@macro()
def ana_data(self, channel):
None, ’Channel to analize’]]
"""Analysis function"""
pools = []
pools = self.getPools()
pool = pools[0]
self.output(pool)
fullname = "Channel not found"
for el in pool.AcqChannelList:
chan = json.loads( el)
if channel == chan[’name’]:
#
# from: expchan/hasysis3820ctrl/1/value
# to:
expchan/hasysis3820ctrl/1
#
arr = chan[’full_name’].split("/")
fullname = "/".join(arr[0:-1])
global ascan
global gmotor
motor_name = gmotor.name
arr_data = []
arr_motpos = []
for elm in ascan.data.records:
self.output( elm.recordno)
self.output( elm.data)
self.output( elm.written)
for dat in elm.data:
if dat == fullname:
arr_data.append(elm.data[fullname])
if dat == motor_name:
arr_motpos.append(elm.data[motor_name])
# Compute maximum
dmax = 0
for i in range(0, len(arr_data)):
if arr_data[i] > dmax:
dmax = arr_data[i]
imax = i
# Position to move arr_motpos[imax]
self.output("Position to move")
self.output(arr_motpos[imax])
self.mv(gmotor,31)
class ana_func(Macro):
"""Analysis macro"""
param_def = [
[’channel’,
]
Type.String,
None, ’Channel to analize’]
def prepare(self, channel):
self.dmax = 0
pools = []
pools = self.getPools()
pool = pools[0]
self.fullname = "Channel not found"
for el in pool.AcqChannelList:
chan = json.loads( el)
if channel == chan[’name’]:
#
# from: expchan/hasysis3820ctrl/1/value
# to:
expchan/hasysis3820ctrl/1
#
arr = chan[’full_name’].split("/")
self.fullname = "/".join(arr[0:-1])
def run(self, channel):
global ascan
global gmotor
motor_name = gmotor.name
self.arr_data = []
arr_motpos = []
for elm in ascan.data.records:
for dat in elm.data:
if dat == self.fullname:
self.arr_data.append(elm.data[self.fullname])
if dat == motor_name:
arr_motpos.append(elm.data[motor_name])
self.peak()
# Position to move arr_motpos[imax]
self.output("Position to move")
self.output(arr_motpos[self.imax])
self.mv(gmotor,arr_motpos[self.imax])
def peak(self):
# Compute maximum
for i in range(0, len(self.arr_data)):
if self.arr_data[i] > self.dmax:
self.dmax = self.arr_data[i]
self.imax = i
This code can be easily extended for moving to another calculated positions.
6.4.12 Mixed classes
A file may contain several classes. This is demonstrated in the following code. If the code is edited,
it can be reloaded by one of these commands:
Spock> relmac mixed1
Spock> relmac mixed2
Spock> relmac LoopOfScansMixed
Spock> relmaclib mixed_class
#!/usr/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/mixed_class.py
#
"""this module contains some macros"""
__all__ = ["mixed1", "mixed2", "LoopOfScansMixed"]
from sardana.macroserver.macro import *
from sardana.macroserver.macro import macro
class mixed1(Macro):
"""Mixed1"""
def run(self):
self.output( "This is mixed1")
class mixed2(Macro):
"""Mixed2"""
def run(self):
self.output( "This is mixed2")
class LoopOfScansMixed(Macro):
"""reads an environment variable and displays it"""
param_def = [
[ "mot1", Type.Moveable, None, "the inner loop motor" ],
[ "mot2", Type.Moveable, None, "the outer loop motor" ],
[ "delta", Type.Float, None, "a floating point number" ],
[ "np", Type.Integer, None, "the no. of points" ],
[ "comment", Type.String, None, "a comment line" ],
]
def run(self, mot1, mot2, delta, np, comment):
self.output( "Comment: %s " % comment)
posOld = mot2.position
for i in range(0, np):
newPos = posOld + i*delta
self.output( "%s is moving to %g" % (mot2.name, newPos))
self.mv(mot2, newPos)
self.output( "%s is at %g" % (mot2.name, mot2.position))
self.ascan( mot1, 0, 0.1, 5, 0.1)
self.mv( mot2, posOld)
6.4.13 Call on stop, also from parent class
The following example shows how the on stop() function of a derived class invodes the on stop()
function of the parent class.
#!/usr/bin/env python
#
from sardana.macroserver.macro import Macro, Type
import time
import PyTango
class parentClass(Macro):
def say( self):
self.output( "parentClass.say Hello ")
return
def on_stop( self):
self.output( "parentClass.on_stop")
class derivedClass( parentClass):
"""prints a string"""
param_def = [
[ "text", Type.String, None, "some text" ],
]
def on_stop( self):
self.output( "derivedClass.on_stop")
# parentClass.on_stop( self)
super( derivedClass, self).on_stop()
def run(self, text):
self.say()
timer = PyTango.DeviceProxy( "p21/dgg2/exp.01")
self.output( "waiting for ctrl-c")
timer.SampleTime = 0.5
for n in range(20):
self.output( "still waiting")
timer.StartAndWaitForTimer()
6.5
Example Macro Functions
6.5.1 Hello world
The first example prints ’Hello world’:
#!/usr/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/hello_world.py
#
from sardana.macroserver.macro import macro
@macro()
def hello_world( self):
""" this prints ’hello world’"""
self.output( "macro-function: hello world")
The Macro hello world is loaded into the MacroServer by:
$ SardanaRestartMacroServer.py
It is available in the next Spock session.
p09/door/exp.01 [4]: hello_world
macro-function: hello world
If hello world.py is edited while the MacroServer is running, the changes have to be made available
by reloading the macro:
p09/door/exp.01 [5]: relmac hello_world
hello_world successfully (re)loaded
Notice that all macros that are possibly stored in hello world.py are also reloaded by this command.
6.5.2 Environment Variables
The following code shows how environment variables are set/get from within a Macro.
#!/usr/bin/env python
from sardana.macroserver.macro import macro
@macro()
def environment( self):
"""
sets/gets/unsets the environment variables scalarVar and dictVar
"""
self.setEnv( ’scalarVar’, ’12’)
self.setEnv( ’dictVar’, {"key1": "val1", "key2":"val2"})
scalarVar = self.getEnv( ’scalarVar’)
self.output( "the value of scalarVar is %s " % scalarVar)
dictVar = self.getEnv( ’dictVar’)
self.output( "--- %s" % dictVar)
for key in dictVar.keys():
self.output( "key %s -> %s" % (key, dictVar[key]))
self.lsenv()
#
# cleanup
#
self.unsetEnv( "scalarVar")
self.unsetEnv( "dictVar")
6.5.3 A Loop of Scans
Here is a Macro that executes a loop of scans:
#!/usr/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/LoopOfScans.py
#
from sardana.macroserver.macro import macro, Type
@macro([
[ "mot1", Type.Moveable, None, "the inner loop motor" ],
[ "mot2", Type.Moveable, None, "the outer loop motor" ],
[ "delta", Type.Float, None, "a floating point number" ],
[ "np", Type.Integer, None, "the no. of points" ],
[ "comment", Type.String, None, "a comment line" ],
])
def LoopOfScans(self, mot1, mot2, delta, np, comment):
self.output( "Comment: %s " % comment)
posOld = mot2.position
for i in range(0, np):
newPos = posOld + i*delta
self.output( "%s is moving to %g" % (mot2.name, newPos))
self.mv(mot2, newPos)
self.output( "%s is at %g" % (mot2.name, mot2.position))
self.ascan( mot1, 0, 0.1, 5, 0.1)
self.mv( mot2, posOld)
An important purpose of this example is to demonstrate how parameters are passed to the Macro.
The Macro LoopOfScans can be executed from the Spock command line by:
p09/door/exp.01 [2]: LoopOfScans exp_dmy01 exp_dmy02 0.1 5 "a comment"
6.5.4 Access to Scan Data
The following example demonstrates how data is extracted from a scan object after a scan has been
executed.
#!/usr/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/scanData.py
#
from sardana.macroserver.macro import macro, Type
@macro( [
[’motor’,
Type.Motor,
None, ’Motor to move’],
[’start_pos’, Type.Float,
None, ’Scan start position’],
[’final_pos’, Type.Float,
None, ’Scan final position’],
[’nr_interv’, Type.Integer, None, ’Number of scan intervals’],
[’integ_time’, Type.Float,
None, ’Integration time’]
])
def scanData(self, motor, start_pos, final_pos, nr_interv, integ_time):
"""An example on how to look at the data after a scan has been executed
ascan, pars = self.createMacro("ascan",motor, start_pos, final_pos, nr_
self.runMacro(ascan)
petra = []
for elm in ascan.data.records:
petra.append( elm.data[’hasppXX:10000/expchan/petra_exp_petra/1’])
self.output( "The Petra current is ")
self.output( petra)
#
#
#
#
#
#
#
#
to understand how the data is packed you may use the following print
self.output(
self.output(
self.output(
self.output(
self.output(
dir(ascan))
dir(ascan.data))
dir(ascan.data.records))
dir(ascan.data.records[0]))
ascan.data.records[0].data)
6.5.5 Access to Scan Data, modified
The Example below does essentially the same as 6.5.4 using a different syntax.
#!/usr/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/scanDataMod.py
#
from sardana.macroserver.macro import *
from sardana.macroserver.macro import macro, Type
@macro()
def scanDataMod(self):q
"""An example on how to look at the data after a scan has been executed
a = self.ascan("exp_dmy01", 0, 1, 10, 0.1)
for elm in a.data.records:
self.output( elm.data[’hasppXX:10000/expchan/petra_exp_petra/1’])
#
#
#
#
#
#
#
#
to understand how the data is packed you may use the following print
self.output(
self.output(
self.output(
self.output(
self.output(
dir(a))
dir(a.data))
dir(a.data.records))
dir(a.data.records[0]))
a.data.records[0].data)
6.5.6 Move a Motor Asynchronously
In the LoopOfScan example, 6.5.3, it is demonstrated how a motor is moved synchronously from
within a Macro. Now and then we need an asynchronous move:
#!/usr/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/MoveMotorAsync.py
#
from sardana.macroserver.macro import macro, Type
import time
import PyTango
@macro([
[ "mot1", Type.Moveable, None, "the motor to be moved" ],
[ "target", Type.Float, None, "the target position" ],
] )
def MoveMotorAsync( self, mot1, target):
"""moves a motor to a target position, asynchronously"""
self.output( "MoveMotorAsync_f: %s is at %g %s " %
(mot1.name, mot1.position, mot1.state()))
mot1.write_attribute( "position", target)
while mot1.state() == PyTango.DevState.MOVING:
time.sleep(0.1)
self.output( "MoveMotorAsync_f: %s now at %g %s " %
(mot1.name,
mot1.position,
mot1.state()))
self.output( "MoveMotorAsync_f: %s finally at %g %s " %
(mot1.name,
mot1.position,
mot1.state()))
Notice that mot1.write attribute( ”position”, target) has been used to move the motor. We use
this syntax and not mot1.position = target because the latter form was partially not supported.
6.5.7 Read an ADC an return the result
#!/usr/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/ReadAdc.py
#
from sardana.macroserver.macro import macro, Type
@macro( param_def = [[’adc’, Type.ZeroDExpChannel, None, "The ADC name"]],
result_def= [[ ’result’, Type.Float, None, "The ADC reading" ]])
def ReadAdc( self, adc):
""" read and ADC, display the value and return the result"""
result = adc.CurrentValue
self.output( "%s reading %g " % (adc.name, result))
return result
6.5.8 IO Register
This is how the macro, see below, is called from the Spock command line:
p09/door/hasppXX [1]: %DemoIO_f exp_in01 exp_out01
exp_in01 inReg 0
exp_in01 inReg 1
Here is the code:
#!/usr/bin/env python
__all__ = ["DemoIO_f"]
from sardana.macroserver.macro import macro, Type
@macro( param_def = [[’regIn’, Type.IORegister, None, "The input register
[’regOut’, Type.IORegister, None, "The output registe
result_def= [[ ’result’, Type.Float, None, "The last reading of the
def DemoIO_f( self, regIn, regOut):
""" if regIn and regOut are connected by a cable,
the status change is displayed
"""
regOut.value = 0
result = regIn.value
self.output( "%s inReg %g " % (regIn.name, result))
regOut.value = 1
result = regIn.value
self.output( "%s inReg %g " % (regIn.name, result))
return result
6.5.9 A Hooked Scan
Hooks allow users to inject code at certain positions of the scanning procedure. The following
example shows how this is done using Macro functions.
#!/usr/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/hooked_scan.py
#
from sardana.macroserver.macro import *
@macro()
def hook_pre_acq(self):
self.output("pre_acq hook")
@macro()
def hook_post_acq(self):
self.output("post_acq hook")
@macro()
def hook_pre_move(self):
self.output("pre_move hook")
@macro()
def hook_post_move(self):
self.output("post_move hook")
@macro()
def hook_pre_scan(self):
self.output("pre_scan hook")
@macro()
def hook_post_scan(self):
self.output("post_scan hook")
@macro([
[’motor’,
Type.Motor,
None, ’Motor to move’],
[’start_pos’, Type.Float,
None, ’Scan start position’],
[’final_pos’, Type.Float,
None, ’Scan final position’],
[’nr_interv’, Type.Integer, None, ’Number of scan intervals’],
[’integ_time’, Type.Float,
None, ’Integration time’] ])
def hooked_scan(self, motor, start_pos, final_pos, nr_interv, integ_time):
"""
This macro executes a scan with all hooks busy.
"""
self.lsenv()
macro,pars = self.createMacro(’ascan’, motor, start_pos, final_pos, nr_
# Add here only the hooks that you want to have. In this example six ho
# The first element of each pair tells wich function has to be executed
# the second one at which moment has to be executed.
macro.hooks = [
(self.hook_pre_scan, ["pre-scan"]),
(self.hook_pre_move, ["pre-move"]),
(self.hook_pre_acq, ["pre-acq"]),
(self.hook_post_acq, ["post-acq"]),
(self.hook_post_move, ["post-move"]),
(self.hook_post_scan, ["post-scan"])
]
self.runMacro(macro)
Example of a hook function called with arguments.
#!/usr/bin/env python
#
# file name: /home/pXXuser/sardanaMacros/hooked_scan.py
#
from sardana.macroserver.macro import *
class HookPars:
pass
def hook_pre_acq( self, o):
"""
this is a hook function that has access to self and
receives arguments which are packed into an object
"""
self.output( "\n\npost_scan_hook: %s %g" % (o.motor, o.start_pos))
m, pars = self.createMacro( ’wm ’ + str(o.motor))
self.runMacro( m)
@macro([
[’motor’,
Type.Motor,
None, ’Motor to move’],
[’start_pos’, Type.Float,
None, ’Scan start position’],
[’final_pos’, Type.Float,
None, ’Scan final position’],
[’nr_interv’, Type.Integer, None, ’Number of scan intervals’],
[’integ_time’, Type.Float,
None, ’Integration time’] ])
def hooked_scan_args(self, motor, start_pos, final_pos, nr_interv, integ_ti
"""
This macro executes a scan with one hook reciving arguments.
"""
self.lsenv()
macro,pars = self.createMacro(’ascan’, motor, start_pos, final_pos, nr_
parameters = HookPars()
parameters.motor = motor
parameters.start_pos = start_pos
#
# hook functions cannot have arguments - use lambda to overcome this
#
f = lambda : hook_pre_acq( self, parameters)
macro.hooks = [
(f, ["pre-acq"])
]
self.runMacro(macro)
6.5.10 Interactive Macros
It is possible to implement macros in which the input parameters do not have to be given when the
macro is called, but interactivily while the macro is running.
The macro will ask for the parameters with a prompt in the Spock command line or opening a new
window. The selection between this two ways is done in the Sardana sardanacustomsettings.py file
by setting the variable INPUTHANDLER to SpockInputHandler (spock CL) or QtInputHandler
(new window for entering the value). The default value is SpockInputHandler.
The following example shows the four ways of declaring a macro as interactive:
from sardana.macroserver.macro import macro, Macro, Type
from sardana.macroserver.macro import imacro, iMacro
@macro(interactive=True)
def ask_name(self):
"""Macro function to ask for user name"""
answer = self.input("What’s your name?")
self.output("So, your name is ’%s’", answer)
class ask_name_c(Macro):
"""Macro class to ask for user name"""
interactive = True
def run(self):
answer = self.input("What’s your name?",data_type=Type.String)
self.output("So, your name is ’%s’", answer)
# interactive macro function
@imacro()
def ask_name_i(self):
"""Macro function to ask for user age"""
answer = self.input("What’s your age?",default_value = 30,data_type=Typ
self.output("So, your age is ’%s’", answer)
# interactive macro class version
class ask_name_ci(iMacro):
"""Macro class to ask for user height"""
def run(self):
answer = self.input("What’s your height?",data_type=Type.Float)
self.output("So, your height is ’%s’", answer)
The call to:
self.input("")
is the one asking for the input value. ’data type’ and ’default value’ are optional.
Chapter 7
The Sardana NeXus Recorder
This chapter describes writing data into NeXus files using Sardana NXS FileRecorder1 .
7.1
Device Selection
Every measurement requires devices to be selected. The Component Selector (CS) is a graphical
user interface serving this purpose. It is launched from a Linux terminal by
haspp09% nxscomp_selector
Figure 7.1: Component Selector: Detectors
Configuration of the hardware devices for the NeXus files is described in configuration components.
The concept employed is inspired by these correspondences2 :
• measurement ↔ file
• experimental setup ↔ NeXus configuration
1
2
In order to run the NeXus Recorder, the installed version of Sardana should contain NXS FileRecorder.
For more details about NeXus configuration components and their data sources see Section 7.5
108
• hardware device ↔ component
• device attribute, e.g. counter reading, motor position ↔ data source
Figure 7.1 displays the Detectors tab of the Component Selector including the available device
components. Composite components with multiple data sources appear in the Components frame.
Simple components are distributed in the frames labelled Counters, ADC, MCA, etc. Components
are selected by activating the Sel. checkbox. If the Dis. checkbox is enabled, the output of the
device is displayed during the scan for monitoring purposes.
Simple components may be part of composite components. As a consequence, selecting a composite component may implicitly select one or more simple components. This dependency has to be
visible for the users. Therefore, simple components being implicitly selected are deactivated and
their font colour changes to grey. The user may also move the mouse over a composite component
to inspect the contents.
Ideally all devices are contained in components ensuring that they have sensible NeXus paths and
meaningful metadata associated with them. In practice this is not always possible. Consider a
counter module with 32 channels. Some of them are permanently connected to specific detectors.
It is an easy task to create components for these inputs. However, during the course of a beamtime,
it may happen that a researcher needs to record some other signal. Depending on the circumstances it may be impossible to create a new component immediately. Still the new signal has to be
recorded. In order to handle this situation, dynamical components have been introduced. They are
automatically created whenever a selected device is not covered by a component.
Figure 7.2: Component Selector: Descriptions
In the upper part of the Selector window the user sets Scan File, Scan Directory and if consecutive
scans are appended to one file or stored separately. To use NeXus Sardana Recorder the file extension has to be set to .nxs. Before the scan all the changes has to be confirmed by the Apply
button. Its action updates settings of the active Sardana measurement group and configuration of
the Component Selector.
After devices have been selected the state of the Detectors tab can be saved into a profile in the
Configuration tab. It is possible to create several profiles. Profiles can be loaded to restore a
particular device selection. This way, the researcher can easily switch from one data acquisition
setup to another.
The Descriptions tab, Figure 7.2, displays components containing metadata only. They are divided
into two groups: the beamline-specific components and the discipline-specific components. The
beamline group describes the source device and the facility. The discipline group contains information about the spatial arrangement of the experimental setup, mainly motor positions.
Figure 7.3: Component Selector: NeXus User Data
In order to describe the experiment completely some of the client data have to be provided by the
user. Figure 7.3 shows the CS tab allowing the researcher to supply this information. Typical
examples for user-supplied metadata are title, sample name and user comment.
The layout of the Component Selector can be easily adapted into different beamline specification.
The Section 7.6.5 contains more detail description of the settings, i.e. Configuration tag.
7.2
Component Selector and MacroGUI integration in TaurusGUI
Since the Component Selector is a Taurus widget it can be launched within taurusgui, e.g. by
haspp09% nxsmacrogui
The above command runs macrogui and the Component Selector integrated as one taurusgui
application.
The required door device can be set by -d<door device> or --door=<door device>
switches, i.e.
haspp09% nxsmacrogui -d p09/door/haso228k.02
or changed in the Taurus\Macro execution configuration item of taurusgui menu.
By default the first door is used.
7.3
The first NeXus scan
After selecting a profile in the component selector the user start a scan in Spock. A name of
Scan file and a used measurement group can be also set by scan environment variables: ScanDir,
ScanFile, and ActiveMntGrp. Choosing the .nxs file extension causes automatic selection
of NeXus file recorder.
p09/door/exp.01 [1]: senv ScanFile tst.nxs
ScanFile = tst.nxs
p09/door/exp.01 [2]: senv ScanDir /home/p09user/temp
ScanDir = /home/p09user/temp
p09/door/exp.01 [4]: senv ActiveMntGrp mg1
ActiveMntGrp = mg1
If configuration is set properly the user performs the first NeXus scan, e.g.
p09/door/exp.01 [7]: ascan mot01 0 1 10 0.1
Operation will be saved in /tmp/sardana/tst_00253.nxs (nxs)
Scan #253 started at Tue Jan 28 14:25:42 2014. It will take at least 0:00:01.693513
Moving to start positions...
#Pt No
mot01
ct01
ct02
ct03
ct04
dt
0
0
0.1
0.2
0.3
0.4
0.763649
1
0.1
0.1
0.2
0.3
0.4
1.01022
2
0.2
0.1
0.2
0.3
0.4
1.27261
3
0.3
0.1
0.2
0.3
0.4
1.55113
4
0.4
0.1
0.2
0.3
0.4
1.8011
5
0.5
0.1
0.2
0.3
0.4
2.02495
6
0.6
0.1
0.2
0.3
0.4
2.25807
7
0.7
0.1
0.2
0.3
0.4
2.45539
8
0.8
0.1
0.2
0.3
0.4
2.68389
9
0.9
0.1
0.2
0.3
0.4
2.90792
10
1
0.1
0.2
0.3
0.4
3.16542
Operation saved in /tmp/sardana/tst_00253.nxs (nxs)
Scan #253 ended at Tue Jan 28 14:25:45 2014, taking 0:00:03.302698. Dead time 66.7% (motion dead
֒→ time 34.7%)
Notice that if you are going to write NeXus file and you change ActiveMntGrp a given name of the
active measurement group has to be consistent with a measurement group name of the Component
Selector. Otherwise, components from the selector will not be used to configure the NeXus writer
and the file will be stored without the beamline metadata information.
7.4
NeXus Macros
As an alternative to the Component Selector to prepare the NeXus Recorder Profile (and Measurement Group) the user can use the following sardana macros:
• lsprof or nxsprof – lists the current profile
• nxsettimers – sets the current profile timers
• nxsadd – selects the given detector components
• nxsrm – deselects the given detector components
• nxsclr – removes all detector components from the current profile
• nxsetappentry – sets the append entry flag for the current profile
• nxsetudata – sets the given user data
• nxsusetudata – unsets the given user data
• nxsadddesc – adds the given description components to the current profile
• nxsrmdesc – removes the given description components from the current profile
• nxsupdatedesc – updates a selection of description components
• nxsetprof – sets the active profile
• nxsrmprof – removes the current profile
• nxslsprof – lists all avaliable profiles
• nxsls – lists all available components to select
• nxslstimers – lists all available timers
• nxslscp – lists configuration server components
• nxslsds – lists configuration server datasources
• nxslsdevtype – list all defined device types
• nxshow – describes the given component
• nxsave – saves the current profile to the given file
• nxsload – loads a profile from the given file
7.5
NeXus Configuration
At DESY, writing of the NeXus files is based on NeXus Writer and Configuration Server. A
schematic of the whole process is displayed in Figure 7.4. The Configuration Server provides
configuration string into NeXus Writer. The NeXus configuration is a composition of components
and data sources. The writer fetches the data from the Experimental Control Client, other Tango
devices or various databases and store them into a file system.
7.5.1 Components and data sources
The NeXus configuration components are objects constructed of NeXus base classes representing
simple devices with single data sources, (e.g. counter, MCA), composite devices, (e.g. monochromators, diffractometer), structures holding metadata only, (e.g. mandatory metadata). The components can be inserted into a configuration or deleted from it without side effects.
The Components consist of the following items:
• name – a string identifying the component
Figure 7.4: Data Flow for NeXus Data Writer
• data sources – a tag specifying where the data stored in NeXus fields have to be fetched.
There are the available data source types:
– Tango attributes
– database queries
– data supplied by the experimental control client (ECC)
– output of python scripts
• strategy – a tag of four strategy types: INIT, STEP, FINAL and POSTRUN, indicating
whether data are captured before, during or after the scanning procedure. A NeXus field
or attribute is marked with the POSTRUN tag if the information is inserted during a subsequent data processing step.
• NeXus path – an absolute position of the data inside the file. It is expected in terms of NeXus
groups.
7.5.2 NeXus file structure
A concrete example of a component, a Pilatus1M detector is given by Figure 7.5. The component
is built using groups (red), fields (blue), attributes (grey) and links (green). Groups contain groups,
fields and links. They generate the hierarchical file structure. Groups have names, associated
attributes and types like NXentry, NXinstrument or NXdetector. Fields contain data with their
attributes: names, data type, shape and unit. Attributes are descriptive information for groups and
fields Links refer to fields at different locations in the data tree. They are generally used to the
NXdata group identifying those data fields required for easy plotting.
A hardware device is represented by a hierarchy of NeXus groups: NXentry, NXinstrument and
NXdetector. The pathname is a concatenation of the group names. If NeXus files contain several
Figure 7.5: Component for Pilatus1M detector
scans, they have multiple entries in the NXentry level. The NXinstrument group collects all devices belonging to an experiment. Therefore, NeXus files have only one NXinstrument entry per
NXentry.
Let’s look at the detector from Figure 7.5 in more detail. The detector component is composed
of several NeXus fields. Most of them are provided by the NXdetector class. Those fields not
contained in the base class are stored in the NXcollection group. One of the NXdetector fields
specifies the pixel size in x-direction. It has the NX FLOAT64 type. Since this field contains static
information, it has no data source assigned. In contrast, we see that the data field has a tag named
FileDir, a string of the type NX CHAR. The value of the string is a reference to the data source
$datasources.P1M fileDir. It is marked with the FINAL strategy tag. Therefore FileDir is resolved
when the measurement ends.
Figure 7.6: Component Merging
Since components contain absolute path names, they can be merged to obtain the complete configuration. Figure 7.6 exemplifies the process with two components, a detector component representing
a Pilatus camera and the default component containing the beamline description. The components
are merged according to their group types and names. In practice, NeXus files are typically configured by 10 to 50 components.
When components are created, the data source fields are initially occupied by placeholders. The
process of assigning actual references to the data sources can be considered as an instantiation of a
component. The aim is to create generic components that can be used at various places.
7.5.3 Preparation of Components
Since the preparation of components and data sources is a rather tedious task, the Component
Designer (Figure 7.7), has been developed. It is launched from a Linux terminal by
haspp09% nxscomp_designer
It creates new components and data sources, edits existing ones or links data sources to components.
The Component Designer (CD) uses a data base to manage components and data sources. The
following procedure outlines the steps to create all required components for a particular experiment:
1. Creation of the data sources: data sources being related to Tango1 attributes can be generated
by automated procedures. The others have to be created with the CD.
Figure 7.7: Component Designer
2. Re-use of existing components: if a device is already in use at another beamline the corresponding component can be imported and the local data sources have to be supplied.
3. Creation of new components: if a device is used for the first time, the component has to be
created with the CD and the data sources have to be supplied.
4. Storage of the newly created components and data sources.
Creating data sources and components requires a reasonable amount of expertise. Hence, this tasks
is typically performed by beamline staff or members of the experiment control group.
7.5.4 Data acquisition process
This section explains how the NeXus file production is integrated into the data acquisition system
at DESY. The whole writing process is depicted as a sequence diagram in Figure 7.8.
The Experiment Control Client (ECC) serves as an interface to the researcher. This application
prompts the Configuration Server for a list of available components. Depending on the experimental
technique the researcher selects required components. The ECC sends the selected components
to the Configuration Server where they are merged into the configuration. Next, the ECC reads
the created configuration string and passes it to the NeXus Writer. The NeXus Writer parses the
configuration and creates a NeXus file with the required hierarchical structure. For each data source
an object is created carrying which holds the information of where to fetch the data from and where
to store them. These data objects are maintained in lists. There is one list for INIT, STEP and
FINAL strategies.
After the NeXus file has been prepared and the lists of data objects have been produced, the measurement starts. The sequence begins by invoking the NeXus Writer for the INIT phase. The
process iterates through the list of INIT data objects invoking their function members that transfer
the data from the sources to the NeXus file. Likewise the NeXus Writer executes the STEP and
FINAL phases. INIT and FINAL data are read once. STEP data are read for each step of a scanning
procedure.
Data collected during the INIT phase typically consist of information describing the instrument and
the facility. User metadata and the initial state of the beamline devices, motor positions, are also
included. The STEP phase generates most of the raw data. An example for FINAL data is the end
time of the scan.
Figure 7.8: Sequence diagram
In addition there is the POSTRUN strategy for data to be supplied in a post processing step. This
feature has been introduced for 2D detectors creating data which, for performance reasons, are not
immediately inserted into the NeXus files. Camera servers store their image frames temporarily on
ramdisks, local disks or on network file systems. After a measurement is finished the data collection
process opens the NeXus file, searches for POSTRUN strategy labels and inserts the images into
the file. This way, the NeXus file contains all experimental data belonging to a measurement.
NeXus field or attribute
/NXentry/experiment identifier
/NXentry/title
/NXentry/NXinstrument/name
/NXentry/NXinstrument/
name@short name
/NXentry/NXinstrument/NXsource/
name
/NXentry/NXinstrument/NXsource/
name@short name
/NXentry/NXsample/name
/NXentry/NXsample/chemical formula
/NXentry/start time
/NXentry/end time
ICAT field
Investigation
Investigation.Summary
Instrument.Fullname
Instrument.Name
Remark
a unique beamtime IT
description of the experiment
name of the beamline
short name of the beamline
Facility.Fullname
accelerator name
Facility.Name
short name for the accelerator
Sample.Name
Sampletype.Molecularformula
Instrument.Start Date
Instrument.End Date
name of the sample
the sample chemical formula
start time of the scan
end time of the scan
Table 7.1: Mandatory metadata
7.5.5 Mandatory metadata
Certain metadata contained in NeXus files are common to all DESY experiments, the mandatory
metadata (MMD). The size of the MMD is small. But they are sufficient for a rough identification
of the file contents. Besides, MMD are used by the ingestion procedure inserting files into the
metadata catalogue.
Table 7.1 contains a list of the DESY MMD and it shows how the NeXus items are mapped to ICAT
fields.
• /NXentry/experiment identifier identifies the beamtime. Several NeXus files can have the
same beamtimeId.
• /NXentry/title is supplied by the user. It is scan-specific.
• /NXentry/NXinstrument/name is the full beamline name, e.g.: HighRes Diffraction
• /NXentry/NXinstrument/name@short name the name of the beamline, e.g. P08
• /NXentry/NXsample/name is the sample name, it is scan specific.
• /NXentry/NXsample/chemical formula the chemical formula, it is scan specific, e.g. LaB4
• NXentry/start time, NXentry/end time start and end of a scan, expressed in ISO format, e.g.
2013-06-10T14:30:16.238875+0200.
7.6
Instruction for experts
The chapter presents more technical information which are dedicated for IT staff or beamline scientists.
7.6.1 NeXus Servers
There are the following NeXus Tango Servers:
• NXSConfigServer serves as an interface to nxsconfig database with components and
datasources.
• NXSDataWriter writes NeXus files using pni-libraries.
• NXSRecSelector keeps the selected experimental channels, i.e. selected component,
datasources or pool devices, which are used during the scan.
Due to its synchronization mechanism NXSRecSelector has to be started after MacroServer,
i.e. with Startup level 4 in Astor.
7.6.2 Check if nxsconfig mysql database exists on TANGO HOST
The nxsconfig database is created on each TANGO HOST. It is required by NXSConfigServer.
In oder to check if required mysql db exists on the unix console of tango host type
haspp09% mysql -e "show databases;"
+--------------------+
| Database
|
+--------------------+
| information_schema |
| nxsconfig
|
| tango
|
+--------------------+
which displays available mysql databases. The NeXus Component database name usually starts
with nxsconfig, e.g. nxsconfig, nxsconfig 2.
It contains components, datasources and properties tables, i.e.:
haspp09% mysql -e "use nxsconfig; show tables;"
+---------------------+
| Tables_in_nxsconfig |
+---------------------+
| components
|
| datasources
|
| properties
|
+---------------------+
7.6.3 Setting up NeXus servers
If on TANGO HOST the nxsconfig* database exists the host is ready to setup servers. This can be
done by
haspp09% nxsetup -x
if the localhost is known by nxsetup script or
haspp09% nxsetup -x -b <beamline> -m <masterHost> -u <local_user>
֒→ -d <dbname>
e.g.
haspp09% nxsetup -x -b p09 -m haspp09 -u p09user -d nxsconfig
in a general case.
The above commands create NXSConfigServer, NXSDataWriter, NXSRecSelector and start them
on proper hosts. In the current PETRA software environment the NXSConfigServer server has to
run on the TANGO HOST. Therfore, if the localhost is not TANGO HOST the nxsetup script
searches for TANGO HOST and starts NXSConfigServer on TANGO HOST. Other servers are
started on the localhost. The instance names of the servers are defined by the masterHost name.
To setup only one server, e.g. NXSDataWriter, specify the class name of the server as an additional
argument, i.e.
haspp09% nxsetup -x
NXSDataWriter
7.6.4 Restarting NeXus servers
To restart all NeXus servers running on the localhost, type
haspp09% nxsetup -r
If you want to restart all NeXus servers of the given server class, specify the class name of the
server as an additional argument, e.g.
haspp09% nxsetup -r NXSDataWriter
To restart the specific server instance, type also the server instance name after the server class name,
e.g.
haspp09% nxsetup -r NXSDataWriter/haspp09
7.6.5 Component Selector Configuration
Figure 7.9 shows the Configuration tab of the Component Selector. It provides profile and layout
parameters grouped in various frames.
Figure 7.9: Component Selector: Configuration
• In the Profile frame, at the top of the tab, the user saves to a file or loads the current scan
profile.
• In the Components there are: Groups, Warnings and Reset Descriptions widgets.
– The Groups widget allows to define sets of components and datasources which are
visible in the Detector and Descriptions widgets.
– In the Warnings widget user checks reasons why Description components are deselected.
– The Reset Descriptions sets a group of Description components to the default one, i.e.
defined in the DefaultAutomaticComponents property of the current NXSRecSelector
server.
• The View frame defines a type of the selection widgets, a maximal number of rows in Detector
tab frames and if a widget with an apply status is displayed.
• In the Frames frame user sets a layout, names, numeric labels of Detectors tab device groups.
The layout is defined by square brackets nested in four levels: frames, columns, rows, groups.
• In the Groups frame defines a content of device groups from the Detectors tabs. Its dictionary
associates numeric labels of the device groups to a list of component and device data source
items. The item names can be written a UNIX wildcard format, i.e. ”exp c*”.
• The Info shows Tango servers used by the Sardana recorder3 .
• The layout setting can be saved to a file or loaded in the Layout frame.
7.6.6 Creating a new TaurusGUI application with the Component Selector
To create a new TaurusGUI application the use can run a wizard by
haspp09% taurusgui --new-gui
An example of config.xml file looks as follows:
<taurusgui_config>
<GUI_NAME>Experiment Control Panel</GUI_NAME>
<ORGANIZATION>DESY</ORGANIZATION>
<ORGANIZATION_LOGO>./desylogo.png</ORGANIZATION_LOGO>
<MACROSERVER_NAME>p09/macroserver/haso228k</MACROSERVER_NAME>
<DOOR_NAME>p09/door/haso228k</DOOR_NAME>
<INSTRUMENTS_FROM_POOL>False</INSTRUMENTS_FROM_POOL>
<PanelDescriptions>
<PanelDescription>
<name>NXSelector</name>
<classname>Selector</classname>
<modulename>nxselector.Selector</modulename>
<floating>True</floating>
<sharedDataWrite>
<item datauid="expConfChanged" signalName="experimentConfigurationChanged" />
</sharedDataWrite>
<sharedDataRead>
<item datauid="expConfChanged" slotName="resetConfiguration" />
<item datauid="doorName" slotName="updateDoorName" />
</sharedDataRead>
</PanelDescription>
</PanelDescriptions>
</taurusgui_config>
where to enable communication between the Component Selector and the MacroGui the <sharedDataRead>
and <sharedDataWrite> tags were added manually.
7.7
Using the Selector Server without Sardana
A device profile selected by the Component Selector 7.1 is stored in the Selector Server. The
Selector Server is an instance of the NXSRecSelector Tango class. The currectly used Selector
Server is defined in the macroserver environment variable NeXusSelectorDevice.
3
If instead of Selector Tango server the Python module has to be used the device name of the Selector Server has
to be set to module.
If the Selector Server does not exist in the given host or NeXusSelectorDevice is set to
’module’ a storage of the Component Selector is in the NeXusConfigutation environment
variable.
7.7.1 Writing NeXus files configured by the Component Selector
Information from the Selector Server can be used also outside Sardana. The following script
demonstrates how to perform a simple scan using Components and User Data from selected by
the Component Selector.
#!/bin/env python
import PyTango
import json
from datetime import datetime
from pytz import timezone
## NeXus file name and timezone info
nxsfile = ’/tmp/example1.nxs’
berlin = timezone(’Europe/Berlin’)
fmt = ’%Y-%m-%dT%H:%M:%S.%f%z’
# Selector Server
selector = PyTango.DeviceProxy("p09/nxsrecselector/haso228k")
# NeXus Writer
writer = PyTango.DeviceProxy(selector.WriterDevice)
# Configuration Server
cserver = PyTango.DeviceProxy(selector.ConfigDevice)
# Detector components
detectors = selector.Components
# Description components
descriptions = selector.AutomaticComponents
components = list(detectors) if detectors else []
if descriptions:
components.extend(descriptions)
# user data
datarecord = selector.DataRecord
# configuration variables
confvars = selector.ConfigVariables
# create configuration
cserver.Open()
cserver.Variables = confvars
cserver.CreateConfiguration(components)
finalXML = cserver.XMLString
# open the file
writer.Init()
writer.FileName = nxsfile
writer.OpenFile()
writer.XMLSettings = finalXML
# get start_time
starttime = berlin.localize(datetime.now())
# open the entry
record = {}
record["data"] = json.loads(datarecord)
record["data"]["start_time"] = str(starttime.strftime(fmt))
writer.JSONRecord = json.dumps(record)
writer.OpenEntry()
# experimental loop
for i in range(10):
srecord = {}
## setting CLIENT data
srecord["data"] = {}
writer.Record(json.dumps(srecord))
#close the entry
endtime = berlin.localize(datetime.now())
record["data"]["end_time"] = str(endtime.strftime(fmt))
writer.JSONRecord = json.dumps(record)
writer.CloseEntry()
#close the file
writer.CloseFile()
7.7.2 Writing NeXus files with CLIENT data fetched from Tango Servers
We can extend the previous script by Dynamic components for Sardana channels selected by Component Selector4
#!/usr/bin/env python
""" Client for NeXus Writer """
import PyTango
import json
from datetime import datetime
from pytz import timezone
import pprint
import HasyUtils
class ScanComp(object):
## constructor
def __init__(self, selector):
# Selector Server
self.selector = PyTango.DeviceProxy(selector)
# NeXus Writer
self.writer = PyTango.DeviceProxy(self.selector.WriterDevice)
self.writer.set_timeout_millis(30000)
# Selected Channels
# datasources = self.selector.DataSources
self.appendEntry = self.selector.AppendEntry
# user data
self.datarecord = self.selector.DataRecord
self.berlin = timezone(’Europe/Berlin’)
self.fmt = ’%Y-%m-%dT%H:%M:%S.%f%z’
# name of a dynamic component
self.dynamicCP = "__dynamic_component__"
self.mcl = {"INIT": {}, "STEP": {}, "FINAL": {}}
self.globalDct = {}
self.components = []
self.proxies = {}
self.clientSources = []
self.attributeNames = {}
self.configurationXML = ""
self.attrsToCheck = ["Value", "Position", "Counts", "Data",
"Voltage", "Energy", "SampleTime"]
def openFile(self, nxsfile):
# open the file
self.writer.Init()
self.writer.FileName = nxsfile
self.writer.OpenFile()
def getClientSources(self):
return self.clientSources
#
# mostly "Value", but ...
4
NXSRecSelector (>= 1.8.6), NXSConfigServer (>= 1.7.0) is needed
#
def findAttributeName(self, proxy):
result = None
for at in self.attrsToCheck:
if hasattr(proxy, at):
result = at
break
return result
@classmethod
def isTangoDevice(cls, devName):
try:
dp = PyTango.DeviceProxy(str(devName))
dp.ping()
return dp
except:
print devName, " is not Tango device"
return None
def fetchClientSources(self):
self.clientSources = json.loads(self.selector.clientSources([]))
dynClientSources = json.loads(
self.selector.clientSources([self.dynamicCP])) \
if self.dynamicCP else []
self.clientSources.extend(dynClientSources)
for elm in self.clientSources:
print "comp and dynComp",
print pprint.pformat(elm, indent=1)
self.mcl = {"INIT": {}, "STEP": {}, "FINAL": {}}
for cs in self.clientSources:
self.mcl[cs["strategy"]][cs["record"]] = cs["dsname"]
dp = self.isTangoDevice(cs["record"])
self.proxies[cs["record"]] = dp
if dp:
self.attributeNames[cs["record"]] = self.findAttributeName(dp)
def createConfiguration(self):
# Detector components
detectors = self.selector.Components
# Description components
descriptions = self.selector.AutomaticComponents
self.components = list(detectors) if detectors else []
if descriptions:
self.components.extend(descriptions)
#
# dynamicCP contains all dynamic data sources
#
self.dynamicCP = str(self.selector.createDynamicComponent([]))
self.fetchClientSources()
self.selector.updateConfigVariables()
if self.dynamicCP:
self.components.append(self.dynamicCP)
self.configurationXML = str(
self.selector.CreateConfiguration(self.components))
self.selector.RemoveDynamicComponent(self.dynamicCP)
return
def openEntry(self, data=None):
self.createConfiguration()
self.writer.XMLSettings = self.configurationXML
self.globalDct = json.loads(self.datarecord)
# get start_time
starttime = self.berlin.localize(datetime.now())
self.globalDct["start_time"] = str(
starttime.strftime(self.fmt))
if isinstance(data, dict):
self.globalDct.update(data)
missing = list(
set(self.mcl[’INIT’].keys()) - set(self.globalDct.keys()))
if missing:
raise Exception("Missing INIT CLIENT data: %s" % missing)
self.writer.JSONRecord = json.dumps({"data": self.globalDct})
self.writer.OpenEntry()
def execStep(self, data=None):
localDct = data if isinstance(data, dict) else {}
missing = list(
(set(self.mcl[’STEP’].keys()) - set(self.globalDct.keys())))
# if the record name is Tango device we can try to read its Value
for dv in missing:
if self.proxies[str(dv)]:
value = self.proxies[str(dv)].read_attribute(
self.attributeNames[str(dv)]).value
if hasattr(value, "tolist"):
value = value.tolist()
localDct[dv] = value
else:
localDct[dv] = "SomeValue"
missing = list(
(set(self.mcl[’STEP’].keys())) - set(localDct.keys())
- set(self.globalDct.keys()))
if missing:
raise Exception("Missing STEP CLIENT data: %s" % missing)
# print("RECORD: %s" % localDct)
print pprint.pformat(localDct, indent=1, depth=1)
self.writer.Record(json.dumps({"data": localDct}))
def closeEntry(self, data=None):
endtime = self.berlin.localize(datetime.now())
self.globalDct["end_time"] = str(endtime.strftime(self.fmt))
if isinstance(data, dict):
self.globalDct.update(data)
missing = list(
set(self.mcl[’FINAL’].keys()) - set(self.globalDct.keys()))
if missing:
raise Exception("Missing FINAL CLIENT data: %s" % missing)
self.writer.JSONRecord = json.dumps({"data": self.globalDct})
self.writer.CloseEntry()
def closeFile(self):
self.writer.CloseFile()
if __name__ == ’__main__’:
scan = ScanComp("p09/nxsrecselector/hastodt")
if scan.appendEntry:
scan.openFile("/tmp/hasylab.nxs")
else:
scan.openFile(HasyUtils.createScanName("/tmp/hasylab") + ".nxs")
scan.openEntry()
for el in scan.getClientSources():
print "client sources", pprint.pformat(el, indent=1)
## experimental loop
for li in range(3):
scan.execStep()
scan.closeEntry()
scan.closeFile()
7.7.3 Writing NeXus files with dynamic components in C++
A similar program can be also written in C++. The main program selclient.cc:
#include <tango.h>
#include <string>
#include "nxselwriter.hh"
int main(int argc, char ** argv){
// tango selector server name
std::string selectorName("p09/nxsrecselector/hastodt");
// file name
std::string fileName("/tmp/selector_3.nxs");
// timeout for servers in msec
int timeout = 24000;
try{
// create writer
NXSelWriter wr(selectorName);
// create configuration stting
std::string xmlconf = wr.createConfiguration(timeout);
// open NeXus file
wr.openFile(fileName);
// open scan entry
wr.openEntry(xmlconf);
// experimental loop
for(int i =0 ;i<10; i++)
wr.loopStep();
// close entry
wr.closeEntry();
// close NeXus file
wr.closeFile();
}
catch(Tango::DevFailed &e){
Tango::Except::print_exception(e);
}
catch(std::string &e){
std::cerr << "ERROR: " << e << std::endl;
}
}
where we create the file configuration according to the Selector Server setting and writer the NeXus
file using the NeXus Writer.
The detail code was closed in the NXSelWriter class. Its header is in the nxselwriter.hh
file:
#ifndef NXSELWRITER_HH
#define NXSELWRITER_HH
#include <string>
#include <tango.h>
#include <jsoncpp/json/json.h>
#include <boost/property_tree/ptree.hpp>
class NXSelWriter{
public:
NXSelWriter(const std::string& selectorName);
std::string createConfiguration(int timeout=25000);
void openFile(const std::string & fileName);
void openEntry(const std::string & xmlconf);
void loopStep(Json::Value ldata = Json::objectValue);
void closeEntry();
void closeFile() const;
˜NXSelWriter();
protected:
std::vector<std::string> availableComponents() const;
std::vector<std::string> selectedComponents() const;
std::vector<std::string> selectedDataSources() const;
std::string clientDataSources(const std::vector<std::string>& components) const;
std::vector<std::string> automaticComponents() const;
std::vector<std::string> availableDataSources() const;
Json::Value getValue(std::string device) const;
bool checkServer(Tango::DeviceProxy & proxy) const;
void printList(const std::string& label, const std::vector<std::string>& list) const;
void init(int timeout=25000);
std::string fetchConfigVariables();
void removeDynamicComponent();
void fetchSelection();
void updateDataSources(const Json::Value mjsds);
private:
std::string _isoformat = "%Y-%m-%dT%H:%M:%S.%%06u%z";
std::set <std::string> _clinitdv;
std::map <std::string, std::string> _clstepds;
std::set <std::string> _clfinaldv;
std::string _configName;
std::string _writerName;
std::string _selectorName;
std::string _dynamicCP;
std::set<std::string> _components;
Json::Value _dataRecord;
Json::Reader _jsonReader;
Json::StyledWriter _jsonWriter;
Tango::DeviceProxy * _configProxy = 0;
Tango::DeviceProxy * _writerProxy = 0;
Tango::DeviceProxy * _selectorProxy = 0;
std::string _fileName;
std::string _xml;
NXSelWriter(const NXSelWriter&);
NXSelWriter& operator=(const NXSelWriter&);
};
#endif
while an implementation of its methods is in the nxselwriter.cc file:
#include
#include
#include
#include
#include
#include
#include
<string>
<tango.h>
<iostream>
<string>
<sstream>
<algorithm>
<iterator>
#include
#include
#include
#include
#include
#include
<boost/algorithm/string.hpp>
<boost/property_tree/ptree.hpp>
<boost/property_tree/json_parser.hpp>
<boost/property_tree/xml_parser.hpp>
<boost/property_tree/exceptions.hpp>
<boost/foreach.hpp>
#include <jsoncpp/json/json.h>
#include "nxselwriter.hh"
#include <tango.h>
using boost::property_tree::ptree;
using boost::property_tree::ptree_error;
std::string NXTypes[] = {
"NX_CHAR",
// DEV_VOID = 0,
"NX_BOOLEAN", // DEV_BOOLEAN,
"NX_INT16",
// DEV_SHORT,
"NX_INT32",
// DEV_LONG,
"NX_FLOAT32", // DEV_FLOAT,
"NX_FLOAT64", // DEV_DOUBLE,
"NX_UINT16",
// DEV_USHORT,
"NX_UINT32",
// DEV_ULONG,
"NX_CHAR",
// DEV_STRING,
"NX_UINT8",
// DEVVAR_CHARARRAY,
"NX_INT16",
// DEVVAR_SHORTARRAY,
"NX_INT32",
// DEVVAR_LONGARRAY,
"NX_FLOAT32", // DEVVAR_FLOATARRAY,
"NX_FLOAT64", // DEVVAR_DOUBLEARRAY,
"NX_UINT16",
// DEVVAR_USHORTARRAY,
"NX_UINT32",
// DEVVAR_ULONGARRAY,
"NX_CHAR",
// DEVVAR_STRINGARRAY,
"NX_CHAR",
// DEVVAR_LONGSTRINGARRAY,
"NX_CHAR",
// DEVVAR_DOUBLESTRINGARRAY,
"NX_CHAR",
// DEV_STATE,
"NX_CHAR",
// CONST_DEV_STRING,
"NX_BOOLEAN", // DEVVAR_BOOLEANARRAY,
"NX_UINT8",
// DEV_UCHAR,
"NX_INT64",
// DEV_LONG64,
"NX_UINT64",
// DEV_ULONG64,
"NX_INT64",
// DEVVAR_LONG64ARRAY,
"NX_UINT64",
// DEVVAR_ULONG64ARRAY,
"NX_INT64",
// DEV_INT,
"NX_CHAR"
// DEV_ENCODED
};
NXSelWriter::NXSelWriter(const std::string& selectorName):
_selectorName(selectorName){
}
NXSelWriter::˜NXSelWriter(){
if(_configProxy)
delete _configProxy;
if(_writerProxy)
delete _writerProxy;
}
bool NXSelWriter::checkServer(Tango::DeviceProxy& proxy) const{
bool found = false;
int cnt = 0;
int cntmax = 1000;
Tango::DevState state;
while (!found && cnt < cntmax){
if(cnt >1)
usleep(10);
try{
Tango::DeviceData stat = proxy.command_inout("State");
stat >> state;
if (state != Tango::RUNNING){
found = true;
}
}
catch(Tango::DevFailed &e){
found = false;
if (cnt == cntmax-1){
Tango::Except::print_exception(e);
}
}
catch(...){
found = false;
}
}
return found;
}
void NXSelWriter::removeDynamicComponent(){
std::vector<std::string> components = availableComponents();
if (std::find(components.begin(), components.end(),
_dynamicCP) != components.end()){
Tango::DeviceData ndata;
ndata << _dynamicCP;
_configProxy->command_inout("DeleteComponent",ndata);
}
}
std::string NXSelWriter::fetchConfigVariables(){
Tango::DeviceAttribute attr;
std::string selvars;
std::string confvars;
Json::Value svars;
Json::Value cvars;
bool appendEntry;
attr = _selectorProxy->read_attribute("ConfigVariables");
attr >> selvars;
if(!selvars.size()){
selvars = std::string("{}");
}
attr = _selectorProxy->read_attribute("AppendEntry");
attr >> appendEntry;
if(appendEntry){
_jsonReader.parse(selvars, svars);
std::vector<std::string> smembers = svars.getMemberNames();
if (std::find(smembers.begin(), smembers.end(),
"serialno") == smembers.end()){
attr = _configProxy->read_attribute("Variables");
attr >> confvars;
_jsonReader.parse(confvars, cvars);
std::vector<std::string> cmembers = cvars.getMemberNames();
if (std::find(cmembers.begin(), cmembers.end(),
"serialno") != cmembers.end()){
std::string strno;
try{
strno = cvars["serialno"].asString();
}catch(...){
strno = std::to_string(cvars["serialno"].asInt());
}
int sno = atoi( strno.c_str() );
sno++;
cvars["serialno"] =std::to_string(sno);
}
else{
cvars["serialno"] ="1";
}
svars["serialno"] = cvars["serialno"];
selvars = _jsonWriter.write(svars);
}
}
std::cout << "CONFIGURATION VARIABLES: " << selvars << std::endl;
return selvars;
}
std::string NXSelWriter::createConfiguration(int timeout){
init(timeout);
Tango::DeviceData arg;
std::vector<std::string> empty;
arg << empty;
Tango::DeviceData ret = _selectorProxy->command_inout("CreateDynamicComponent", arg);
ret >> _dynamicCP;
fetchSelection();
std::string confvar= fetchConfigVariables();
Tango::DeviceAttribute cv("Variables", confvar);
_configProxy->write_attribute(cv);
if(_dynamicCP.size())
_components.insert(_dynamicCP);
std::vector<std::string> components = availableComponents();
// checks if componentNames in ConfigServer components
for(std::set<std::string>::const_iterator it = _components.begin();
it != _components.end(); ++it) {
if (std::find(components.begin(), components.end(), *it) == components.end()){
std::stringstream sstream;
sstream << "Component "<< *it
<< " not stored in configuration server" << std::endl;
throw sstream.str();
}
}
Tango::DeviceData mycmps;
std::vector<std::string> vcomponents(_components.begin(), _components.end());
mycmps << vcomponents;
_configProxy->command_inout("CreateConfiguration", mycmps);
Tango::DeviceAttribute xmlAttr;
std::string xmlconf;
xmlAttr = _configProxy->read_attribute("XMLString");
xmlAttr >> xmlconf;
//
std::cout << "CONFIGURATION: " << xmlconf << std::endl;
removeDynamicComponent();
return xmlconf;
}
void NXSelWriter::init(int timeout){
_selectorProxy = new Tango::DeviceProxy(_selectorName);
if(!checkServer(*_selectorProxy)){
std::stringstream sstream;
sstream << "Error: Setting up "<< _configName
<< " takes too long" << std::endl;
throw sstream.str();
}
_selectorProxy->set_timeout_millis(timeout);
Tango::DeviceAttribute attr;
attr = _selectorProxy->read_attribute("ConfigDevice");
attr >> _configName;
attr = _selectorProxy->read_attribute("WriterDevice");
attr >> _writerName;
_configProxy = new Tango::DeviceProxy(_configName);
if(!checkServer(*_configProxy)){
std::stringstream sstream;
sstream << "Error: Setting up "<< _configName
<< " takes too long" << std::endl;
throw sstream.str();
}
_configProxy->set_timeout_millis(timeout);
_configProxy->command_inout("Open");
_writerProxy = new Tango::DeviceProxy(_writerName);
if(!checkServer(*_writerProxy)){
std::stringstream sstream;
sstream << "Error: Setting up "<< _writerName
<< " takes too long" << std::endl;
throw sstream.str();
}
_writerProxy->set_timeout_millis(timeout);
_writerProxy->command_inout("Init");
}
void NXSelWriter::printList(const std::string& label,
const std::vector<std::string>& list) const{
std::cout << label;
for(std::vector<std::string>::const_iterator it = list.begin();
it != list.end(); ++it) {
std::cout << (*it) << ’ ’;
}
std::cout << std::endl;
}
void NXSelWriter::fetchSelection(){
std::vector<std::string> detectors = selectedComponents();
// detector components
printList("DETECTORS: ", detectors);
// discipline components
std::vector<std::string> discipline = automaticComponents();
printList("DISCIPLINE: ", discipline);
_components = std::set<std::string>(detectors.begin(), detectors.end());
_components.insert(discipline.begin(), discipline.end());
std::string datarecord;
Tango::DeviceAttribute attr;
attr = _selectorProxy->read_attribute("DataRecord");
attr >> datarecord;
_jsonReader.parse(datarecord, _dataRecord);
std::vector<std::string> comps;
std::string jsds = clientDataSources(comps);
Json::Value mjsds;
Json::Value mjsds2= Json::arrayValue;
_jsonReader.parse(jsds, mjsds);
std::cout <<"DYNAMIC COMPONENT: " << _dynamicCP << std::endl;
if(_dynamicCP.size()){
comps.push_back(_dynamicCP);
jsds = clientDataSources(comps);
_jsonReader.parse(jsds, mjsds2);
}
updateDataSources(mjsds);
updateDataSources(mjsds2);
}
void NXSelWriter::updateDataSources(const Json::Value mjsds){
for(Json::ValueIterator itv = mjsds.begin();
itv != mjsds.end(); ++itv){
std::string mode = (*itv)["strategy"].asString();
if(mode == "STEP"){
_clstepds[(*itv)["record"].asString()] = (*itv)["dsname"].asString();
}
else if (mode == "INIT"){
_clinitdv.insert((*itv)["record"].asString() );
}
else if (mode == "FINAL"){
_clfinaldv.insert((*itv)["record"].asString() );
}
std::cout << "STRATEGY: " << (*itv)["strategy"].asString()
<< " RECORD: " << (*itv)["record"].asString()
<< " DS NAME: " << (*itv)["dsname"].asString() << std::endl;
}
}
void NXSelWriter::openFile(const std::string & fileName){
_fileName = fileName;
Tango::DeviceAttribute da("FileName", _fileName);
_writerProxy->write_attribute(da);
_writerProxy->command_inout("OpenFile");
}
std::vector<std::string> NXSelWriter::selectedComponents() const{
std::vector<std::string> components;
Tango::DeviceAttribute attr;
attr = _selectorProxy->read_attribute("Components");
attr >> components;
return components;
}
std::string NXSelWriter::clientDataSources(const std::vector<std::string>& components) const{
std::string datasources;
Tango::DeviceData comps;
comps << const_cast< std::vector<std::string>& >(components);
Tango::DeviceData csources = _selectorProxy->command_inout("ClientSources", comps);
csources >> datasources;
return datasources;
}
std::vector<std::string> NXSelWriter::selectedDataSources() const{
std::vector<std::string> datasources;
Tango::DeviceAttribute attr;
attr = _selectorProxy->read_attribute("DataSources");
attr >> datasources;
return datasources;
}
std::vector<std::string> NXSelWriter::automaticComponents() const{
std::vector<std::string> components;
Tango::DeviceAttribute attr;
attr = _selectorProxy->read_attribute("AutomaticComponents");
attr >> components;
return components;
}
std::vector<std::string> NXSelWriter::availableComponents() const{
std::vector<std::string> components;
Tango::DeviceData cmps = _configProxy->command_inout("AvailableComponents");
cmps >> components;
return components;
}
std::vector<std::string> NXSelWriter::availableDataSources() const{
std::vector<std::string> datasources;
Tango::DeviceData dss = _configProxy->command_inout("AvailableDataSources");
dss >> datasources;
return datasources;
}
void NXSelWriter::openEntry(const std::string& xmlconf){
_xml = xmlconf;
Tango::DeviceAttribute xmlSetting("XMLSettings", _xml);
_writerProxy->write_attribute(xmlSetting);
char fmt[64], buf[64];
struct timeval tv;
struct tm *tm;
gettimeofday(&tv, NULL);
if((tm = localtime(&tv.tv_sec)) != NULL){
std::strftime(fmt, sizeof(fmt), _isoformat.c_str(), tm);
std::snprintf(buf, sizeof(buf), fmt, tv.tv_usec);
}
Json::Value record;
record["data"] = _dataRecord;
record["data"]["start_time"] = std::string(buf);
std::vector<std::string> recordNames = record["data"].getMemberNames();
std::set<std::string> missing;
std::set_difference(_clinitdv.begin(), _clinitdv.end(),
recordNames.begin(), recordNames.end(),
std::inserter(missing, missing.end()));
if(missing.size()){
std::stringstream sstream;
sstream << "Missing INIT CLIENT data: ";
for(std::set<std::string>::const_iterator it = missing.begin();
it != missing.end(); ++it )
sstream << (*it) << " ";
throw sstream.str();
}
std::string drecord = _jsonWriter.write(record);
Tango::DeviceAttribute jsonRecord("JSONRecord", drecord);
std::cout << "GLOBAL CLIENT DATA: " << drecord << std::endl;
_writerProxy->write_attribute(jsonRecord);
_writerProxy->command_inout("OpenEntry");
}
Json::Value NXSelWriter::getValue(std::string device) const{
Tango::DeviceProxy *dp = new Tango::DeviceProxy(device);
Tango::DeviceAttribute attr;
attr = dp->read_attribute("Value");
Tango::AttributeInfoEx info= dp->get_attribute_config("Value");
Tango::AttrDataFormat format = info.data_format;
int ttype = info.data_type;
Json::Value vl;
double db;
float fl;
bool bl;
long lg;
std::string st;
unsigned long ul;
std::vector<double> vdb;
std::vector<float> vfl;
std::vector<long> vlg;
std::vector<unsigned long> vul;
std::vector<std::string> vst;
if(format == 0){
if(ttype == Tango::DEV_DOUBLE || ttype == Tango::DEVVAR_DOUBLEARRAY){
attr >> db; vl = db;
}
else if(ttype == Tango::DEV_FLOAT || ttype == Tango::DEVVAR_FLOATARRAY){
attr >> fl; vl = fl;
}
else if(ttype == Tango::DEV_BOOLEAN || ttype == Tango::DEVVAR_BOOLEANARRAY){
attr >> bl; vl = bl;
}
else if(ttype == Tango::DEV_SHORT || ttype == Tango::DEVVAR_SHORTARRAY){
attr >> lg; vl = static_cast<short>(lg);
}
else if(ttype == Tango::DEV_LONG || ttype == Tango::DEVVAR_LONGARRAY){
attr >> lg; vl = static_cast<int>(lg);
}
else if(ttype == Tango::DEV_LONG64 || ttype == Tango::DEVVAR_LONG64ARRAY){
attr >> lg; vl = static_cast<long long>(lg);
}
else if(ttype == Tango::DEV_STRING || ttype == Tango::DEVVAR_STRINGARRAY){
attr >> st; vl = st;
}
else if(ttype == Tango::DEV_USHORT || ttype == Tango::DEVVAR_USHORTARRAY){
attr >> lg; vl = static_cast<unsigned short>(lg);
}
else if(ttype == Tango::DEV_ULONG || ttype == Tango::DEVVAR_ULONGARRAY){
attr >> lg; vl = static_cast<unsigned int>(lg);
}
else if(ttype == Tango::DEV_ULONG64 || ttype == Tango::DEVVAR_ULONG64ARRAY){
attr >> ul; vl = static_cast<unsigned long long>(ul);
}
}
else if(format == 1){
if(ttype == Tango::DEV_DOUBLE || ttype == Tango::DEVVAR_DOUBLEARRAY){
attr >> vdb;
vl = Json::arrayValue;
for(size_t i = 0; i<vdb.size(); i++)
vl[int(i)] = vdb[i];
}
else if(ttype == Tango::DEV_FLOAT || ttype == Tango::DEVVAR_FLOATARRAY){
attr >> vfl;
vl = Json::arrayValue;
for(size_t i = 0; i<vfl.size(); i++)
vl[int(i)] = vfl[i];
}
else if(ttype == Tango::DEV_BOOLEAN || ttype == Tango::DEVVAR_BOOLEANARRAY){
attr >> vlg;
vl = Json::arrayValue;
for(size_t i = 0; i<vlg.size(); i++)
vl[int(i)] = static_cast<bool>(vlg[i]);
}
else if(ttype == Tango::DEV_SHORT || ttype == Tango::DEVVAR_SHORTARRAY){
attr >> vlg;
vl = Json::arrayValue;
for(size_t i = 0; i<vlg.size(); i++)
vl[int(i)] = static_cast<short>(vlg[i]);
}
else if(ttype == Tango::DEV_LONG || ttype == Tango::DEVVAR_LONGARRAY){
attr >> vlg;
vl = Json::arrayValue;
for(size_t i = 0; i<vlg.size(); i++)
vl[int(i)] = static_cast<int>(vlg[i]);
}
else if(ttype == Tango::DEV_LONG64 || ttype == Tango::DEVVAR_LONG64ARRAY){
attr >> vlg;
vl = Json::arrayValue;
for(size_t i = 0; i<vlg.size(); i++)
vl[int(i)] = static_cast<long long>(vlg[i]);
}
else if(ttype == Tango::DEV_STRING || ttype == Tango::DEVVAR_STRINGARRAY){
attr >> vst;
vl = Json::arrayValue;
for(size_t i = 0; i<vst.size(); i++)
vl[int(i)] = vst[i];
}
else if(ttype == Tango::DEV_SHORT || ttype == Tango::DEVVAR_USHORTARRAY){
attr >> vlg;
vl = Json::arrayValue;
for(size_t i = 0; i<vlg.size(); i++)
vl[int(i)] = static_cast<unsigned short>(vlg[i]);
}
else if(ttype == Tango::DEV_LONG || ttype == Tango::DEVVAR_ULONGARRAY){
attr >> vlg;
vl = Json::arrayValue;
for(size_t i = 0; i<vlg.size(); i++)
vl[int(i)] = static_cast<unsigned int>(vlg[i]);
}
else if(ttype == Tango::DEV_ULONG64 || ttype == Tango::DEVVAR_ULONG64ARRAY){
attr >> vul;
vl = Json::arrayValue;
for(size_t i = 0; i<vul.size(); i++)
vl[int(i)] = static_cast<unsigned long long>(vul[i]);
}
}
return vl;
}
void NXSelWriter::loopStep(Json::Value ldata){
Json::Value srecord;
Json::Value record;
record["data"] = _dataRecord;
srecord["data"] = ldata;
std::vector<std::string> recordNames = record["data"].getMemberNames();
std::vector<std::string> srecordNames = srecord["data"].getMemberNames();
std::set<std::string> tmp;
std::set<std::string> missing;
for (std::map <std::string, std::string>::iterator mi = _clstepds.begin();
mi != _clstepds.end(); ++mi)
missing.insert(mi->first);
tmp.clear();
std::set_difference(missing.begin(), missing.end(),
recordNames.begin(), recordNames.end(),
std::inserter(tmp, tmp.end()));
missing.clear();
std::set_difference(tmp.begin(), tmp.end(),
srecordNames.begin(), srecordNames.end(),
std::inserter(missing, missing.end()));
for(std::set<std::string>::const_iterator it = missing.begin();
it != missing.end(); ++it){
try{
Json::Value value = getValue(*it);
srecord["data"][*it] = value;
}
catch(...){
}
}
std::string drecord = _jsonWriter.write(srecord);
Tango::DeviceAttribute jsonRecord("JSONRecord", drecord);
std::cout << "LOCAL CLIENT DATA: " << drecord << std::endl;
_writerProxy->write_attribute(jsonRecord);
Tango::DeviceData tdata;
tdata << drecord;
_writerProxy->command_inout("Record", tdata);
}
void NXSelWriter::closeEntry(){
char fmt[64], buf[64];
struct timeval tv;
struct tm *tm;
Json::Value record;
record["data"] = _dataRecord;
gettimeofday(&tv, NULL);
if((tm = localtime(&tv.tv_sec)) != NULL){
std::strftime(fmt, sizeof(fmt), _isoformat.c_str(), tm);
std::snprintf(buf, sizeof(buf), fmt, tv.tv_usec);
}
record["data"]["end_time"] = std::string(buf);
std::vector<std::string> recordNames = record["data"].getMemberNames();
std::set<std::string> missing;
std::set_difference(_clfinaldv.begin(), _clfinaldv.end(),
recordNames.begin(), recordNames.end(),
std::inserter(missing, missing.end()));
if(missing.size()){
std::stringstream sstream;
sstream << "Missing INIT CLIENT data: ";
for(std::set<std::string>::const_iterator it = missing.begin();
it != missing.end(); ++it)
sstream << (*it) << " ";
throw sstream.str();
}
std::string drecord = _jsonWriter.write(record);
Tango::DeviceAttribute jsonRecord("JSONRecord", drecord);
std::cout << "GLOBAL CLIENT DATA: " << drecord << std::endl;
_writerProxy->write_attribute(jsonRecord);
_writerProxy->command_inout("CloseEntry");
}
void NXSelWriter::closeFile() const{
_writerProxy->command_inout("CloseFile");
}
To compile the program one performs
haspp09# g++ -std=c++0x -I /usr/include/tango/ nxselwriter.cc selclient.cc -ltango -lomniORB4 ֒→ lomniDynamic4 -lomnithread -ljsoncpp -o selclient
Chapter 8
Taurus Tools
This chapter describes selected Taurus features.
8.1
taurusform
The taurusform application provides full access to a preselected group of devices. The widget 8.1
is started by, e.g.:
[p04user@haspp04exp2:˜$ taurusform p04/hviseg/exp2.01 p04/hviseg/
֒→ exp2.02 p04/hviseg/exp2.03 p04/hviseg/exp2.04
Figure 8.1: taurusform: device selector widget
After a device has been selected, its attributes and commands are accessible via 8.1.
If taurusform is invoked without parameters, the devices are selected using this 8.1 widget.
8.2
taurusimage
• Start the Taurus Image Application
[xxx@xxx:bin$ taurusimage "TANGO_HOST:DEVICE_NAME/ATTRIBUTE"
e.g.[xxx@xxx:bin$ taurusimage "haso228y:1000/test/limaccds/01/
֒→ video_last_image"
Application help. Show other options on this application.
[xxx@xxx:bin$ taurusimage -h
136
Figure 8.2: taurusform: device widget
If everything is right, the Figure 8.4 should be shown.
• Taurus Image GUI-Toolbar The following table is description for Figure 8.5.
Figure 8.3: taurusform: the main widget
Button
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Description
selection tool
zoom tool. zoom in a selected rectangle region
configure the parameters of current selected item. e.g.colormap. Current items are shown
in window 5 in Figure 8.6
configure the paramaters of the grid. e.g. Background color
configure the axes style
change the colormap of the image. e.g. binary,gray
rectangle snapshot. Unkown functionalites, it looks it does not work
image statistics. shows data statistics (e.g. maximum and minimum value of z ) in the
selected region
cross selection. shows the x and y direction pixel values respectively by selecting an point
in the image
average cross selection. In a selected rectangle region,shows average x and y direction
pixel values respectively
save the image in to local directory
print the image
help. show other operations. e.g. zoom: right-click+mouse move
change taurus model. change another model that you want to visualize
Figure 8.4: TaurusImage Panel
Figure 8.5: taurusimage toolbar
• Taurus Image GUI-Panel Below table describes the different windows in Figure 8.6
Window
1
2
3
4
5
8.3
Description
shows the value of x direction when the cross or average cross selection is enabled
shows the value of y direction when the cross or average cross selection is enabled
shows the realtime image data from selected tango server
contrast adjustment tool. This functionality can be enabled by right click in window
3.Then select the ’contrast adjustment’ option
shows the current item list in window 3.e.g. image, cross selection marker
taurus device, accessing MacroServer environment
Here is an example of how an environment variable can be accessed via a taurus device:
p09/door/haso107d1.01 [3]: import taurus
p09/door/haso107d1.01 [4]: door = taurus.Device(’p09/door/haso107d1.01’)
p09/door/haso107d1.01 [5]: door.macro_server.setEnvironment( "var", "value"
Figure 8.6: TaurusImage Panel with all functionalities
p09/door/haso107d1.01 [6]: door.macro_server.getEnvironment( "var")
Result [6]: ’value’
p09/door/haso107d1.01 [7]: door.macro_server.removeEnvironment( "var")
Chapter 9
Diffractometer Software
9.1
Configuration
Section 11.3.2 describes how the a Diffractometer PseudoMotor is specified in /online dir/online.xml.
In addition the environment variables DiffracDevice and Psi have to be set, e.g.:
p09/door/haso107d1.01 [2]: senv DiffracDevice controller/diffrace6c/e6cctrl
p09/door/haso107d1.01 [3]: senv Psi pm/e6cctrl/4
p09/door/haso107d1.01 [4]: lsenv
Name
Value
Type
--------------- ------------------------------- -----DiffracDevice
controller/diffrace6c/e6cctrl
str
Psi
pm/e6cctrl/4
str
p09/door/haso107d1.01 [2]: wh
H K L =
1.00000
1.00000
0.00000
Azimuth (Psi) = 180.00000
Wavelength = 1.54000
Delta
93.09800
9.2
Theta
46.54900
Chi
45.00000
Phi
90.00000
Mu
0.00000
Macros
9.2.1 add reflection
class add_reflection(Macro, _diffrac):
""" Add reflection at the botton of reflections list """
param_def = [
[’H’, Type.Float, None, "H
[’K’, Type.Float, None, "K
[’L’, Type.Float, None, "L
[’affinement’, Type.Float,
value"],
value"],
value"],
-999., "Affinement"]
141
Gamma
0.00000
]
p09/door/haso107d1.01 [27]: add_reflection 1 0 1
p09/door/haso107d1.01 [28]: pa
Eulerian 6C Geometry, bissector_vertical
Sector [ToDo]
1st Reflection (index 0):
H K L :
1.0000
0.0000
0.0000
Affinement, Relevance : 0 1
mu theta chi phi gamma delta:
0.0000
30.8860
0.0000
2nd Reflection (index 1):
H K L :
1.0000
1.0000
-0.0000
Affinement, Relevance : 0 1
mu theta chi phi gamma delta:
0.0000
46.5490
45.0000
3rd Reflection (index 2):
H K L :
1.0000
0.0000
1.0000
Affinement, Relevance : 0 1
mu theta chi phi gamma delta:
0.0000
46.5490
0.0000
Lattice Constants (lengths / angles):
real space = 1.5 1.5 1.5 / 90.0 90.0 90.0
Azimuthal Reference:
90.0000
90.0000
45.0000
[ToDo] = [ToDo]
Lambda = 1.54
Cut Points:
[ToDo]
9.2.2 affine
class affine(Macro, _diffrac):
"""Affine current crystal"""
p09/door/haso107d1.01 [27]: affine
9.2.3 br (move to reciprocal space coordinate)
class br(Macro, _diffrac):
"""The br macro is used to move the diffractometer to the reciprocal sp
coordinates given by H, K and L. If a fourth parameter is given, the co
of angles to be set is the correspondig to the given index. The index o
angles combinations are then changed."""
param_def = [
[’H’, Type.Float, None, "H value"],
[’K’, Type.Float, None, "K value"],
[’L’, Type.Float, None, "L value"],
[’AnglesIndex’, Type.Integer, -1, "Angles index"]
]
p09/door/haso107d1.01 [3]:
H = 1.00381 K = 0.99617 L
H = 1.04769 K = 0.87619 L
...
H = 1.00000 K = 0.00000 L
br 1 0 0
= 0.00309
= 0.07269
= 0.00000
9.2.4 ca, caa (hkl2Angles)
class ca(Macro, _diffrac):
"""Calculate motor positions for given H K L according to the current
operation mode (trajectory 0)."""
param_def = [
[’H’, Type.Float, None, "H value for the azimutal vector"],
[’K’, Type.Float, None, "K value for the azimutal vector"],
[’L’, Type.Float, None, "L value for the azimutal vector"],
]
class caa(Macro, _diffrac):
"""Calculate motor positions for given H K L according to the current
operation mode (all trajectories, aka angle sets)"""
param_def = [
[’H’, Type.Float, None, "H value for the azimutal vector"],
[’K’, Type.Float, None, "K value for the azimutal vector"],
[’L’, Type.Float, None, "L value for the azimutal vector"],
]
p09/door/haso107d1.01 [4]: ca 1 0 0
Trajectory 0 (more trajectories by caa H K L)
Azimuth (Psi) = 135.00000
Wavelength = 1.54000
Delta
61.77224
Theta
30.88612
Chi
-0.00000
Phi
90.00000
Mu
0.00000
Gamma
0.00000
9.2.5 compute u
class compute_u(Macro, _diffrac):
""" Compute U matrix with reflections 0 and 1 """
p09/door/haso107d1.01 [22]: compute_u
Computing U with reflections 0 and 1
Not file name given as argument and not SaveCrystalFile environment defined
Data not saved
9.2.6 freeze (psi)
class freeze(Macro, _diffrac):
""" Set psi value for psi constant modes """
p09/door/haso107d1.01 [24]: setmode 11
Now using psi_constant_vertical mode
p09/door/haso107d1.01 [25]: freeze psi 45
9.2.7 getmode, setmode
class getmode(Macro, _diffrac):
"""Get operation mode."""
class setmode(Macro, _diffrac):
"""Set operation mode."""
param_def = [
[’new_mode’, Type.Integer, -1, "Mode to be set"]
]
p09/door/haso107d1.01 [9]: setmode 1
Now using bissector_vertical mode
p09/door/haso107d1.01 [10]: getmode
bissector_vertical
9.2.8 hscan, kscan, lscan, hklscan
class hscan(Macro, _diffrac):
"Scan h axis"
param_def = [
[’start_pos’,
[’final_pos’,
[’nr_interv’,
[’integ_time’,
]
Type.Float,
Type.Float,
Type.Integer,
Type.Float,
None,
None,
None,
None,
’Scan start position’],
’Scan final position’],
’Number of scan intervals’],
’Integration time’],
None,
None,
None,
None,
’Scan start position’],
’Scan final position’],
’Number of scan intervals’],
’Integration time’],
class kscan(Macro, _diffrac):
"Scan k axis"
param_def = [
[’start_pos’,
[’final_pos’,
[’nr_interv’,
[’integ_time’,
]
Type.Float,
Type.Float,
Type.Integer,
Type.Float,
class lscan(Macro, _diffrac):
"Scan l axis"
param_def = [
[’start_pos’,
[’final_pos’,
[’nr_interv’,
[’integ_time’,
]
Type.Float,
Type.Float,
Type.Integer,
Type.Float,
None,
None,
None,
None,
’Scan start position’],
’Scan final position’],
’Number of scan intervals’],
’Integration time’],
class hklscan(Macro, _diffrac):
"Scan h k l axes"
param_def = [
[’h_start_pos’, Type.Float,
None, ’Scan h start position’],
[’h_final_pos’, Type.Float,
None, ’Scan h final position’],
[’k_start_pos’, Type.Float,
None, ’Scan k start position’],
[’k_final_pos’, Type.Float,
None, ’Scan k final position’],
[’l_start_pos’, Type.Float,
None, ’Scan l start position’],
[’l_final_pos’, Type.Float,
None, ’Scan l final position’],
[’nr_interv’, Type.Integer, None, ’Number of scan intervals’],
[’integ_time’, Type.Float,
None, ’Integration time’],
]
p09/door/haso107d1.01 [32]: kscan 0 1 11 0.1
ScanDir is not defined. This operation will not be stored persistently. Use
Scan #1 started at Tue Jan 20 18:17:21 2015. It will take at least 0:00:01.
Moving to start positions...
#Pt No
e6cctrl_k exp_t01
d1_c01
dt
0
-4.29333e-32
0.1
1
0.496505
1
0.0909015
0.1
2
2.13244
2
0.18182
0.1
6
3.57856
3
0.272723
0.1
11
5.08008
4
0.363634
0.1
19
6.64288
5
0.454543
0.1
30
8.13935
6
0.545446
0.1
46
9.60844
7
0.636358
0.1
63
10.2903
8
0.727268
0.1
74
11.0591
9
0.818178
0.1
86
12.7656
10
0.909093
0.1
86
14.3544
11
1
0.1
88
16.019
Scan #1 ended at Tue Jan 20 18:17:37 2015, taking 0:00:16.183987.Dead time
p09/door/haso107d1.01 [34]: hklscan 1 1 0 1 0 0 11 0.1
ScanDir is not defined. This operation will not be stored persistently. Use
Scan #2 started at Tue Jan 20 18:19:38 2015. It will take at least 0:00:01.
Moving to start positions...
#Pt No
e6cctrl_h e6cctrl_k e6cctrl_l exp_t01
d1_c01
dt
0
0.999997 -4.29333e-32 -9.66773e-17
0.1
75
0.4252
1
0.999999 0.0909015 -8.76262e-06
0.1
106
1.91755
2
0.999995
0.18182
-8.86968e-06
0.1
121
3.42069
3
0.999994
0.272723 -1.93355e-16
0.1
131
4.90196
4
1
0.363634
0
0.1
128
6.41766
5
0.999994
0.454543
0
0.1
114
7.94115
6
1
0.545446 -3.86709e-16
0.1
93
9.4274
7
0.999999
0.636358 -6.76741e-16
0.1
70
10.1194
8
1.00001
0.727268 1.07905e-05
0.1
47
10.6942
9
1.00001
0.818178 -2.90032e-16
0.1
32
12.2742
10
0.999992
0.909093 -1.17937e-05
0.1
17
13.8966
11
1
1
-3.86709e-16
0.1
9
15.521
Scan #2 ended at Tue Jan 20 18:19:54 2015, taking 0:00:15.783787.Dead time
9.2.9 or0, or1, or swap
class or0(Macro, _diffrac):
"""Set primary orientation reflection."""
param_def = [
[’H’, Type.Float, None, "H value"],
[’K’, Type.Float, None, "K value"],
[’L’, Type.Float, None, "L value"],
]
class or1(Macro, _diffrac):
"""Set secondary orientation reflection."""
param_def = [
[’H’, Type.Float, None, "H value"],
[’K’, Type.Float, None, "K value"],
[’L’, Type.Float, None, "L value"],
]
class or_swap(Macro, _diffrac):
"""Swap values for primary and secondary vectors."""
p09/door/haso107d1.01 [14]: or0 1 0 0
U can not be computed. Only one reflection
[ move to a new angular position ]
p09/door/haso107d1.01 [16]: or1 1 1 0
Computing U with reflections 0 and 1
Not file name given as argument and not SaveCrystalFile environment defined
Data not saved
p09/door/haso107d1.01 [28]: or_swap
p09/door/haso107d1.01 [29]: pa
Eulerian 6C Geometry, bissector_vertical
Sector [ToDo]
1st Reflection (index 0):
H K L :
1.0000
1.0000
-0.0000
Affinement, Relevance : 0 1
mu theta chi phi gamma delta:
0.0000
46.5490
45.0000
90.0000
2nd Reflection (index 1):
H K L :
1.0000
0.0000
0.0000
Affinement, Relevance : 0 1
mu theta chi phi gamma delta:
0.0000
30.8860
0.0000
90.0000
Lattice Constants (lengths / angles):
real space = 1.50000521641 1.4999998163 1.49999988198 / 90.00
Azimuthal Reference:
[ToDo] = [ToDo]
Lambda = 1.54
Cut Points:
[ToDo]
9.2.10 newcrystal, savecrystal
class newcrystal(Macro, _diffrac):
""" Create a new crystal (if it does not exist) and select it. """
param_def = [
[’crystal_name’,
]
Type.String,
None, ’Name of the crystal to add
class savecrystal(Macro,_diffrac):
param_def = [
[’crystal_file’,
]
Type.String,
"Not set", ’Name of the file (with
n.n.
9.2.11 pa (print diffractometer info)
class pa(Macro, _diffrac):
"""Prints information about the active diffractometer."""
p09/door/haso107d1.01 [5]: pa
Eulerian 6C Geometry, bissector_vertical
Sector [ToDo]
Lattice Constants (lengths / angles):
real space = 1.5 1.5 1.5 / 90.0 90.0 90.0
Azimuthal Reference:
[ToDo] = [ToDo]
Lambda = 1.54
Cut Points:
[ToDo]
9.2.12 printmove
class printmove(Macro,_diffrac):
n.n.
9.2.13 setaz (reference vector)
class setaz(Macro, _diffrac):
""" Set hkl values of the psi reference vector"""
param_def = [
[’PsiH’, Type.Float, None, "H value of psi reference vector"],
[’PsiK’, Type.Float, None, "K value of psi reference vector"],
[’PsiL’, Type.Float, None, "L value of psi reference vector"],
]
n.n.
9.2.14 setlat
class setlat(Macro, _diffrac):
"""Set the crystal lattice parameters a, b, c, alpha, beta, gamma.
for the currently active diffraction pseudo motor controller."""
param_def = [
[’a’, Type.Float, None, "Lattice ’a’
[’b’, Type.Float, None, "Lattice ’b’
[’c’, Type.Float, None, "Lattice ’c’
[’alpha’, Type.Float, None, "Lattice
[’beta’, Type.Float, None, "Lattice
[’gamma’, Type.Float, None, "Lattice
]
parameter"],
parameter"],
parameter"],
’alpha’ parameter"],
’beta’ parameter"],
’gamma’ parameter"]
p09/door/haso107d1.01 [12]: setlat 1.5 1.5 1.5 90.0 90.0 90.0
U can not be computed. No reflection
9.2.15 setor0, setor1
class setor0(Macro, _diffrac):
"""Set primary orientation reflection. Alternative to or0"""
class setor1(Macro, _diffrac):
"""Set secondary orientation reflection. Alternative to or1"""
p09/door/haso107d1.01 [18]: setor1
Computing U with reflections 0 and 1
Not file name given as argument and not SaveCrystalFile environment defined
Data not saved
9.2.16 setorn
class setorn(Macro, _diffrac):
"""Set orientation reflection indicated by the index."""
param_def = [
[’i’, Type.Integer, None, "reflection index (starting at 0)"],
]
p09/door/haso107d1.01 [19]: setorn 0
Error: prepare() takes exactly 1 argument (2 given)
9.2.17 th2th
class th2th(Macro):
"""th2th - scan:
Relative scan around current position in del and th with d_th=2*d_delta
"""
param_def = [
[’rel_start_pos’, Type.Float,
-999, ’Scan start position’],
[’rel_final_pos’, Type.Float,
-999, ’Scan final position’],
[’nr_interv’, Type.Integer, -999, ’Number of scan intervals’],
[’integ_time’, Type.Float,
-999, ’Integration time’]
]
p09/door/haso107d1.01 [35]: th2th -0.1 0.1 11 0.1
An error occurred while running Macro ’th2th(-0.1, 0.1, 11, 0.1)’:
’NoneType’ object has no attribute ’getPosition’
9.2.18 wh (print positions and parameterst)
class wh(Macro, _diffrac):
"""wh - where, principal axes and reciprocal space
Prints the current reciprocal space coordinates (H K L) and the user
positions of the principal motors. Depending on the diffractometer geom
other parameters such as the angles of incidence and reflection (ALPHA
BETA) and the incident wavelength (LAMBDA) may be displayed."""
p09/door/haso107d1.01 [6]: wh
H K L =
1.00000
Azimuth (Psi) =
0.00000
135.00000
0.00000
Wavelength =
1.54000
Delta
61.77200
Theta
30.88600
Chi
0.00000
Phi
90.00000
Mu
0.00000
Gamma
0.00000
Chapter 10
Tools
10.1
ECMonitor
The ECMonitor.py is a tool monitoring the status of selected control system components (MacroServer,
Pool, ZMX):
$ ECMonitor.py
Usage: ECMonitor.py -x [-l [-t <timeSleep> ]]
Checks the status of the MacroServer, the Pool and ZMXs
and notifies users in case of trouble via email and/or sms
-x
the check is executed once, the errors are displayed, no further no
-x -l the check is repeatedly executed
-x -l -n [email protected]
-x -l -n [email protected],sms/[email protected]
Options:
-h, --help
-x
-l
-n NOTIFY
-t TIMESLEEP
show this help message and exit
execute
execute repeatedly
comma separated notify list, no blanks
sleep time when looping, def. 10
If it is used for the first time on an experiment PC, it is executed in single-shot mode:
$ ECMonitor.py -x
This command displays all errors. After they have been fixed the tool can be executed repeatedly:
$ ECMonitor.py -x -l -n [email protected],sms/[email protected]
This way the procedure is executed in a loop. The wait time is 10s. If errors occur the listed
addresses are notified. The notifaction happens only once when the status changes from ’good’ to
’error’.
10.2
MotorLogger
The MotorLogger creates new versions of the files /online dir/MotorLogs/motorLog.lis /online dir/MotorLogs/m
151
$ MotorLogger.py
Usage: MotorLogger.py -x [-q] [-c <progName>]
Creates new versions of /online_dir/MotorLogs/motorLog.lis and ˜.py
Device names are printed, if stdout is a TTY AND -q is not supplied.
<progName> is written to the log-file, if supplied.
Options:
-h, --help
-x
-q
-c CALLER
show this help message and exit
execute
quiet
name of the calling program, e.g. ’spock’ or ’cron’
The MotorLogger is invoked by a cron job and during the startup of each Spock session.
Chapter 11
Sardana configuration
11.1
Setting up the Pools and the MacroServers
Warning: Currently the Sardana configuration of the ExperimentPCs (ExpPC) undergoes considerable changes. In the future, all ExpPCs of a beamline will have separate Tango DBs. So far, this
is true only at P01, P02, P03, P03NANO, P06, P08, P09, P10 and P64.
The configuration scripts are executed on ExperimentPCs (ExpPC). The term ExpPC refers to
a PC used to conduct measurements, runs spock, online, etc. There are beamlines that have
only one ExpPC, e.g.: haspp03, others have more, e.g.: haspp09, haspp09dif, haspp09mag and
haspp09haxps.
Each ExpPC runs its own Pool, exporting one Pool device, and its own MacroServer, exporting
three MacroServer devices, each one with a separate Door. To have more than one Door available
allows the beamline staff to assign certain tasks to Doors, e.g. p09/door/haspp09.01 could be used
for general beamline operations, whereas the other Doors are used for specific purposes.
ExpPCs have separate Tango DBs, thereby separate Tango name spaces. Pools is populated with
devices contained on /online dir/online.xml. This file contains references to Tango devices from
several Tango DBs. The file /online dir/online.xml serves two purposes:
Visibility By adding a device to online.xml this devices becomes available for beamline control and
data acquisition on a specific ExpPC. In other words: Tango devices not appearing in online.xml are invisible to the user.
Naming The entries of online.xml define Tango aliases for devices. Aliases are important. They
unambiguously identify devices in a Tango DB. Since each ExpPC of a beamline sees its
own Tango name space, aliases can be re-used. To clarify this: assume that a beamline has
two ExpPCs: PC-A and PC-B. Our configuration allows to use the alias ’theta’ on PC-A for
some motor and on PC-B for a different motor.
The following table gives an overview of the scripts:
ardanaAIO.py -x Removes the Pool and MacroServer, converts online.xml to onlineSardana.xml, creates the
Pool and MacroServer, starts them and populates the Pool with devices (all-in-one). This
script is equivalent to:
– SardanaShutdown.py -x
– SardanaConvert.py -f /online dir/online.xml -o /online dir/onlineSardana.xml
– SardanaStartup.py -f /online dir/onlineSardana.xml
153
SardanaAIO.py -x can be executed after /online dir/online.xml has been edited.
ardanaStartup.py Prepares the Pool and the Macroserver using an xml file, by default /online dir/onlineSardana.xml:
– It creates one Pool per ExperimentPC, if there is no Pool so far.
– It creates three MacroServer instances per ExperimentPC, if there are no MacroServers
so far. This is due to the requirement to have more that one Door available. This Spock
sessions may speak/listen to the same or separate Door.
– The Pool and the MacroServer are injected into the Starter, level 2 and 3.
– The Pool and the MacroServer are started.
– All devices are deleted from the Pool, if the Pool is not empty.
– The Pools are populated with devices.
– A MeasurementGroup is is created.
– The file /online dir/SardanaConfig.py is executed to setup some environment variables. An example for such a configuration file can be found here 11.1.1.
The meaning of the command line parameters:
-b p09 The beamline is used for the first part of the Pool and MacroServer names, as in:
p09/pool/haspp09.
-f ... The xml file which contains the devices to be created within the Pool. In general this is
/online dir/onlineSardana.xml. See the section 11.3 for explanations of the syntax.
In general ExperimentPCs are known to the startup script. Whether the local host is known,
can be checked by invoking the script without command line arguments:
haspp09:˜> SardanaStartup.py
Usage:
SardanaStartup.py -f /online_dir/onlineSardana.xml
(haspp09 is known, -b: p09)
Options:
-h, --help
-b BEAMLINE
-f XMLFILE
show this help message and exit
name of the beamline
xmlfile, e.g. online.xml
This output tells us that haspp09 is known and that we can execute the startup script with this
command:
SardanaStartup.py -f /online_dir/onlineSardana.xml
rdanaConvert.py Converts a non-Sardana xml file into a Sardana xml file, e.g.:
SardanaConvert.py -f /online dir/online.xml -o /online dir/onlineSardan
anaShutdown.py Deletes the devices from the local Pool, stops the MacroServer and the Pool and deletes them,
e.g.: SardanaShutdown.py -x
rtMacroServer.py Restarts the MacroServer, e.g.:
SardanaRestartMacroServer.py -x
naRestartBoth.py Stops the MacroServer, restarts the Pool and starts the MacroServer, e.g.:
SardanaRestartBoth.py -x
FromTS2Pool.py Copies the units limits from the Tango servers (motors) to the Pool devices, e.g.:
SardanaLimitsFromTs2Pool.py
SardanaStatus.py Displays information about the Sardana: the controllers, the Pool devices, the contents of the
MeasurementGroup, the state of the Pool, MacroServer and Door, e.g.:
haspp09:˜> SardanaStatus.py
Local DoorNames(s): [’p09/door/haspp09.01’, ’p09/door/haspp09.02’, ’p0
MacroServer
p09/macroserver/haspp09.01
state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09
state ON
MG: [u’mono_t01’, u’exp_c01’]
MacroServer
p09/macroserver/haspp09.02
state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09
state ON
MG: [u’mono_t01’, u’exp_c01’]
MacroServer
p09/macroserver/haspp09.03
state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09
state ON
MG: [u’mono_t01’, u’exp_c01’]
If the script is invoked using the -f parameter, information about the Pool controllers and Pool
devices are displayed:
haspp09:˜> SardanaStatus.py -f
Local DoorNames(s): [’p09/door/haspp09.01’, ’p09/door/haspp09.02’, ’p0
--- Pool ( local) p09/pool/haspp09
state ON
ControllerList
am_gap ON, 1 devices: gap
am_tga ON, 1 devices: tga_off
analyzer_eh1 ON, 1 devices: analyzer
atto300_exp ON, 2 devices: atto1, atto2
dcm_energy ON, 1 devices: energyfmb
dcm_motor_mono_01 ON, 1 devices: dcm_bragg
dgg2_exp_01 ON, 1 devices: exp_t01
dgg2_exp_02 ON, 1 devices: exp_t02
dgg2_mono_01 ON, 1 devices: mono_t01
dgg2_mono_02 ON, 1 devices: mono_t02
e6cctrl ON, 8 devices: e6cctrl_psi, e6cctrl_h ...
mca8701_exp ON, 1 devices: exp_mca01
mult_mono_01 ON, 1 devices: multiplemotormono1
...
The command files mentioned so far are most important. The follwing may also be helpful now
and then:
rtMacroServer.py Starts the local MacroServer, e.g.:
SardanaStartMacroServer.py -x
pMacroServer.py Stops the local MacroServer, e.g.:
SardanaStopMacroServer.py -x
danaStartPool.py Starts the local Pool, e.g.:
SardanaStartPool.py -x
danaStopPool.py Stops the local Pool, e.g.:
SardanaStopPool.py -x
11.1.1
Finishing the Sardana configuration: /online dir/SardanaConfig.py
The file /online dir/SardanaConfig.py is executed at the end of the SardanaStartup.py script. This
is an example:
#!/usr/bin/python
#
# /online_dir/SardanaConfig.py
#
import PyTango
import HasyUtils
import sys
print "\n--- executing %s \n" % __file__
#
# change the configuration of several attributes
#
devices = { "motor/omsvme58_d1/1":
{ "Position": { "Label": "Ort1", "Format": "%12g", "Unit": "mm
"Offset": { "Label": "Aufsatz", "Format": "%11g", "Unit": "mm
},
"motor/omsvme58_d1/2":
{ "Position": { "Label": "Ort2", "Format": "%12g", "Unit": "Deg
"Offset": { "Label": "Aufsatz2", "Format": "%11g", "Unit": "D
},
}
for dev in devices.keys():
print "device", dev
try:
proxy = PyTango.DeviceProxy( dev)
except:
print "The attribute configuration failed for %s" % dev
continue
for attr in devices[dev].keys():
print " ",attr
attrInfo = proxy.get_attribute_config( attr)
for attrCfg in devices[dev][attr].keys():
temp = "attrInfo.%s = \"%s\"" % (attrCfg.lower(), devices[dev][
print " ",temp
exec temp
proxy.set_attribute_config( attrInfo)
#
# Set some view options
#
HasyUtils.configMacroServer(
[ "setvo ShowDial False",
"setvo ShowCtrlAxis True"])
#
# Set some environment variables. ’Cautious’ means that variables
# are not overridden, if the exist already
#
HasyUtils.setEnvCautious(
{"JsonRecorder": "True",
"ScanDir": "/home/kracht/temp",
"ScanFile": "tst.fio"})
sys.exit( 0)
11.2
TangoExplorer
The command ’TangoExplorer.py’ (TE) invokes an application that gives an overview of the Tango
configuration of a beamline and it supports several actions. The TANGO HOSTs are specified in
/online dir/dbHostsTangoExplorer.lis.
Warning: The TE supports write operations, single and multiple action. Be careful to do a Multiple
Action write operation!
The TangoExplorer can be used to search for servers. In the 11.2 the user specified ’oms galil’ and
pressed the ’Search’ button. The output widget contains the results.
Figure 11.3 shows how the position attribute of some motor is selected. The user may enter a new
position in the “Single Action” frame and press ’Set’.
Figure 11.4 shows how the positions of all devices matching a pattern are displayed.
Figure 11.4 shows the Python code which is generated by ’Misc’ - ’Create Code’ after the multiple
read has been executed.
Figure 11.6 gives an example of a ’Multiple Action’ write operation. In this case the SampleTime
attribute of two DGG2 devices have been selected. After pressing the ’Write’ button in the ’Multiple
Action’ frame, the resulting actions are displayed in the log window and the user has to press
’Exec’ to execute them. This two-step process is a security measure because ’Multiple Action’
write operations can be very dangerous.
11.2.1 TangoExplorer: Pattern matching
For selecting hosts, servers, devices, attribute and properties in the ’Server’ and ’Multi Action’
frames wildcards can be supplied. Here is the explanation by examples:
Figure 11.1: TangoExplorer
Matches are case insensitive
Examples:
haspp03nano
haspp03nano
haspp03nano
haspp03nano
haspp03nano
vs.
vs.
vs.
vs.
vs.
ˆhaspp03nano$ True
haspp03
True
pp03
True
haspp03$
False
ˆhaspp03
True
Figure 11.2: TangoExplorer searching servers
haspp03nano vs. ˆaspp03
haspp03nano vs. *
SIS3820
SIS3820
SIS3820
SIS3820
SIS3820
SIS3820
vs.
vs.
vs.
vs.
vs.
vs.
ˆsis3820$ True
sis
True
38
True
3610$
False
ˆsis
True
ˆis
False
False
True
Figure 11.3: TangoExplorer: Motor position
SIS3820 vs. *
True
p08/timer/exp.01
p08/timer/exp.01
p08/timer/exp.01
p08/timer/exp.01
p08/timer/exp.01
p08/timer/exp.01
p08/timer/exp.01
vs.
vs.
vs.
vs.
vs.
vs.
vs.
ˆp08/timer/exp.01$
p08
timer
timer$
ˆp08
ˆ08
*
True
True
True
False
True
False
True
Figure 11.4: TangoExplorer: Multiple Action, read motor positions
Acceleration
Acceleration
Acceleration
Acceleration
Acceleration
Acceleration
Acceleration
vs.
vs.
vs.
vs.
vs.
vs.
vs.
ˆAcceleration$
accel
ccel
tionn$
ˆacce
ˆccel
*
True
True
True
False
True
False
True
Figure 11.5: TangoExplorer: Create Code
11.3
Selecting the devices: online.xml syntax
The file online.xml contains the configuration for Online. The file onlineSardana.xml, which is
used by the Sardana setup script SardanaStartup.py, is generated by SardanaConvert.py by converting online.xml. Both files are Online-compliant. If devices have to be added/removed/renamed
the changes have to be made in online.xml which is then converted. The conversion allows for
some reconfiguration.
Here is an example for an entry from online.xml:
<device>
<name>exp_mot01</name>
<sardananame>diffx</sardananame>
<tags>expert</tags>
<type>stepping_motor</type>
<module>oms58</module>
<device>p09/motor/exp.01</device>
<control>tango</control>
<hostname>haspp09mono:10000</hostname>
</device>
Figure 11.6: TangoExplorer: Multiple Action, write
Some notes about this tags (except for the entries corresponding to the diffractometer and to the
measurement groups, that will be described below):
• ¡sardananame¿ is an optional tag. If it is given, this will be the name with which the device
will be created in Sardana and therefore seen in Spock, and ¡name¿ has not any use in Sardana. If it is not give, ¡name¿ is used instead. Within Online devices are created always using
¡name¿ and a symbol ¡sardananame¿, in case given, is defined which refers to ¡name¿.
Usually if ¡name¿ and ¡sardananame¿ are defined, the tag ¡name¿ refers to the physical device
and ¡sardananame¿ refers to role of the device in the experimental setup.
• ¡tags¿ is an optional tag.
• ¡type¿ is not used by Sardana. Possible values are:
– stepping motor, for motors of the class OmsVme58
– timer, for timers
– counter, for counters
– dac, for DACs
– adc, for ADCs
– mca, for MCAs
– input register, for input registers
– output register, for output registers
– DETECTOR, for detectors
– type tango, for other tango devices
• ¡module¿ refers mainly to the Tango class. Possible values are: oms58, motor tango, dgg2,
sis3820, vfcadc, sis3610, tip830, spk, mca 8701, mca xia, mca sis3302new (for devices of
the class sis3302), mca sis3302 (for device of the class sis3302Client), module tango.
• ¡device¿ is the name of the Tango device.
• ¡control¿ is tango (not used in sardana, since we work only with Tango).
• ¡hostname¿ is the Tango Host in which the Tango device in ¡device¿ runs.
The conversion of the above entry to onlineSardana.xml would be:
<device>
<name>exp_mot01</name>
<sardananame>diffx</sardananame>
<tags>expert</tags>
<type>stepping_motor</type>
<module>oms58</module>
<device>p09/motor/exp.01</device>
<control>tango</control>
<hostname>haspp09mono:10000</hostname>
<pool>pool_haspp09</pool>
<controller>omsvme58_exp</controller>
<channel>1</channel>
<rootdevicename>p09/motor/exp</rootdevicename>
</device>
11.3.1 Detectors
The detectors are integrated in Sardana as two dimesional experimental channels. In this way they
can be added to the measurement group as counters and during a scan images will be taken at each
point.
Up to now he detectors implemented in Sardana are: Pilatus, PCO, MarCCD, PerkinElmer, LCX
camera, Lambda and the ones controlled via Lima.
This is how a detector has to be introduced in the online.xml file for having it in Sardana:
<device>
<name>detector_name</name>
<type>DETECTOR</type>
<module>module_name</module>
<device>my/detector/device.01</device>
<control>tango</control>
<hostname>hasppXY:10000</hostname>
</device>
The most important tag for recognizing that the device corresponds to a detector and from which
type is ¡module¿. The possible ¡module¿ values are:
• For Pilatus detector: pilatus100k, pilatus300k, pilatus1m, pilatus2m or pilatus6m. You can
use the one corresponding to your type of detector, for Sardana there is no difference.
• For PCO detector: pco, or any name containing pco, if we want to be more precise, for
example pco200.
• For MarCCD detector: marccd, or any name containing marccd.
• For PerkinElmer detector: perkinelmer, or any name containing perkinelmer
• For LCX camera: lcxcamera, or any name containing lcxcamera.
• For Lambda: lambda, or any name containing lambda.
• For detectors controlled via Lima: limaccd, or any name containing limaccd.
Remarks about the other tags:
• ¡name¿ is arbitrary, it will be the name of the detector inside of Sardana
• ¡type¿ has to be DETECTOR (important if used in Online, not used in Sardana)
• ¡device¿ has to be the name of the Tango device corresponding to the detector.
• ¡control¿ is tango (only relevant for Online)
• ¡hostname¿ is the Tango hostname your the detector Tango server runs in.
Certain detector attributes are not handled by the controllers because this would involve MacroServer
I/O which can be too time consuming. The following sub-sections present Macros that do this job.
Macro to setup the Lambda detector
The Lambda controller does not update certain Lambda attributes (FileStartNum, SaveFilePath
and FilePrefix) since this would involve Macroserver I/O which may be too time consuming. The
following code is an example for a Macro which sets these attribute. The Macro has to be executed
before each scan.
#!/usr/bin/env python
***
***
*** warning: this script hasn’t been tested to far
***
***
"""to setup the lamda detector, before each scan"""
__all__ = ["setup_lambda"]
from sardana.macroserver.macro import *
from sardana.macroserver.macro import macro
import os
import PyTango
class setup_lambda(Macro):
"""
To be executed before each scan using a lambda.
Reads the Macroserver environment and sets these attributes:
FileStartNum to 1
SaveFilePath, e.g.: ’/gpfs/current/raw/sample_00123/lmbd’
FilePrefix, e.g.:
’sample_00123’
Creates these directories (0777)
’/gpfs/current/raw/sample_00123’
’/gpfs/current/raw/sample_00123/lmbd’
"""
param_def = [
[’lmbd’, Type.String,
]
None, ’The Lambda detector, e.g. hasclambda
def run(self, lmbd):
scanFile = self.getEnv( ’ScanFile’)
if type(scanFile) is list:
scanFile = scanFile[0]
scanID = self.getEnv( ’ScanID’)
scanDir = self.getEnv( ’ScanDir’)
#
# prefix, e.g.: sample_00123
#
prefix = "%s_%05d" % (scanFile.split(’.’)[0], scanID)
saveFilePath = "%s/%s" % (scanDir, prefix)
os.mkdir( saveFilePath, 0777)
os.chmod( saveFilePath, 0777)
saveFilePathFinal = saveFilePath + "/lmbd"
os.mkdir( saveFilePathFinal)
os.chmod( saveFilePathFinal, 0777)
try:
lmbdProxy = PyTango.DeviceProxy( lmbd)
except:
self.output( "setup_lambda: failed to create proxy to %s" % lmb
return
lmbdProxy.FileStartNum = 1
lmbdProxy.SaveFilePath = saveFilePathFinal
lmbdProxy.FilePrefix = prefix
return
11.3.2 Diffractometer
The controller corresponding to the diffractometer can be created by the SardanaStartup.py script
if the corresponding entry was previously introduced in the online.xml file and converted to onlineSardana.xml using the SardanaConvert.py script.
The entry for the diffractometer looks like:
<device>
<name>e6cctrl</name>
<hkl>mu = 6, omega = 5, chi = 4, phi = 7, gamma = 13, delta = 14, crystal=
<type>diffractometercontroller</type>
<module>E6C</module>
<device>p09/motor/exp.04</device>
<control>tango</control>
<hostname>haspp09mono:10000</hostname>
</device>
¡name¿ is the name the diffractometer device will be created with, it can be choosen arbitrarily.
¡type¿ has to be diffractometercontroller, for indicating that this is the entry corresponding to the
diffractometer.
¡module¿ refers to the diffractometer type, like it is defined in the hkl libraries. The possible
difractometers types are: PETRA3 P09 EH2, E6C, K6C, E4CV, E4CH, K4CV, ZAXIS, SOLEIL
SIXS MED2+2 and SOLEIL SIXS MED1+2.
¡device¿ refers to the name of the Tango Device corresponding to one of the motors used by the
diffractometer (it is assumed that the name of the rest of the motors differs from this one only in
the final number.
¡hkl¿ lists the motors of the diffractometer with the corresponding axis number (final number from
the Tango Device corresponding to each motor). If the name of the motors or any motor is not the
same except the axis number, the whole name of the motor device name in the Pool has to be given
instead of an axis number, ex.:
<hkl>mu = motor/diffracmu_mag/01, th = 5, chi = 4, phi = 7, gamma = 13, del
By request from p08 also the following list of names can be used for the setting the diffractometer
motors:
<hkl>omh = motor/diffracmu_mag/01, om = 5, chi = 4, phic = 7, tth = 13, tt
It is possible (optional) to add a name of a file with the crystal parameters: crystal name, wavelength, lattice and reflections, in this case this file will be loaded after creating the diffractometer
controller. It is also possible (optional) to add the name of the tango device with the energy for
setting the wavelength automatically. The meaning of ¡control¿ and ¡hostname¿ is the same as for
the rest of the online.xml entries.
11.3.3 Measurement Groups
The measurement groups can be created by the SardanaStartup.py script if the corresponding entries
were previously introduced in the online.xml file and converted to onlineSardana.xml using the
SardanaConvert.py script. A measurement group with the first timer and the first counter found in
the pool is always created, independently of what in the online.xml file is defined.
The entry for a measurement group looks like:
<device>
<name>mg_name</name>
<mgs>timers = exp_t01, counters=exp_c01, exp_c02</mgs>
<type>measurement_group</type>
<module>None</module>
<device>None</device>
<control>tango</control>
<hostname>haspp09mono:10000</hostname>
</device>
¡name¿ is the name the measurement group will be created with, it can be choosen arbitrarily.
¡type¿ has to be measurement group.
¡module¿ is not used. ¡device¿ is not used.
¡mgs¿ lists the timers and counters constituting the measurement group. Several timers are allowed.
Counters can be any counter or experimental channel in the Pool. If an option -d or -nd is append
to the name of the counter (ex.: exp ic01 -nd, exp c02 -d, exp c03 -d, exp c04) the counter will be
configured to be displayed or not in the plot, if nothing is set the default setting is used.
The meaning of ¡control¿ and ¡hostname¿ is the same as for the rest of the online.xml entries.
11.3.4 RoIs as counters
The counts of the RoIs defined in the MCA8715 or in the MythenRoIs Tango devices can be used
as counters in Sardana.
The entry in the online.xml file for one of these counters looks like:
<device>
<name>mycountername1</name>
<type>counter</type>
<module>mca8715roi</module>
<device>test/mca/ex.01/counts1</device>
<control>tango</control>
<hostname>haso113u:10000</hostname>
</device>
¡module¿ has to be mca8715roi for the devices of the MCA8715 class and mythenroi for devices
of the MythenRoIs one. ¡device¿ has to contain not only the name of the Tango device but also of
the Tango attribute with the counts.
Here is an example introducing three of these counters from devices of the MCA8715 class, two of
one mca device and one of a different one:
<device>
<name>mycountername1</name>
<type>counter</type>
<module>mca8715roi</module>
<device>test/mca/ex.01/counts1</device>
<control>tango</control>
<hostname>haso113u:10000</hostname>
</device>
<device>
<name>mycountername2</name>
<type>counter</type>
<module>mca8715roi</module>
<device>test/mca/ex.01/counts2</device>
<control>tango</control>
<hostname>haso113u:10000</hostname>
</device>
<device>
<name>mycountername3</name>
<type>counter</type>
<module>mca8715roi</module>
<device>test/mca/ex.02/counts1</device>
<control>tango</control>
<hostname>haso113u:10000</hostname>
</device>
11.4
The configuration at P01
P01 has 3 ExperimentPCs: haspp01eh1, haspp01eh2, haspp01eh3, each with its own Tango DB.
There is an additional Tango DB on haspp01oh1 a,d haspp01oh2.
The Sardana configurations are defined in the files /online dir/online.xml, on each ExpPC. Each
ExpPC has its own Pool and Macroserver which are created by:
p01user@haspp01eh1:˜$ SardanaAIO.py -x
p01user@haspp01eh2:˜$ SardanaAIO.py -x
p01user@haspp01eh3:˜$ SardanaAIO.py -x
Notice that the script SardanaAIO.py executes the following commands (all-in-one):
SardanaShutdow.py -x
SardanaConvert.py -f /online_dir/online.xml -o /online_dir/onlineSardana.xm
SardanaStartup.py -f /online_dir/onlineSardana.xml
The command ’TangoExplorer.py’ gives an overview of the Tango configuration of this beamline
(see section 11.2).
11.5
The configuration at P02
P02 has two ExperimentPCs: haspp02ch1 and haspp02ch2, each with its own Tango DB. There is
an additional Tango DB on haspp02oh1.
The Sardana configurations are defined in the files /online dir/online.xml, on each ExperimentPC.
Each ExperimentPC has its own Pool and Macroserver which are created by:
p02user@haspp02ch1:˜$ SardanaAIO.py -x
p02user@haspp02ch2:˜$ SardanaAIO.py -x
Notice that the script SardanaAIO.py executes the following commands (all-in-one):
SardanaShutdow.py -x
SardanaConvert.py -f /online_dir/online.xml -o /online_dir/onlineSardana.xm
SardanaStartup.py -f /online_dir/onlineSardana.xml
The command ’TangoExplorer.py’ gives an overview of the Tango configuration of this beamline
(see section 11.2). The following aliases have been created to use jive and astor across the Tango
DBs: jive ch1, jive ch2, jive oh1, astor ch1, astor ch2, astor oh1.
This is the configuration of the Pools and MacroServers at P02:
p02user@haspp02ch1:˜/temp$ SardanaStatus.py
Local DoorNames(s): [’p02/door/haspp02ch1.01’, ’p02/door/haspp02ch1.02’, ’
MacroServer
p02/macroserver/haspp02ch1.01 state ON
Related Pools:
--- Pool ( local) p02/pool/haspp02ch1
state ON
mg_haspp02ch1 [u’oh1_t01’, u’eh1a_c01’]
nxsmntgrp [u’eh1a_c23’, u’eh1b_c07’, u’eh1a_c07’, u’eh1a_t01’]
MacroServer
p02/macroserver/haspp02ch1.02 state ON
Related Pools:
--- Pool ( local) p02/pool/haspp02ch1
state ON
mg_haspp02ch1 [u’oh1_t01’, u’eh1a_c01’]
nxsmntgrp [u’eh1a_c23’, u’eh1b_c07’, u’eh1a_c07’, u’eh1a_t01’]
MacroServer
p02/macroserver/haspp02ch1.03 state ON
Related Pools:
--- Pool ( local) p02/pool/haspp02ch1
state ON
mg_haspp02ch1 [u’oh1_t01’, u’eh1a_c01’]
nxsmntgrp [u’eh1a_c23’, u’eh1b_c07’, u’eh1a_c07’, u’eh1a_t01’]
p02user@haspp02ch2:˜$ SardanaStatus.py
Local DoorNames(s): [’p02/door/haspp02ch2.01’,
MacroServer
p02/macroserver/haspp02ch2.01
Related Pools:
--- Pool ( local) p02/pool/haspp02ch2
MG: [u’oh1_t01’, u’eh2a_c01’]
MacroServer
p02/macroserver/haspp02ch2.02
Related Pools:
--- Pool ( local) p02/pool/haspp02ch2
MG: [u’oh1_t01’, u’eh2a_c01’]
MacroServer
p02/macroserver/haspp02ch2.03
Related Pools:
--- Pool ( local) p02/pool/haspp02ch2
MG: [u’oh1_t01’, u’eh2a_c01’]
11.6
The configuration at P03
P03 has one ExperimentPC: haspp03. The command
’p02/door/haspp02ch2.02’, ’
state ON
state ON
state ON
state ON
state ON
state ON
haspp03:˜$ SardanaAIO.py -x
sets-up Pool and MacroServer (all-in-one). It is equivalent to
haspp03:/online_dir> SardanaShutdown.py -x
haspp03:/online_dir> SardanaConvert.py -f /online_dir/online.xml > /online_
haspp03:/online_dir> SardanaStartup.py -f /online_dir/onlineSardana.xml
This is the configuration of the Pool and Macroserver:
p03user ˜/temp #SardanaStatus.py
Local DoorNames(s): [’p03/door/haspp03.01’, ’p03/door/haspp03.02’, ’p03/do
MacroServer
p03/macroserver/haspp03.01
state ON
Related Pools:
--- Pool ( local) p03/pool/haspp03
state ON
MG: [u’exp_t01’, u’exp_c01’]
MacroServer
p03/macroserver/haspp03.02
state ON
Related Pools:
--- Pool ( local) p03/pool/haspp03
state ON
MG: [u’exp_t01’, u’exp_c01’]
MacroServer
p03/macroserver/haspp03.03
state ON
Related Pools:
--- Pool ( local) p03/pool/haspp03
state ON
MG: [u’exp_t01’, u’exp_c01’]
11.7
The configuration at P03NANO
P03NANO has one ExperimentPC: haspp03nano. The command
p03nano@haspp03nano:˜$ SardanaAIO.py -x
sets-up Pool and MacroServer (all-in-one). It is equivalent to
p03nano@haspp03nano:˜$ SardanaShutdown.py -x
p03nano@haspp03nano:˜$ SardanaConvert.py -f /online_dir/online.xml > /onlin
p03nano@haspp03nano:˜$ SardanaStartup.py -f /online_dir/onlineSardana.xml
This is the configuration of the Pool and Macroserver:
p03nano@haspp03nano:˜$ SardanaStatus.py
Local DoorNames(s): [’p03nano/door/haspp03nano.01’, ’p03nano/door/haspp03n
MacroServer
p03nano/macroserver/haspp03nan state ON
Related Pools:
--- Pool ( local) p03nano/pool/haspp03nano
state ON
MG: [u’t02’, u’c01’]
MacroServer
p03nano/macroserver/haspp03nan state ON
Related Pools:
--- Pool ( local) p03nano/pool/haspp03nano
state ON
MG: [u’t02’, u’c01’]
MacroServer
p03nano/macroserver/haspp03nan state ON
Related Pools:
--- Pool ( local) p03nano/pool/haspp03nano
state ON
MG: [u’t02’, u’c01’]
11.8
The configuration at P04
P04 has 2 TANGO HOSTS: haspp04exp1 and haspp04exp2.
11.8.1 The configuration at P04EXP1
P04EXP1 has one SardanaHost: haspp04exp1. The command
p04user@haspp04exp1:/online_dir$ SardanaConvert.py -f online.xml
Pools
pool_haspp04exp1
These pools are created, if no container pool is specified
-p
tells us that only one StarterHost is involved. So we can create the xml file for Sardana with:
p04user@haspp04exp1:/online_dir$ SardanaConvert.py -f online.xml > onlineSa
If Sardana is running, you have to stop it first:
p04user@haspp04exp1:/online_dir$ SardanaShutdown.py -x
Sardana is configured and started by:
p04user@haspp04exp1:/online_dir$ SardanaStartup.py -f /online_dir/onlineSar
Here is the output of SardanaStatus.py:
p04user@haspp04exp1:/online_dir$ SardanaStatus.py
Local DoorNames(s): [’p04/door/haspp04exp1.01’, ’p04/door/haspp04exp1.02’,
MacroServer
p04/macroserver/haspp04exp1.01 state ON
Related Pools:
--- Pool ( local) p04/pool/haspp04exp1
state ON
MG: [u’exp1_t01’, u’exp1_vfc01’]
MacroServer
p04/macroserver/haspp04exp1.02 state ON
Related Pools:
--- Pool ( local) p04/pool/haspp04exp1
state ON
MG: [u’exp1_t01’, u’exp1_vfc01’]
MacroServer
p04/macroserver/haspp04exp1.03 state ON
Related Pools:
--- Pool ( local) p04/pool/haspp04exp1
state ON
MG: [u’exp1_t01’, u’exp1_vfc01’]
11.8.2 The configuration at P04EXP2
P04EXP2 has one SardanaHost: haspp04exp2. The command
p04user@haspp04exp2:/online_dir$ SardanaConvert.py -f online.xml
Pools
pool_haspp04exp2
These pools are created, if no container pool is specified
-p
tells us that only one StarterHost is involved. So we can create the xml file for Sardana with:
p04user@haspp04exp2:/online_dir$ SardanaConvert.py -f online.xml > onlineSa
If Sardana is running, you have to stop it first:
p04user@haspp04exp2:/online_dir$ SardanaShutdown.py -x
Sardana is configured and started by:
p04user@haspp04exp2:/online_dir$ SardanaStartup.py -f /online_dir/onlineSar
Here is the output of SardanaStatus.py:
p04user@haspp04exp2:/online_dir$ SardanaStatus.py
Local DoorNames(s): [’p04/door/haspp04exp2.01’, ’p04/door/haspp04exp2.02’,
MacroServer
p04/macroserver/haspp04exp2.01 state ON
Related Pools:
--- Pool ( local) p04/pool/haspp04exp2
state ON
MG: [u’exp2_t01’, u’exp2_vfc01’]
MacroServer
p04/macroserver/haspp04exp2.02 state ON
Related Pools:
--- Pool ( local) p04/pool/haspp04exp2
state ON
MG: [u’exp2_t01’, u’exp2_vfc01’]
MacroServer
p04/macroserver/haspp04exp2.03 state ON
Related Pools:
--- Pool ( local) p04/pool/haspp04exp2
state ON
MG: [u’exp2_t01’, u’exp2_vfc01’]
11.9
The configuration at P06
P06 has 4 ExperimentPCs: haspp06ctrl (Micro), haspp06mc01 (Micro-Cryo), haspp06nc1 (Nano),
hasp029rack (laboratory), each with its own Tango DB. There are additional Tango DBs on haspp06,
haspp06mono.
The Sardana configurations are defined in the files /online dir/online.xml, on each ExpPC. Each
ExpPC has its own Pool and Macroserver which are created by:
[p06user@haspp06ctrl:˜]$ SardanaAIO.py -x
[p06user@haspp06mc01:˜]$ SardanaAIO.py -x
[p06user@haspp06nc1:˜]$ SardanaAIO.py -x
[p06user@hasp029rack:˜]$ SardanaAIO.py -x
Notice that the script SardanaAIO.py executes the following commands (all-in-one):
SardanaShutdow.py -x
SardanaConvert.py -f /online_dir/online.xml -o /online_dir/onlineSardana.xm
SardanaStartup.py -f /online_dir/onlineSardana.xml
haspp06ctrl> SardanaStartup.py -f /online_dir/onlineSardana.xml
The command ’TangoExplorer.py’ gives an overview of the Tango configuration of this beamline
(see section 11.2).
For using the macros concerning the Maia detector (maia scan, maia regions scan, ...) several
environment variables have to be defined:
• MaiaAxis0Device
• MaiaAxis1Device
• MaiaScanDevice
• MaiaProcessingDevice
• MaiaLoggerDevice
• MaiaSampleDevice
• MaiaFlux0Device
• MaiaFlux1Device
• EnergyDevice
• Flux0Device
• Flux1Device
• Pixel1JogMode
• ShutterDevice
Use the macro lsenv to check if they are defined. In case they are not, you can set them running the
macro maia senv:
p06/door/haspp06ctrl [1]: maia_senv
Setting MaiaAxis0Device to p06/maiaaxis/exp.00
Setting MaiaAxis1Device to p06/maiaaxis/exp.01
Setting MaiaScanDevice to p06/maiascan/exp.01
Setting MaiaProcessingDevice to p06/maiaprocessing/exp.01
Setting MaiaLoggerDevice to p06/maialogger/exp.01
Setting MaiaSampleDevice to p06/maiasample/exp.01
Setting MaiaFlux0Device to p06/maiaflux/exp.00
Setting MaiaFlux1Device to p06/maiaflux/exp.01
Setting EnergyDevice to p06/dcmener/mono.01
Setting Flux0Device to p06/keithley428/mc01.10
Setting Flux1Device to p06/keithley428/mc01.11
Setting Pixel1JogMode to 0
Setting ShutterDevice to p06/timer/exp.01
or define them one by one:
p06/door/haspp06ctrl
p06/door/haspp06ctrl
p06/door/haspp06ctrl
p06/door/haspp06ctrl
p06/door/haspp06ctrl
p06/door/haspp06ctrl
p06/door/haspp06ctrl
p06/door/haspp06ctrl
p06/door/haspp06ctrl
p06/door/haspp06ctrl
p06/door/haspp06ctrl
p06/door/haspp06ctrl
p06/door/haspp06ctrl
[1]: senv
[2]: senv
[3]: senv
[4]: senv
[5]: senv
[6]: senv
[7]: senv
[8]: senv
[9]: senv
[10]: senv
[11]: senv
[12]: senv
[13]: senv
MaiaAxis0Device
p06/maiaaxis/exp.00
MaiaAxis1Device
p06/maiaaxis/exp.01
MaiaScanDevice
p06/maiascan/exp.01
MaiaProcessingDevice
p06/maiaprocessing
MaiaLoggerDevice
p06/maialogger/exp.01
MaiaSampleDevice
p06/maiasample/exp.01
MaiaFlux0Device
p06/maiaflux/exp.00
MaiaFlux1Device
p06/maiaflux/exp.01
EnergyDevice
p06/dcmener/mono.01
Flux0Device
p06/keithley428/mc01.10
Flux1Device
p06/keithley428/mc01.11
Pixel1JogMode
0
ShutterDevice
p06/timer/exp.01
The file MaiaDefaultEnv.txt in the directory
haspp06ctrl:/home/p06user/sardanaMacros
contains these lines for setting the environment correctly. Simply copy and paste them in your
spock sesion.
11.10
The configuration at P07EH2A
This section describes the Sardana features on hasgksspp07eh2a. The command
[p07user@hasgksspp07eh2a:online_dir$ SardanaConvert.py -f online.xml -p
Pools
pool_hasgksspp07eh2a
tells us that only one StarterHost of the local Tango DB is involved. So we can create the xml file
for Sardana with:
[p07user@hasgksspp07eh2a:online_dir$ SardanaConvert.py -f online8Feb2013.xm
P07EH2A imports devices from other Tango DBs. The conversion script knows how to deal with
them.
If Sardana is running, you have to stop it first:
[p07user@hasgksspp07eh2a$ SardanaShutdown.py -x
Sardana is configured and started by:
[p07user@hasgksspp07eh2a$ SardanaStartup.py -f /online_dir/onlineSardana.xm
Figure 11.10 displays the configuration of the Pools and the MacroServer.
Figure 11.10 shows the properties of the MacroServer. Notice that the property PoolNames is a list
of connected Pools. There is one Pool per StarterHost.
This is how the configuration is seen through the Door an haspp03:
[p07user@hasgksspp07eh2a:online_dir$ SardanaStatus.py
Local DoorNames(s): [’p07/door/hasgksspp07eh2a’]
MacroServer
p07/macroserver/hasgksspp07eh2 state ON
Figure 11.7: Astor: Sardana on hasgksspp07eh2a
Related Pools:
--- Pool ( local) p07/pool/hasgksspp07eh2a
MG: [u’eh2a_t01’, u’eh2a_c01’]
--- Pool ( local) p07/pool/hasgksspp07eh3
<empty>
--- Pool ( local) p07/pool/hasgksspp07oh1
<empty>
--- Pool ( local) p07/pool/haspp07eh2
<empty>
--- Pool ( local) p07/pool/haspp07oh2
<empty>
state ON
state ON
state ON
state ON
state ON
The active MeasurementGroup is mg hasgksspp07eh2a. We use Spock to verify this.
p07/door/hasgksspp07eh2a [2]: lsenv
Name
Value
Type
-------------- --------------------- -----JsonRecorder
True
bool
ActiveMntGrp
mg_hasgksspp07eh2a
str
ScanFile
tst.fio
str
_ViewOptions
{’ShowDial’: False}
dict
ScanDir
/home/p07user/temp
str
p07/door/hasgksspp07eh2a [3]: lsmeas
Active
Name
Timer Experim. channels
-------- -------------------- ---------- -------------------mg_hasgksspp07eh2a
eh2a_t01 eh2a_t01, eh2a_c01
*
Figure 11.8: Jive: The MacroServer PoolNames Property on hasgksspp07eh2a
Finally, let’s look at the controllers:
p07/door/hasgksspp07eh2a [1]: lsctrl
Name
Type
Class
Module
------------------ ----------------- --------------- --------------am_gap_oh1
Motor
HasyMotorCtrl
HasyMotorCtrl
dcm_motor_oh1_21
Motor
HasyMotorCtrl
HasyMotorCtrl
dcm_motor_oh1_22
Motor
HasyMotorCtrl
HasyMotorCtrl
dcm_motor_oh1_23
Motor
HasyMotorCtrl
HasyMotorCtrl
dcm_motor_oh1_24
Motor
HasyMotorCtrl
HasyMotorCtrl
dcm_motor_oh1_25
Motor
HasyMotorCtrl
HasyMotorCtrl
dcm_motor_oh1_26
Motor
HasyMotorCtrl
HasyMotorCtrl
dcm_motor_oh1_27
Motor
HasyMotorCtrl
HasyMotorCtrl
dcm_motor_oh1_28
Motor
HasyMotorCtrl
HasyMotorCtrl
dgg2_eh2a_01
CTExpChannel
DGG2Ctrl
DGG2Ctrl
dgg2_eh2a_02
CTExpChannel
DGG2Ctrl
DGG2Ctrl
mca8701_eh2a
OneDExpChannel
HasyOneDCtrl
HasyOneDCtrl
mult_mono_01
Motor
HasyMotorCtrl
HasyMotorCtrl
omsvme58_eh2
Motor
HasyMotorCtrl
HasyMotorCtrl
omsvme58_eh2a
Motor
HasyMotorCtrl
HasyMotorCtrl
omsvme58_oh2
Motor
HasyMotorCtrl
HasyMotorCtrl
sis3610in_eh2a
IORegister
SIS3610Ctrl
SIS3610Ctrl
sis3610out_eh2a
IORegister
SIS3610Ctrl
SIS3610Ctrl
sis3820_eh2a
CTExpChannel
SIS3820Ctrl
SIS3820Ctrl
spk_eh3
tip551_eh2a
tip830_eh2a
tm_oh2_05
tm_oh2_06
tm_oh2_07
tm_oh2_08
tm_undulator_oh1
tth_eh2a
vfcadc_eh2a
Motor
Motor
ZeroDExpChannel
Motor
Motor
Motor
Motor
Motor
Motor
ZeroDExpChannel
HasyMotorCtrl
HasyDACCtrl
HasyADCCtrl
HasyMotorCtrl
HasyMotorCtrl
HasyMotorCtrl
HasyMotorCtrl
HasyMotorCtrl
HasyMotorCtrl
HasyADCCtrl
HasyMotorCtrl
HasyDACCtrl
HasyADCCtrl
HasyMotorCtrl
HasyMotorCtrl
HasyMotorCtrl
HasyMotorCtrl
HasyMotorCtrl
HasyMotorCtrl
HasyADCCtrl
The controller names contain these strings: oh1, eh2a, eh3, oh2 and eh2. They tell us which
StarterHost is referenced by a controller.
To find the mapping of devices and controllers, say:
p07/door/hasgksspp07eh2a [9]: lsa
Name
Type
Controller
Axis
---------------------- ----------------- ------------------ -----dcm_1st_pitch
Motor
dcm_motor_oh1_21
1
dcm_1st_roll
Motor
dcm_motor_oh1_25
1
dcm_1st_yaw
Motor
dcm_motor_oh1_27
1
dcm_2nd_lateral
Motor
dcm_motor_oh1_24
1
dcm_2nd_longitudinal
Motor
dcm_motor_oh1_23
1
dcm_2nd_pitch
Motor
dcm_motor_oh1_22
1
dcm_2nd_roll
Motor
dcm_motor_oh1_26
1
dcm_2nd_yaw
Motor
dcm_motor_oh1_28
1
eh2a_adc01
ZeroDExpChannel
tip830_eh2a
1
eh2a_adc02
ZeroDExpChannel
tip830_eh2a
2
eh2a_adc03
ZeroDExpChannel
tip830_eh2a
3
eh2a_adc04
ZeroDExpChannel
tip830_eh2a
4
eh2a_adc05
ZeroDExpChannel
tip830_eh2a
5
eh2a_adc06
ZeroDExpChannel
tip830_eh2a
6
eh2a_adc07
ZeroDExpChannel
tip830_eh2a
7
eh2a_adc08
ZeroDExpChannel
tip830_eh2a
8
eh2a_c01
CTExpChannel
sis3820_eh2a
1
eh2a_c02
CTExpChannel
sis3820_eh2a
2
eh2a_c03
CTExpChannel
sis3820_eh2a
3
eh2a_c04
CTExpChannel
sis3820_eh2a
4
eh2a_c05
CTExpChannel
sis3820_eh2a
5
eh2a_c06
CTExpChannel
sis3820_eh2a
6
eh2a_c07
CTExpChannel
sis3820_eh2a
7
eh2a_c08
CTExpChannel
sis3820_eh2a
8
eh2a_c09
CTExpChannel
sis3820_eh2a
9
eh2a_c10
CTExpChannel
sis3820_eh2a
10
eh2a_c11
CTExpChannel
sis3820_eh2a
11
eh2a_c12
CTExpChannel
sis3820_eh2a
12
...
11.11
The configuration at P08
P08 has one ExperimentPC: haspp08. The Tango DB runs on haspp08mono. The Pool and the
MacroServer are created by:
p08user@haspp08:˜$ SardanaAIO.py -x
SardanaAIO.py -x executes the following commands (all-in-one):
haspp08:/online_dir> SardanaShutdown.py -x
haspp08:/online_dir> SardanaConvert.py -f /online_dir/online.xml > /online_
haspp08:/online_dir> SardanaStartup.py -f /online_dir/onlineSardana.xml
The script SardanaAIO.py -x can be executed after /online dir/online.xml has been changed.
This is the configuration of the Pool and the MacroServer at P08:
p08user@haspp08:˜$ SardanaStatus.py
Local DoorNames(s): [’p08/door/haspp08.01’, ’p08/door/haspp08.02’, ’p08/do
MacroServer
p08/macroserver/haspp08.01
state ON
Related Pools:
--- Pool ( local) p08/pool/haspp08
state ON
MG: [u’exp_t01’, u’exp_c01’]
MacroServer
p08/macroserver/haspp08.02
state ON
Related Pools:
--- Pool ( local) p08/pool/haspp08
state ON
MG: [u’exp_t01’, u’exp_c01’]
MacroServer
p08/macroserver/haspp08.03
state ON
Related Pools:
--- Pool ( local) p08/pool/haspp08
state ON
MG: [u’exp_t01’, u’exp_c01’]
11.12
The configuration at P09
P09 has four ExperimentPCs: haspp09, haspp09dif (for simulation), haspp09mag, haspp09haxps,
and four Tango DBs: haspp09mono (includes haspp09), haspp09dif, haspp09mag and haspp09haxps.
The Tango namespaces of the ExperimentPCs are decoupled.
Each of the ExpPCs has its own Pool and its own Macroserver. The contents of a Pool is specified by online.xml. This file may contain references across the Tango DBs. The Pools and the
MacroServers are created and started by:
haspp09:˜$ SardanaAIO.py -x
haspp09dif:˜$ SardanaAIO.py -x
haspp09mag:˜$ SardanaAIO.py -x
haspp09haxps:˜$ SardanaAIO.py -x
SardanaAIO.py -x executes the following commands (all-in-one):
haspp09:/online_dir> SardanaShutdown.py -x
haspp09:/online_dir> SardanaConvert.py -f /online_dir/online.xml > /online_
haspp09:/online_dir> SardanaStartup.py -f /online_dir/onlineSardana.xml
The script SardanaAIO.py -x can be executed after /online dir/online.xml has been changed.
P09 operates a diffractometer. The diffractometer pool devices are created from the online.xml file
(see documentation in section above.
These environment variables have to be defined in spock:
p09/door/haspp09.01
p09/door/haspp09.01
p09/door/haspp09.01
p09/door/haspp09.01
[8]: senv DiffracDevice controller/diffrace6c/e6cctrl
[9]: senv Psi pm/e6cctrl/4
[10]: setvo ShowDial False
[11]: setvo ShowCtrlAxis True
The command ’TangoExplorer.py’ gives an overview of the Tango configuration of this beamline
(see section 11.2). The following aliases have been created to use jive and astor across the Tango
DBs: jive exp, jive haxps, jive mag, jive mono, astor exp, astor haxps, astor mag, astor mono.
This is the configuration of the Pool and Macroserver on haspp09:
haspp09:˜> SardanaStatus.py
Local DoorNames(s): [’p09/door/haspp09.01’, ’p09/door/haspp09.02’, ’p09/do
MacroServer
p09/macroserver/haspp09.01
state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09
state ON
MG: [u’mono_t01’, u’exp_c01’]
MacroServer
p09/macroserver/haspp09.02
state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09
state ON
MG: [u’mono_t01’, u’exp_c01’]
MacroServer
p09/macroserver/haspp09.03
state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09
state ON
MG: [u’mono_t01’, u’exp_c01’]
This is the configuration of the Pools and the MacroServers:
haspp09:˜> SardanaStatus.py
Local DoorNames(s): [’p09/door/haspp09.01’, ’p09/door/haspp09.02’, ’p09/do
MacroServer
p09/macroserver/haspp09.01
state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09
state ON
MG: [u’mono_t01’, u’exp_c01’]
MacroServer
p09/macroserver/haspp09.02
state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09
state ON
MG: [u’mono_t01’, u’exp_c01’]
MacroServer
p09/macroserver/haspp09.03
state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09
state ON
MG: [u’mono_t01’, u’exp_c01’]
p09user@haspp09mag:˜$ SardanaStatus.py
Local DoorNames(s): [’p09/door/haspp09mag.01’, ’p09/door/haspp09mag.02’, ’
MacroServer
p09/macroserver/haspp09mag.01 state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09mag
state ON
mg_haspp09mag [u’exp_t01’, u’mag_c01’]
nxsmntgrp [u’exp_t01’]
MacroServer
p09/macroserver/haspp09mag.02 state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09mag
state ON
mg_haspp09mag [u’exp_t01’, u’mag_c01’]
nxsmntgrp [u’exp_t01’]
MacroServer
p09/macroserver/haspp09mag.03 state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09mag
state ON
mg_haspp09mag [u’exp_t01’, u’mag_c01’]
nxsmntgrp [u’exp_t01’]
p09haxps@haspp09haxps:˜/temp$ SardanaStatus.py
Local DoorNames(s): [’p09/door/haspp09haxps.01’, ’p09/door/haspp09haxps.02
MacroServer
p09/macroserver/haspp09haxps.0 state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09haxps
state ON
MG: [u’mag_t01’, u’haxps_c01’]
MacroServer
p09/macroserver/haspp09haxps.0 state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09haxps
state ON
MG: [u’mag_t01’, u’haxps_c01’]
MacroServer
p09/macroserver/haspp09haxps.0 state ON
Related Pools:
--- Pool ( local) p09/pool/haspp09haxps
state ON
MG: [u’mag_t01’, u’haxps_c01’]
11.13
The configuration at P10
P10 has 3 ExperimentPCs: haspp10e1, haspp10e2, haspp10lcx, each with its own Tango DB. There
is an additional Tango DB on haspp10opt.
The Sardana configurations are defined in the files /online dir/online.xml, on each ExpPC. Each
ExpPC has its own Pool and Macroserver which are created by:
p10user@haspp10e1:˜$ SardanaAIO.py -x
p10user@haspp10e2:˜$ SardanaAIO.py -x
p10user@haspp10lcx:˜$ SardanaAIO.py -x
Notice that the script SardanaAIO.py executes the following commands (all-in-one):
SardanaShutdow.py -x
SardanaConvert.py -f /online_dir/online.xml -o /online_dir/onlineSardana.xm
SardanaStartup.py -f /online_dir/onlineSardana.xml
The command ’TangoExplorer.py’ gives an overview of the Tango configuration of this beamline
(see section 11.2). The following aliases have been created to use jive and astor across the Tango
DBs: jive e1, jive e2, jive lcx, jive opt, astor e1, astor e2, astor lcx, astor oh1.
This is the configuration of the Pools and the MacroServers at P10:
p10user@haspp10e1:˜$ SardanaStatus.py
Local DoorNames(s): [’p10/door/haspp10e1.01’, ’p10/door/haspp10e1.02’, ’p1
MacroServer
p10/macroserver/haspp10e1.01
state ON
Related Pools:
--- Pool ( local) p10/pool/haspp10e1
state ON
MG: [u’e2_t01’, u’cbs1’]
MacroServer
p10/macroserver/haspp10e1.02
state ON
Related Pools:
--- Pool ( local) p10/pool/haspp10e1
state ON
MG: [u’e2_t01’, u’cbs1’]
MacroServer
p10/macroserver/haspp10e1.03
state ON
Related Pools:
--- Pool ( local) p10/pool/haspp10e1
state ON
MG: [u’e2_t01’, u’cbs1’]
p10user@haspp10e2:˜$ SardanaStatus.py
Local DoorNames(s): [’p10/door/haspp10e2.01’, ’p10/door/haspp10e2.02’, ’p1
MacroServer
p10/macroserver/haspp10e2.01
state ON
Related Pools:
--- Pool ( local) p10/pool/haspp10e2
state ON
MG: [u’e2_t01’, u’apd’]
MacroServer
p10/macroserver/haspp10e2.02
state ON
Related Pools:
--- Pool ( local) p10/pool/haspp10e2
state ON
MG: [u’e2_t01’, u’apd’]
MacroServer
p10/macroserver/haspp10e2.03
state ON
Related Pools:
--- Pool ( local) p10/pool/haspp10e2
state ON
MG: [u’e2_t01’, u’apd’]
11.14
The Configuration at P11
P11 has one SardanaHost: haspp11user02. Since this beamline has one SardanaHost only so far,
there is no need to have different pools. Still it might be interesting to inspect the StarterHosts:
[p11user@haspp11user02:online_dir$ SardanaConvert.py -f online.xml -p
Pools
pool_haspp11user02
pool_haspp11x
pool_haspp11c
pool_haspp11b
pool_haspp11pp
pool_hasnanodiff
pool_haspp11cam1
pool_haspp11oh
pool_HASPP11VORTEX
We create a container pool that contains all devices:
[p11user@haspp11user02:online_dir$ SardanaConvert.py -f online.xml -c pool_
The option -c specifies the container Pool.
The newly created xml file is used to configure and start Sardana.
If Sardana is running, we have to stop it first:
[p11user@haspp11user02:online_dir$ SardanaShutdown.py -x
Then we start it.
[p11user@haspp11user02:online_dir$ SardanaStartup.py -f onlineSardana.xml
Let’s look at the Sardana status:
[p11user@haspp11user02:online_dir$ SardanaStatus.py
Local DoorNames(s): [’p11/door/haspp11user02’]
MacroServer
p11/macroserver/haspp11user02 state ON
Related Pools:
--- Pool ( local) p11/pool/haspp11user02
state ON
MG: [u’ehb_t01’, u’ehc_c01’]
Use ’SardanaStatus.py -f’ to obtain more information about the pools
More information:
[p11user@haspp11user02:online_dir$ SardanaStatus.py -f
Local DoorNames(s): [’p11/door/haspp11user02’]
--- Pool ( local) p11/pool/haspp11user02
state ON
ControllerList
dgg2_ehb_01 ON, 1 devices: ehb_t01
dgg2_ehb_02 ON, 1 devices: ehb_t02
dgg2_ehc_01 ON, 1 devices: ehc_t01
dgg2_ehc_02 ON, 1 devices: ehc_t02
dgg2_pp_01 ON, 1 devices: pp_t01
dgg2_pp_02 ON, 1 devices: pp_t02
mca8701_oh ON, 1 devices: oh_xia01
omsvme58_eh_pp2 ON, 16 devices: eh_pp2_mot01, eh_pp2_mot02 ...
omsvme58_eh_pp3 ON, 16 devices: eh_pp3_mot01, eh_pp3_mot02 ...
omsvme58_granite ON, 8 devices: granite_mot01, granite_mot02 ..
omsvme58_lab ON, 4 devices: lab_mot01, lab_mot02, lab_mot05
omsvme58_oh ON, 32 devices: oh_mot01, oh_mot02 ... oh_mot32
petra_exp_petra ON, 1 devices: exp_petra
sis3610in_eh ON, 16 devices: eh_ireg01, eh_ireg02 ... eh_ire
sis3610in_nano ON, 16 devices: nano_ireg01, nano_ireg02 ... na
sis3610in_pp ON, 16 devices: pp_ireg01, pp_ireg02 ... pp_ire
sis3610out_eh ON, 16 devices: eh_oreg01, eh_oreg02 ... eh_ore
sis3610out_nano ON, 16 devices: nano_oreg01, nano_oreg02 ... na
sis3610out_pp ON, 16 devices: pp_oreg01, pp_oreg02 ... pp_ore
sis3820_ehc ON, 32 devices: ehc_c01, ehc_c02 ... ehc_c32
sis3820_nano ON, 32 devices: nano_c01, nano_c02 ... nano_c32
sis3820_pp ON, 32 devices: pp_c01, pp_c02 ... pp_c32
tip551_eh ON, 4 devices:
tip551_ehc ON, 4 devices:
tip551_pp ON, 4 devices:
tip830_eh ON, 8 devices:
tip830_ehc ON, 8 devices:
tip830_if ON, 1 devices:
tip830_pp ON, 8 devices:
tm_exp ON, 4 devices:
vfcadc_ehb ON, 8 devices:
vfcadc_ehc ON, 8 devices:
vfcadc_pp ON, 8 devices:
MG: [u’ehb_t01’, u’ehc_c01’]
MacroServer p11/macroserver/haspp11user02
Door
p11/door/haspp11user02
Pools [’pool_haspp11user02’]
11.15
eh_dac01, eh_dac02, eh_dac03, e
ehc_dac01, ehc_dac02, ehc_dac03
pp_dac01, pp_dac02, pp_dac03, p
eh_adc01, eh_adc02 ... eh_adc08
ehc_adc01, ehc_adc02 ... ehc_ad
if_adc01
pp_adc01, pp_adc02 ... pp_adc08
exp_dmy01, exp_dmy02, exp_dmy03
ehb_vfc01, ehb_vfc02 ... ehb_vf
ehc_vfc01, ehc_vfc02 ... ehc_vf
pp_vfc01, pp_vfc02 ... pp_vfc08
state ON
state ON
The Configuration at P64
P64 has one ExperimentPC: hasnp64. The command
p64user@hasnp64:˜$ SardanaAIO.py -x
sets-up Pool and MacroServer (all-in-one). It is equivalent to
p64user@hasnp64:˜$ SardanaShutdown.py -x
p64user@hasnp64:˜$ SardanaConvert.py -f /online_dir/online.xml > /online_di
p64user@hasnp64:˜$ SardanaStartup.py -f /online_dir/onlineSardana.xml
This is the configuration of the Pool and Macroserver:
p64user@hasnp64:˜$ SardanaStatus.py
Local DoorNames(s): [’p64/door/hasnp64.01’, ’p64/door/hasnp64.02’, ’p64/do
MacroServer
p64/macroserver/hasnp64.01
state ON
Related Pools:
--- Pool ( local) p64/pool/hasnp64
state ON
nxsmntgrp [u’exp_t01’, u’exp_c01’, u’exp_c02’, u’exp_c03’]
mg_hasnp64 [u’exp_t01’, u’exp_c01’]
MacroServer
p64/macroserver/hasnp64.02
state ON
Related Pools:
--- Pool ( local) p64/pool/hasnp64
state ON
nxsmntgrp [u’exp_t01’, u’exp_c01’, u’exp_c02’, u’exp_c03’]
mg_hasnp64 [u’exp_t01’, u’exp_c01’]
MacroServer
p64/macroserver/hasnp64.03
state ON
Related Pools:
--- Pool ( local) p64/pool/hasnp64
state ON
nxsmntgrp [u’exp_t01’, u’exp_c01’, u’exp_c02’, u’exp_c03’]
mg_hasnp64 [u’exp_t01’, u’exp_c01’]
11.16
The Configuration at hascmexp
CMEXP has one ExperimentPC: hascmexp. The command
rheouser@hascmexp:˜$ SardanaAIO.py -x
sets-up Pool and MacroServer (all-in-one). It is equivalent to
rheouser@hascmexp:˜$ SardanaShutdown.py -x
rheouser@hascmexp:˜$ SardanaConvert.py -f /online_dir/online.xml > /online_
rheouser@hascmexp:˜$ SardanaStartup.py -f /online_dir/onlineSardana.xml
11.17
Solution to possible problems
• If trying to start the MacroServer with any of the scripts (SardanaStartup.py, SardanaStartMacroServer.py, SardanaRestartMacroServer.py) you get the error:
Error in starting server MacroServer/computer_name
the reason could be that the MacroServer process was not correctly killed when it was stoped
and it hangs in the computer. Use:
ps -ef | grep MacroServer
to check if the process exists and in that case kill it by hand. After killing the process the
scripts should be able to start the MacroServer again.
Chapter 12
Technical Details
This module contains some utility code.
12.1
The MacroServer Environment
The MacroServer environment is stored below the directory /tmp/tango/MacroServer, e.g.:
import shelve
e = shelve.open(’/tmp/tango/MacroServer/<NodeName>/macroserver.properties’)
print e
The stored environment is loaded during the restart of the MacroServer.
12.2
The HasyUtils module
The module HasyUtils contains useful functions:
HasyUtils.fastscananalysis
HasyUtils.fioPlotter
HasyUtils.fioReader
HasyUtils.fioWriter
HasyUtils.getAlias
HasyUtils.getClassList
HasyUtils.getDeviceNameByAlias
HasyUtils.getDeviceNamesByClass
HasyUtils.getDoorNames
HasyUtils.getEnv
HasyUtils.getHostList
HasyUtils.getHostname
HasyUtils.getHostnameLong
HasyUtils.getLocalDoorNames
HasyUtils.getLocalMacroServerNames
HasyUtils.getLocalMacroServerServers
HasyUtils.getLocalMgAliases
HasyUtils.getLocalMgNames
HasyUtils.getLocalNXSelectorNames
HasyUtils.getLocalPoolNames
HasyUtils.getLocalPoolServers
186
HasyUtils.getMacroServerNames
HasyUtils.getMgAliases
HasyUtils.getOnlineXML
HasyUtils.getPoolNames
HasyUtils.inkey
HasyUtils.petra
HasyUtils.setEnvCautious
HasyUtils.ssa
To retrieve some information about a function use:
In [15]: import HasyUtils
In [16]: HasyUtils.fioReader?
Type:
classobj
String Form:HasyUtils.fioReader.fioReader
File:
/usr/lib/pymodules/python2.7/HasyUtils/fioReader.py
Docstring:
represents an entire file with several columns
input:
name of the .fio file
returns: object containing:
self.comments
self.user_comments
self.parameters
self.columns
self.fileName
Constructor information:
Definition:HasyUtils.fioReader(self, fileName)
12.2.1
Door-MacroServer-Pool Class
DSMP is a helper class.
In [1]: import HasyUtils
In [2]: d = HasyUtils.DMSP( ’p09/door/haso107d1.01’)
In [3]: d.
d.door
֒→ pools
d.getEnv
d.setEnv
d.macroserver d.poolNames
d.unsetEnv
In [3]: d.getEnv(’ScanDir’)
Out[3]: ’/home/kracht/temp’
12.3
Devices and Controllers
We can use Spock to produce a controller list:
p09/door/haspp09 [3]: lsctrl
Name
֒→ Module
Type
Class
d.
------------------- ----------------- --------------֒→ --------------am_gap
Motor
HasyMotorCtrl
֒→ HasyMotorCtrl
analyzer_eh1
Motor
HasyMotorCtrl
֒→ HasyMotorCtrl
atto300_exp
Motor
HasyMotorCtrl
֒→ HasyMotorCtrl
dcm_energy
Motor
HasyMotorCtrl
֒→ HasyMotorCtrl
dcm_motor_mono_01
Motor
HasyMotorCtrl
֒→ HasyMotorCtrl
dgg2_exp_01
CTExpChannel
DGG2Ctrl
֒→ DGG2Ctrl
dgg2_exp_02
CTExpChannel
DGG2Ctrl
֒→ DGG2Ctrl
dgg2_mono_01
CTExpChannel
DGG2Ctrl
֒→ DGG2Ctrl
dgg2_mono_02
CTExpChannel
DGG2Ctrl
֒→ DGG2Ctrl
mca8701_exp
OneDExpChannel
HasyOneDCtrl
֒→ HasyOneDCtrl
mult_mono_01
Motor
HasyMotorCtrl
֒→ HasyMotorCtrl
omsvme58_exp
Motor
HasyMotorCtrl
֒→ HasyMotorCtrl
omsvme58_mono
Motor
HasyMotorCtrl
֒→ HasyMotorCtrl
phaseretarder_exp
Motor
HasyMotorCtrl
֒→ HasyMotorCtrl
sis3302_exp
OneDExpChannel
HasyOneDCtrl
֒→ HasyOneDCtrl
sis3610in_exp
IORegister
SIS3610Ctrl
֒→ SIS3610Ctrl
sis3610out_exp
IORegister
SIS3610Ctrl
֒→ SIS3610Ctrl
sis3820_exp
CTExpChannel
SIS3820Ctrl
֒→ SIS3820Ctrl
sis3820_mono
CTExpChannel
SIS3820Ctrl
֒→ SIS3820Ctrl
slt_exp
Motor
HasyMotorCtrl
֒→ HasyMotorCtrl
spk_exp
Motor
HasyMotorCtrl
֒→ HasyMotorCtrl
tip551_exp
Motor
HasyDACCtrl
֒→ HasyDACCtrl
tip830_exp
ZeroDExpChannel
HasyADCCtrl
֒→ HasyADCCtrl
tm_absbox
Motor
HasyMotorCtrl
֒→ HasyMotorCtrl
tm_undulator
Motor
֒→ HasyMotorCtrl
vfcadc_exp
ZeroDExpChannel
֒→ HasyADCCtrl
vfcadc_mono
ZeroDExpChannel
֒→ HasyADCCtrl
HasyMotorCtrl
HasyADCCtrl
HasyADCCtrl
The first line tells us e.g. that there is a controller named am gap. It is of the type Motor and it is
implemented by HasyMotorCtrl. Spocks shows us also which motor belongs to which controller:
p09/door/haspp09 [4]: %lsm
Name
Type
Controller
Axis
---------------------- ------- ------------------- -----abs
Motor
tm_absbox
1
analyzer
Motor
analyzer_eh1
1
atto1
Motor
atto300_exp
1
atto2
Motor
atto300_exp
2
dcm_bragg
Motor
dcm_motor_mono_01
1
energyfmb
Motor
dcm_energy
1
exp_dac01
Motor
tip551_exp
1
exp_dac02
Motor
tip551_exp
2
exp_dac03
Motor
tip551_exp
3
exp_dac04
Motor
tip551_exp
4
exp_mot01
Motor
omsvme58_exp
1
exp_mot02
Motor
omsvme58_exp
2
...
We learn e.g. that the device exp mot01 is available through the controller omsvme58 exp.
Chapter 13
Examples
13.1
Access the MacroServer Environment from Python
#!/usr/bin/env python
import os
import PyTango
from taurus.core.util.codecs import CodecFactory
ms=PyTango.DeviceProxy("haso107d1:10000/p09/macroserver/haso107d1.01")
#
# Read environment
#
value = ms.Environment
value = CodecFactory().getCodec(’pickle’).decode(value)
dct = value[1][’new’]
for key in dct.keys():
#
# ScanHistory produces much output
#
if key == ’ScanHistory’:
continue
print "---- " + key, dct[key]
#
# Change environment. For ex. ’ScanDir’
#
dct[’ScanDir’] = "/home/someUser/temp"
new_value = dict(new=dct)
new_value = CodecFactory().getCodec(’pickle’).encode((’’, new_value))
#
# update the macroserver
#
ms.write_attribute(’Environment’, new_value)
190
13.2
Fetch MacroServer Environment Variables (P10)
#!/usr/bin/env python
import os, sys
import PyTango
from taurus.core.util.codecs import CodecFactory
import HasyUtils
msLst = HasyUtils.getLocalMacroServerNames()
#
# msLst: [’p99/macroserver/hostname.01’]
# speak to the first, probaly the only server
#
ms=PyTango.DeviceProxy( msLst[0])
#
# Read the environment
#
value = ms.Environment
value = CodecFactory().getCodec(’pickle’).decode(value)
dct = value[1][’new’]
if len( sys.argv) > 2:
sys.exit(0)
#
# if no argument is supplied, print the whole environment (except ScanHisto
#
if len( sys.argv) == 1:
print "The environment of", msLst[0]
for key in dct.keys():
#
# ScanHistory produces much output
#
if key == ’ScanHistory’:
continue
print "---- " + key, dct[key]
print "\nUsage, e.g.:"
print " ./msEnv.py "
print " ./msEnv.py ScanDir"
print " ./msEnv.py ScanPrefix"
print " ./msEnv.py DetectorName"
sys.exit(0)
if not dct.has_key( sys.argv[1]):
print "%s not in the environment" % sys.argv[1]
sys.exit(0)
print dct[sys.argv[1]]
13.3
Change the Attribute Configuration, %6.2f to %g
The following script runs through all attributes of a TangoDB and identifies those that have the
%6.2f format in their configuration. It changes this format to %g.
#!/usr/bin/env python
""" attrConfig.py changes the attribute configuration from ’%6.2f’ to ’%g’
import sys
import PyTango
import HasyUtils
from optparse import OptionParser
usage = "%prog [-x] [-c]\n" + \
" Changes the attribute configuration from ’%6.2f’ to ’%g’"
parser = OptionParser(usage=usage)
parser.add_option( "-c", action="store_true", dest="classlist",
default = False, help="display classlist (harmless)")
parser.add_option( "-d", action="store_true", dest="dryrun",
default = False, help="dry run (harmless)")
parser.add_option( "-x", action="store_true", dest="execute",
default = False, help="really execute (not harmless)")
(options, args) = parser.parse_args()
#
# ’dry-run’ and ’execute’, both cannot by True
#
if options.dryrun and options.execute:
parser.print_help()
sys.exit(255)
#
# nothing specified, print help and exit
#
if not options.classlist and not options.dryrun and not options.execute:
parser.print_help()
sys.exit(255)
#
# get all classes
#
classList = HasyUtils.getClassList()
#
# the class list is printed as an array [ ’Motor’, ’OmsVme58’, ...]
#
if options.classlist:
print "Classes on", HasyUtils.getHostList()
print classList
sys.exit(0)
#
# you may wish to find all classes with ’./attrConfig.py -c’ and
# then limit the action to selected classes, like
#
# classList = [ ’GalilDMCSlit’, ’Motor’, ’OmsVme58’, ’PseudoAxes’, ’PseudoA
#
for clss in classList:
print clss
if options.classlist:
continue
devs = HasyUtils.getDeviceNamesByClass( clss)
for dev in devs:
print " ", dev
try:
proxy = PyTango.DeviceProxy( dev)
attrs = proxy.get_attribute_list()
except:
continue
for attr in attrs:
attrInfo = proxy.get_attribute_config( attr)
if attrInfo.format == "%6.2f":
print "
", attrInfo.name, attrInfo.format
if options.execute is True:
print "
from ’%6.2f’ to ’%g’"
attrInfo.format = "%g"
proxy.set_attribute_config( attrInfo)
elif options.dryrun is True:
print "
would change ’%6.2f’ to ’%g’"
13.4
Macro to setup the Lambda detector
If measurements involving a Lambda detector are carried out using the Pool controllers, certain
attributes have to be set before each scan. An example Macro can be found here .
13.5
Lambda Integration as VcExecutor (P01)
Sardana has a general detector interface, see section 11.3.1. Since accessing the Macroserver environment may be too time consuming, the detector controller does not update the following Lambda
attributes: FileStartNum, SaveFilePath and FilePrefix. These attributes have to be set before a scan
starts, e.g. by a Macro (13.4).
The code below is an example for a VcExecutonewr which operates a Lambda detector, including
the mentioned attributes. This is the corresponding online.xml entry:
<device>
<name>vc_lmbd</name>
<tags>expert,user</tags>
<type>counter</type>
<module>counter_tango</module>
<device>p01/vcexecutor/lmbd.01</device>
<control>tango</control>
<hostname>haspp01eh2:10000</hostname>
</device>
The VcExecutor code:
#!/bin/env python
""" Lambda implemented as a VcExecutor instance """
#
# Properties:
#
Device: hasclambda:10000/petra3/lambda/01
#
Door:
p01/door/haspp01eh2.01
#
VcCode: /usr/local/experiment/Tango/VcCode/lmbd.py
#
import PyTango
import sys, os, math, time
import HasyUtils
class VC:
def __init__(self, doorname = None, devicename = None):
"""
the doorname is needed to access the environment and mg
the devicename is the name of the Lambda server
"""
if not doorname:
PyTango.Except.throw_exception( "n.n.",
"no doorname supplied",
"vc_lambda.__init__")
if not devicename:
PyTango.Except.throw_exception( "n.n.",
"no devicename supplied",
"vc_lambda.__init__")
self.dmsp = HasyUtils.DMSP( doorname)
# This can not be done in Reset because it causes time out when doi
# scan ... The problem is that this is only read ones when the serv
# is started
mg_name = self.dmsp.getEnv("ActiveMntGrp")
self.mg_device = PyTango.DeviceProxy(mg_name)
self.sampleTime = float( self.mg_device.IntegrationTime)
try:
self.device = PyTango.DeviceProxy( devicename)
except PyTango.DevFailed, e:
PyTango.Except.print_exception( e)
sys.exit( 255)
self.prefix = None
def dev_state( self):
argout = self.device.state()
return argout
def read_Counts( self):
""" wait for the device to be finished"""
while self.device.state() != PyTango.DevState.ON:
time.sleep(0.1)
return 1
def write_Counts( self, argin):
pass
def Reset(self):
""" prepares the
acquisition of a single frame and starts the acqu
if self.device.state() != PyTango.DevState.ON:
PyTango.Except.throw_exception( "state is %s " % self.device.st
"state is not ON",
"vc_lmbd.reset")
#
# fetch the sample time from the MG
#
self.sampleTime = float( self.mg_device.IntegrationTime)
if math.isnan( self.sampleTime):
PyTango.Except.throw_exception( "Not initialized",
"IntegrationTime is nan",
"vc_lmbd.reset")
#
# ShutterTime [ms]
#
self.device.ShutterTime = self.sampleTime*1000.
#
# the prefix corresponds to the scan name, e.g.: hasylab_00001
# it is constructed using ScanFile (hasylab.fio) and ScanID
#
scanFile = self.dmsp.getEnv( ’ScanFile’)
if type(scanFile) is list:
scanFile = scanFile[0]
scanID = self.dmsp.getEnv( ’ScanID’)
scanDir = self.dmsp.getEnv( ’ScanDir’)
#
# prefix, e.g.: sample_00123
#
prefix = "%s_%05d" % (scanFile.split(’.’)[0], scanID)
saveFilePath = "%s/%s" % (scanDir, prefix)
#
# new prefix: reset the image number and create a new directory
#
if not prefix == self.prefix:
self.prefix = prefix
os.mkdir( saveFilePath, 0777)
os.chmod( saveFilePath, 0777)
saveFilePathFinal = saveFilePath + "/lmbd"
os.mkdir( saveFilePathFinal)
os.chmod( saveFilePathFinal, 0777)
self.device.FileStartNum = 1
self.device.SaveFilePath = saveFilePathFinal
self.device.FilePrefix = prefix
#
#
#
self.device.state()
self.device.StartAcq()
return 1
13.6
Signal Generator as VcExecutor (with Offset, P09)
#!/bin/env python
#
# this Vc counter produces a Gauss peak, nPoint == 50
#
import HasyUtils
import random, math
class VC:
def __init__(self):
print " VC.init"
self.xMin = 0.
self.xMax = 50
self.width = 3.
self.height = 800.
self.x = 0.
self.x0 = 25.
self.count = 0
self.offset = 0.
#
# dev_state
#
def dev_state( self):
argout = self.proxies[0].State()
return argout
#
# Counts
#
def read_Counts( self):
self.x += 0.2
self.count += 1
if self.x >= self.xMax:
self.x = 0
self.x0 = random.random()*self.width + self.xMax/2.
self.height = 1000.*random.random() + 500.
temp = (self.x - self.x0)/self.width;
temp = self.height*math.exp( -temp*temp/self.width)
print "readCounts returns ", temp
return temp
def write_Counts( self, argin):
return 1
#
# Offset
#
def read_Offset( self):
return self.offset
def write_Offset( self, argin):
self.offset = argin
return 1
#
# reset
#
def Reset(self):
return True
13.7
Pilatus Integration as Vc (obsolete)
This section demonstrates how a Pilatus detector is operated from an instance of the VcExecutor
server.
Three properties are needed, e.g.:
VcCode
Door
Device
/home/p17user/prog/vc_pilatus.py
p17/door/exp.01
p17/pilatus/exp.01
This is the code itself. The function Reset() prepares everything and starts the acquistion. read Counts()
waits for the Pilatus to complete.
This implementation can not be actually used. The read out of the IntegrationTime can be done
only onces when the server is started, because doing it in Reset produces a timeout during the
scan. Hence the name of the measurement group and the time have to be correctly set before the
VcExecutor server is started, and the server has to be restarted if they change.
Please use the corresponding Sardana Controller instead.
#!/bin/env python
""" Pilatus implemented as a VcExecutor instance """
import
import
import
import
PyTango
sys
Hasylab
math
class VC:
def __init__(self, doorname = None, devicename = None):
"""
the doorname is needed to access the environment and mg
the devicename is the name of the Pilatus server
"""
if not doorname:
PyTango.Except.throw_exception( "n.n.",
"no doorname supplied
֒→ ",
"vc_pilatus.__init__
֒→ ")
if not devicename:
PyTango.Except.throw_exception( "n.n.",
"no devicename
֒→ supplied",
"vc_pilatus.__init__
֒→ ")
self.dmsp = Hasylab.DMSP( doorname)
# This can not be done in Reset because it causes time
֒→ out when doing the
# scan ... The problem is that this is only read ones
֒→ when the server
# is started
mg_name = self.dmsp.getEnv("ActiveMntGrp")
self.mg_device = PyTango.DeviceProxy(mg_name)
self.sampleTime = float( self.mg_device.IntegrationTime)
try:
self.device = PyTango.DeviceProxy( devicename)
except PyTango.DevFailed, e:
Except.print_exception( e)
sys.exit( 255)
#
# nr counts the images in a scan. this number is reset,
֒→ the prefix
#
self.nr = 1
self.prefix = None
def dev_state( self):
argout = self.device.state()
return argout
def read_Counts( self):
""" wait for the device to be finished"""
while self.device.state() != PyTango.DevState.ON:
time.sleep(0.1)
return 1
def write_Counts( self, argin):
pass
def read_FilePrefix( self):
file_prefix = self.device.FilePrefix
return file_prefix
def write_FilePrefix( self, argin):
self.device.FilePrefix = argin
def Reset(self):
""" prepares the acquisition of a single frame and
֒→ starts the acquisition """
if self.device.state() != PyTango.DevState.ON:
PyTango.Except.throw_exception( "state is %s " % self
֒→ .device.state(),
"state is not ON",
"vc_pilatus.reset")
self.device.NbFrames = 1
#
# fetch the sample time from the MG
#
if math.isnan( self.sampleTime):
PyTango.Except.throw_exception( "Not initialized",
"IntegrationTime is
֒→ nan",
"vc_pilatus.reset")
self.device.ExposureTime = self.sampleTime
#
# the prefix corresponds to the scan name, e.g.:
֒→ hasylab_00001
# it is constructed using ScanFile (hasylab.fio) and
֒→ ScanID
#
scanFile = self.dmsp.getEnv( ’ScanFile’)
scanID = self.dmsp.getEnv( ’ScanID’)
prefix = "%s_%05d" % (scanFile.split(’.’)[0], scanID)
if not prefix == self.prefix:
self.prefix = prefix
self.nr = 1
self.device.FilePrefix = prefix
self.device.FilePostfix = ’.tif’
self.device.FileStartNum = self.nr
#
# the FileDir attribute is set by jive or so
#
self.device.state()
self.device.StartStandardAcq()
self.nr = self.nr + 1
13.8
Virtual Motor as VMExecutor
This section demonstrates how to implement a virtual motor using the VmExecutor class.
One property is needed, e.g.:
VmCode
/home/p17user/prog/vm_1.py
This is the code itself.
#!/bin/env python
import PyTango
class VM:
#
# init_device
#
def __init__( self):
self.ResultSim = None
self.PositionSim = None
self.proxies = []
self.proxies.append( PyTango.DeviceProxy( "p17/
֒→ motor/oh1.03"))
self.proxies.append( PyTango.DeviceProxy( "p17/
֒→ motor/oh1.04"))
self.proxies.append( PyTango.DeviceProxy( "p17/
֒→ motor/oh1.07"))
return
#
# dev_state
#
def dev_state( self):
argout = PyTango.DevState.ON
#
# if one device is in FAULT the VM is in FAULT
֒→ too
#
for proxy in self.proxies:
if proxy.state() == PyTango.DevState.
֒→ FAULT:
argout = PyTango.DevState.FAULT
break
if argout == PyTango.DevState.ON:
#
# if one device is MOVING the VM is
֒→ MOVING too
#
for proxy in self.proxies:
if proxy.state() == PyTango.
֒→ DevState.MOVING:
argout = PyTango.DevState
֒→ .MOVING
break
return argout
#
# Position
#
def read_Position( self):
return self.proxies[0].read_attribute( "Position
֒→ ").value
def write_Position( self, argin):
if( argin < self.proxies[0].UnitLimitMin or
argin > self.proxies[0].UnitLimitMax):
PyTango.Except.throw_exception(
"vm1",
"requested position outside
֒→ limits %g %g " % (self.
֒→ proxies[0].UnitLimitMin,
֒→ self.proxies[0].
֒→ UnitLimitMax),
"VmExecutor")
for proxy in self.proxies:
proxy.write_attribute( "Position", argin)
return 1
#
# UnitLimitMax
#
def read_UnitLimitMax( self):
return self.proxies[0].UnitLimitMax
def write_UnitLimitMax( self, argin):
self.UnitLimitMax = argin
#
# UnitLimitMin
#
def read_UnitLimitMin( self):
return self.proxies[0].UnitLimitMin
def write_UnitLimitMin( self, argin):
self.UnitLimitMin = argin
#
# CwLimit, CcwLimit
#
def read_CwLimit( self):
return self.proxies[0].CwLimit
def read_CcwLimit( self):
return self.proxies[0].CcwLimit
#
# PositionSim
#
def read_PositionSim( self):
if self.PositionSim is None:
PyTango.Except.throw_exception( "vm1", "
֒→ PositionSim is undefined", "
֒→ VmExecutor")
return self.PositionSim
def write_PositionSim( self, argin):
self.PositionSim = argin
self.ResultSim = []
self.ResultSim.append( "x: %g " % argin)
def read_ResultSim( self):
if self.ResultSim is None:
PyTango.Except.throw_exception( "vm1", "
֒→ ResultSim is undefined", "
֒→ VmExecutor")
return self.ResultSim
def StopMove( self):
for proxy in self.proxies:
proxy.StopMove()
return 1
Index
?, 18
00-start.py, 20
Configuration
00-start.py, 20
SardanaConfig.py, 156
DIAL, 10
DOOR, 51
store, 19
Taurus, 7
usenv, 43
whos, 18
write attribute(), 103
egPosition(), 103
enviroment, 43
expconf, 26
FioAdditions, 22, 23
FlagFioWriteMotorPositions, 22
getEnv(), 100
HasyUtils, 186
lsenv, 43
lsmeas, 25
Macro API, 53
MACRO SERVER, 51
macros, 100
preset scan, 24
profiles, 42
pylint, 53
read attribute(), 103
relmac, 100
Sardana, 7
SardanaConfig.py, 156
scan
FioAdditions, 22, 23
FlagFioWriteMotorPositions, 22
monitor mode, 24
preset mode, 24
senv, 43
setEnv(), 100
shorthands, 19
ShowDial, 10
203