dev_maplog_controller.md 7.8 KB
Newer Older
1
2
# Adding logging and mapping capabilities to Controller

Cyril Guilloud's avatar
Cyril Guilloud committed
3

4
To know how to use logging inside the shell, see: [Shell Logging](shell_logging.md)
Cyril Guilloud's avatar
Cyril Guilloud committed
5

6
To know more about mapping and how to use it, see: [Session map](dev_instance_map.md)
Cyril Guilloud's avatar
Cyril Guilloud committed
7

8
9
## Logging and Mapping

Cyril Guilloud's avatar
Cyril Guilloud committed
10
11
Logging and Mapping instances are strictly related in Bliss: every
device should register himself before gaining those features.
12
13
14

## How to Register a device

Cyril Guilloud's avatar
Cyril Guilloud committed
15
```python
16
from bliss.common import session
Cyril Guilloud's avatar
Cyril Guilloud committed
17
from bliss.common.logtools import LogMixin
18

Cyril Guilloud's avatar
Cyril Guilloud committed
19
20
class MyController(LogMixin):
    def __init__(self, *args, **kwargs):
21
        session.get_current().map.register(self)
Cyril Guilloud's avatar
Cyril Guilloud committed
22
23
24
        ...
        self._logger.info("HI, I am born")
```
25

Cyril Guilloud's avatar
Cyril Guilloud committed
26
27
The preceding is a barebone example of how to register a device inside
device map and send an INFO logging message.
28
29
30
31
32

Key points are the following:

### Add LogMixin to the device

Cyril Guilloud's avatar
Cyril Guilloud committed
33
34
35
This will add `_logger` method to class that will be the entry point for
all logging operations and will also raise an exception if the logging
will be used before device is mapped inside bliss device map.
36
37
38

### Register the device

39
This is done calling `session.get_current().map.register` passing at list the self
Cyril Guilloud's avatar
Cyril Guilloud committed
40
41
42
parameter. If _logger methods are used before the registration this
will fail raising an exception; for this reason mapping.register
should be called as soon as possible.
43
44
45

## How to Create a nice device map

Cyril Guilloud's avatar
Cyril Guilloud committed
46
The map is in fact a Graph that wants to register every relevant
47
instance of a Session, including Controller, Connections, Devices,
Cyril Guilloud's avatar
Cyril Guilloud committed
48
49
50
51
Axis, and so on.

When registering a device it is convenient to add as much information
as possible in order to have an usefull map that can be used to
52
represent the session or to apply any sort of desired handler.
Cyril Guilloud's avatar
Cyril Guilloud committed
53

54
For this reason is important to add:
Cyril Guilloud's avatar
Cyril Guilloud committed
55
56
57
58
59
60

* parents_list: a list containing the parents of the instance, in case
  of a device it will be the controller instance, in case of a
  communication it will be a controller but also "comms".
* children_list: a list containing children istances as comms,
  transactions, devices, axis
61
62
* tag: this should be the best suited name to represent the instance, if not
       given instance.name will be used or id of the object
63
64
65

Some Examples:

66
67
### Example 1:

68
Here we have a Motor that is child of a controller
Cyril Guilloud's avatar
Cyril Guilloud committed
69
```python
70
71
72
73
74
75
# self is motor instance (we are inside Axis.axis class)
# 'name' attribute is used as default to represent the object in the map
# 'tag' can be passed as kwarg to replace the name
# default is using name attribute of class
m = session.get_current().map
m.register(self, parents_list=[self.controller])
Cyril Guilloud's avatar
Cyril Guilloud committed
76
```
77

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
{% dot session_map_basic.svg
strict digraph  {
  rankdir="LR";
  splines=false;

	session;
	devices;
  controller [label="ee6beb9efb",];
  axis [label="m0"]
  controller -> axis;
  devices -> controller;
	session -> devices;
	comms;
	session -> comms;
	counters;
	session -> counters;
}
%}
### Example 2:

98
Here we have a controller with a child connection
Cyril Guilloud's avatar
Cyril Guilloud committed
99
```python
100
101
102
# self is test_controller
m = session.get_current().map
m.register(self, children_list=[self._cnx], tag='test controller')
Cyril Guilloud's avatar
Cyril Guilloud committed
103
```
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
{% dot session_map_basic.svg
strict digraph  {
  rankdir="LR";
  splines=false;

	session;
	devices;
  controller [label="test controller",];
  conn [label="tcp_ip"]
  controller -> conn;
  devices -> controller;
	session -> devices;
	comms;
	session -> comms;
	counters;
	session -> counters;
}
%}
### Example 3:
Cyril Guilloud's avatar
Cyril Guilloud committed
123
124

Here we have a serial connection that we also want to be child of
125
"session"->"comms"
Cyril Guilloud's avatar
Cyril Guilloud committed
126
127

```python
128
129
130
131
132
133
134
135
# registering m0, this normally is automatic, just as an example
# first passage we register m0, the controller and the connection
m = session.get_current().map
m.register(m0, parent_list=[m0.controller])
# in the second passage we register the TCP connection as a child of
# m0 and of comms
m = session.get_current().map
m.register(m0.conn, parent_list=[m0.controller, 'comms'])
Cyril Guilloud's avatar
Cyril Guilloud committed
136
137
```

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
{% dot session_map_basic.svg
strict digraph  {
  rankdir="LR";
  splines=false;

	session;
	devices;
	session -> devices;
	comms;
	session -> comms;
	counters;
	session -> counters;
  controller;
  devices -> controller;
  m0;
  controller -> m0;
  tcp;
  controller -> tcp;
  comms -> tcp;

}
%}
### Example 4:

Cyril Guilloud's avatar
Cyril Guilloud committed
162
163
164
165
166
To explain the flexibility here we are mapping inside a Command class
(that is self) and `self._fd` is a child socket, in fact we are inside
Command but we are recording all links beetween them. The result will
something like this:

167
168
169
170
171
172
173
174
175
176
177
178
179


In fact, instances that will not have parents will be childs of
"session"->"devices" by default and later eventually remapped if we
register another instance as parent of `Command`.

```python
from bliss.common import session
m = session.get_current().map

self._fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
m.register(self._fd, parents_list=[self, "comms"], tag=f"Socket[{local_host}:{local_port}",
```
Cyril Guilloud's avatar
Cyril Guilloud committed
180
181
182
183
184
{% dot devices_mapping.svg
  digraph devices_mapping{
  rankdir="LR";
  splines=false;

185
186
187
188
189
190
191
192
193
194
195
196
  session;
  devices;
  session -> devices;
  comms;
  session -> comms;
  counters;
  session -> counters;
  command;
  devices -> command;
  sock [label="Socket[localhost:47319]"];
  command -> sock;
  comms -> sock;
Cyril Guilloud's avatar
Cyril Guilloud committed
197
198
199
200

  }
%}

201
### Final Considerations:
Cyril Guilloud's avatar
Cyril Guilloud committed
202
203
204
205
206
207

There is no problem if you want to register the same device twice or
even more times: it will automatically remap adding or removing links
if necessary.  In general this is convenient for example when you want
to log as soon as possible and then after creating let's say a Socket
you want to register it a child.
208
209
210

The Bliss barebone map is something like this:

211
212
213
214
215
216
217
218
219
220
221
222
{% dot session_map_basic.svg
strict digraph  {
	node [label="\N"];
	session;
	devices;'
	session -> devices;
	comms;
	session -> comms;
	counters;
	session -> counters;
}
%}
Cyril Guilloud's avatar
Cyril Guilloud committed
223
224
225

Those Graph nodes are in fact string and they constitute the root to
wich all other nodes will be attached.
226

Cyril Guilloud's avatar
Cyril Guilloud committed
227
228
Be aware that all instances, if nothing is specify, will be child of
devices.
229
230
231
232


## Logging Instance Methods

233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
Every instance that inherits from LogMixin and is registered gains a _logger instance that is in fact a python logging.Logger instance with some more powers.

This means that you will find a ._logger attribute attached to your instance that you can use to send messages and configure logging.

The most useful methods are:

    * .debugon()
    * .debugoff()
    * .debug_data(message, data)
    * .set_ascii_format()
    * .set_hex_format()

### .debugon() and .debugoff()

Simply to set logging level to DEBUG or reset to default level.

### .debug_data, .set_ascii_format, .set_hex_format

The purpose of debug_data is to have a convenient way to debug aggregate data like string, bytestrings and dictionary. This is helpful for hardware comunication.

The first argument of debug_data is the user-readable message, the second is a string, bytestring or a dictionary.

set_ascii_format and set_hex_format methods allows to change the representation of data at runtime.

Let's do some examples:

### Passing a dictionary to debug_data:

```python

T_SESSION [14]: m0._logger.debug_data('Machine connection settings',{'ip':'10.81.0.23','hostname':'wcid00b'})
DEBUG 2019-05-17 14:58:27,861 session.devices.8d6318d713.axis.m0: Machine connection settings ip=10.81.0.23 ; hostname=wcid00b
```

### Simulating a message sent from `m0`.
Check how debug_data works and how we can change format from ascii to hex and viceversa, this can be done at runtime.

```python
TEST_SESSION [1]: m0._logger.debugon()
TEST_SESSION [2]: raw_msg = bytes([0,2,3,12,254,255,232,121,123,83,72])
TEST_SESSION [3]: m0._logger.set_ascii_format()
TEST_SESSION [4]: m0._logger.debug_data('Sending Data',raw_msg)
DEBUG 2019-05-17 15:24:26,231 session.devices.8d6318d.axis.m0: Sending Data bytes=11 b'\x00\x02\x03\x0c\xfe\xff\xe8y{SH'
TEST_SESSION [5]: m0._logger.set_hex_format()
TEST_SESSION [6]: m0._logger.debug_data('Sending Data',raw_msg)
DEBUG 2019-05-17 15:24:34,731 session.devices.8d6318d.axis.m0: Sending Data bytes=11 \x00\x02\x03\x0c\xfe\xff\xe8\x79\x7b\x53\x48
```