[pytango] 01/01: Imported Upstream version 8.1.2
Frédéric-Emmanuel Picca
picca at moszumanska.debian.org
Tue Aug 8 12:01:45 UTC 2017
This is an automated email from the git hooks/post-receive script.
picca pushed a commit to annotated tag upstream/8.1.2
in repository pytango.
commit 6e09dbf4c2027370578ca1c2de6e0742554bbd49
Author: Picca Frédéric-Emmanuel <picca at debian.org>
Date: Mon May 19 23:31:18 2014 +0200
Imported Upstream version 8.1.2
---
PKG-INFO | 2 +-
doc/_static/PowerSupplyDS.py | 80 +
doc/_static/arrow03.png | Bin 0 -> 5669 bytes
doc/_static/banner1.png | Bin 335135 -> 237697 bytes
doc/_static/banner2.png | Bin 89184 -> 73015 bytes
doc/_static/banner3.png | Bin 108825 -> 83303 bytes
doc/_static/database.png | Bin 0 -> 13726 bytes
doc/_static/device.png | Bin 0 -> 65909 bytes
doc/_static/ipython_db.html | 25 +
doc/_static/ipython_motor.html | 56 +
doc/_static/ipython_serial.html | 56 +
doc/_static/ipython_tango.html | 314 +++
doc/_static/itango.ico | Bin 0 -> 16446 bytes
doc/_static/itango.png | Bin 0 -> 15517 bytes
doc/{itango => _static}/itango00.png | Bin
doc/{itango => _static}/itango01.png | Bin
doc/{itango => _static}/itango02.png | Bin
doc/{itango => _static}/itango03.png | Bin
doc/{itango => _static}/itango04.png | Bin
doc/{itango => _static}/itango05.png | Bin
doc/{itango => _static}/itango06.png | Bin
doc/_static/jive_powersupply.png | Bin 0 -> 14023 bytes
doc/_static/jquery-1.9.1.min.js | 20 +
doc/_static/jssor.css | 23 +
doc/_static/jssor.slider.mini.js | 2 +
doc/_static/motor.png | Bin 0 -> 13299 bytes
doc/_static/serial.png | Bin 0 -> 6323 bytes
doc/_static/starter.png | Bin 0 -> 15763 bytes
doc/_templates/index.html | 140 +-
doc/_templates/layout.html | 2 +
doc/api.rst | 8 +-
doc/{client => client_api}/attribute_proxy.rst | 0
doc/{client => client_api}/device_proxy.rst | 0
doc/client_api/green.rst | 21 +
doc/{client => client_api}/group.rst | 0
doc/{client => client_api}/index.rst | 1 +
doc/client_api/miscellaneous.rst | 144 ++
doc/{client => client_api}/other.rst | 0
doc/conf.py | 4 +
doc/contents.rst | 11 +-
doc/data_types.rst | 2 +-
doc/exception.rst | 48 +-
doc/faq.rst | 132 +-
doc/{client/miscellaneous.rst => green.rst} | 176 +-
doc/{server/index.rst => howto.rst} | 873 +++++---
doc/{server/index.rst => howto.rst~} | 878 +++++---
doc/{itango/highlights.rst => itango.rst} | 102 +-
doc/itango/features.rst | 40 -
doc/itango/index.rst | 32 -
doc/quicktour.rst | 629 ++----
doc/revision.rst | 32 +-
doc/server/server.rst | 109 -
doc/{server => server_api}/attribute.rst | 0
doc/{server => server_api}/device.rst | 4 +-
doc/{server => server_api}/device_class.rst | 0
doc/server_api/index.rst | 14 +
doc/{server => server_api}/logging.rst | 0
doc/server_api/server.rst | 303 +++
doc/{server => server_api}/util.rst | 0
doc/start.rst | 25 +-
setup.py | 69 +-
src/boost/cpp/attr_conf_event_data.cpp | 45 +-
src/boost/cpp/attribute_proxy.cpp | 19 +-
src/boost/cpp/base_types.cpp | 73 +-
src/boost/cpp/callback.cpp | 5 +
src/boost/cpp/callback.h | 6 +-
src/boost/cpp/data_ready_event_data.cpp | 39 +-
src/boost/cpp/device_attribute.cpp | 2 +-
src/boost/cpp/device_attribute.h | 10 +-
src/boost/cpp/device_proxy.cpp | 12 +
src/boost/cpp/event_data.cpp | 45 +-
src/boost/cpp/fast_from_py.h | 3 +-
src/boost/cpp/fast_from_py_numpy.hpp | 1 -
src/boost/cpp/server/attribute.cpp | 153 +-
src/boost/cpp/server/attribute.h | 7 +
src/boost/cpp/server/device_impl.cpp | 83 +-
src/boost/cpp/server/device_impl.h | 2 +-
src/boost/cpp/server/dserver.cpp | 7 -
src/boost/cpp/server/tango_util.cpp | 27 +-
src/boost/cpp/to_py_numpy.hpp | 2 -
src/boost/python/__init__.py | 67 +-
src/boost/python/attr_data.py | 9 +-
src/boost/python/connection.py | 8 +-
src/boost/python/databaseds/DataBaseds | 5 +-
src/boost/python/databaseds/create_db.sql | 6 +
src/boost/python/databaseds/database.py | 750 ++++++-
src/boost/python/databaseds/db_access.py | 517 -----
src/boost/python/databaseds/db_access/__init__.py | 14 +
src/boost/python/databaseds/db_access/sqlite3.py | 1575 +++++++++++++++
src/boost/python/db.py | 2 +-
src/boost/python/device_proxy.py.new | 2088 ++++++++++++++++++++
src/boost/python/device_server.py | 129 +-
src/boost/python/group.py | 4 +-
src/boost/python/ipython/__init__.py | 10 +-
src/boost/python/ipython/common.py | 17 +-
.../python/ipython/ipython_00_10/ipy_install.py | 19 +-
.../python/ipython/ipython_00_11/ipy_install.py | 15 +-
.../python/ipython/ipython_00_11/ipython_00_11.py | 2 +-
.../python/ipython/ipython_10_00/ipy_install.py | 17 +-
.../python/ipython/ipython_10_00/ipython_10_00.py | 127 +-
src/boost/python/ipython/resource/serial.png | Bin 6365 -> 6323 bytes
src/boost/python/pytango_init.py | 4 +-
src/boost/python/release.py | 2 +-
src/boost/python/server.py | 729 +++----
src/boost/python/utils.py | 118 +-
105 files changed, 8251 insertions(+), 2901 deletions(-)
diff --git a/PKG-INFO b/PKG-INFO
index cbeb299..4d32d38 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: PyTango
-Version: 8.1.1
+Version: 8.1.2
Summary: A python binding for the Tango control system
Home-page: http://www.tinyurl.com/PyTango/
Author: Tiago Coutinho
diff --git a/doc/_static/PowerSupplyDS.py b/doc/_static/PowerSupplyDS.py
new file mode 100644
index 0000000..e5b8e0d
--- /dev/null
+++ b/doc/_static/PowerSupplyDS.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""Demo power supply tango device server"""
+
+import time
+import numpy
+
+from PyTango import AttrQuality, AttrWriteType, DispLevel, DevState, DebugIt
+from PyTango.server import Device, DeviceMeta, attribute, command, run
+from PyTango.server import device_property
+
+
+class PowerSupply(Device):
+ __metaclass__ = DeviceMeta
+
+ voltage = attribute(label="Voltage", dtype=float,
+ display_level=DispLevel.OPERATOR,
+ access=AttrWriteType.READ,
+ unit="V",format="8.4f",
+ doc="the power supply voltage")
+
+ current = attribute(label="Current", dtype=float,
+ display_level=DispLevel.EXPERT,
+ access=AttrWriteType.READ_WRITE,
+ unit="A",format="8.4f",
+ min_value=0.0, max_value=8.5,
+ min_alarm=0.1, max_alarm=8.4,
+ min_warning=0.5, max_warning=8.0,
+ fget="get_current",
+ fset="set_current",
+ doc="the power supply current")
+
+ noise = attribute(label="Noise",
+ dtype=((int,),),
+ max_dim_x=1024, max_dim_y=1024)
+
+ host = device_property(dtype=str)
+ port = device_property(dtype=int, default_value=9788)
+
+ def init_device(self):
+ Device.init_device(self)
+ self.__current = 0.0
+ self.set_state(DevState.STANDBY)
+
+ def read_voltage(self):
+ self.info_stream("read_voltage(%s, %d)", self.host, self.port)
+ return 9.99, time.time(), AttrQuality.ATTR_WARNING
+
+ def get_current(self):
+ return self.__current
+
+ def set_current(self, current):
+ # should set the power supply current
+ self.__current = current
+
+ @DebugIt()
+ def read_noise(self):
+ return numpy.random.random_integers(1000, size=(100, 100))
+
+ @command
+ def TurnOn(self):
+ # turn on the actual power supply here
+ self.set_state(DevState.ON)
+
+ @command
+ def TurnOff(self):
+ # turn off the actual power supply here
+ self.set_state(DevState.OFF)
+
+ @command(dtype_in=float, doc_in="Ramp target current",
+ dtype_out=bool, doc_out="True if ramping went well, False otherwise")
+ def Ramp(self, target_current):
+ # should do the ramping
+ return True
+
+
+if __name__ == "__main__":
+ run([PowerSupply])
+
diff --git a/doc/_static/arrow03.png b/doc/_static/arrow03.png
new file mode 100644
index 0000000..1855c0b
Binary files /dev/null and b/doc/_static/arrow03.png differ
diff --git a/doc/_static/banner1.png b/doc/_static/banner1.png
index 75ae5d5..d05a270 100644
Binary files a/doc/_static/banner1.png and b/doc/_static/banner1.png differ
diff --git a/doc/_static/banner2.png b/doc/_static/banner2.png
index 4eaf882..fc69431 100644
Binary files a/doc/_static/banner2.png and b/doc/_static/banner2.png differ
diff --git a/doc/_static/banner3.png b/doc/_static/banner3.png
index 6299619..4981397 100644
Binary files a/doc/_static/banner3.png and b/doc/_static/banner3.png differ
diff --git a/doc/_static/database.png b/doc/_static/database.png
new file mode 100644
index 0000000..ad598bb
Binary files /dev/null and b/doc/_static/database.png differ
diff --git a/doc/_static/device.png b/doc/_static/device.png
new file mode 100644
index 0000000..1b52f7d
Binary files /dev/null and b/doc/_static/device.png differ
diff --git a/doc/_static/ipython_db.html b/doc/_static/ipython_db.html
new file mode 100644
index 0000000..1c9b793
--- /dev/null
+++ b/doc/_static/ipython_db.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Monospace'; font-size:8pt; font-weight:400; font-style:normal;">
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#9400d3;">ITango 8.1.1</span> -- An interactive <span style=" color:#9400d3;">Tango</span> client.</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Running on top of Python 2.6.6, IPython 1.1 and PyTango 8.1.1</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">1</span><span style=" color:#000080;">]:</span> db</p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#8b0000;">Result [</span><span style=" font-weight:600; color:#8b0000;">1</span><span style=" color:#8b0000;">]:</span> </p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<table border="0" style=" margin:0px;" width="100%" cellspacing="2" cellpadding="2">
+<tr>
+<td width="140" rowspan="2" style=" vertical-align:middle;">
+<p align="center" style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><img src="database.png" height="128" style="vertical-align: middle;" /></p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">bcu01ctrl.esrf.fr:10000</span></p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">TANGO Database sys/database/2<br /><br />Running since 2013-11-12 17:08:54<br /><br />Devices defined = 32<br />Devices exported = 4<br />Device servers defined = 10<br />Device servers exported = 2<br /><br />Device properties defined = 17 [History lgth = 30]<br />Class properties defined = 63 [History lgth = 56]<br />Device attribute properties defined = 4 [History lgth = 7]<br />Class attribute properties defined = 0 [Histor [...]
+<br/>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">2</span><span style=" color:#000080;">]:</span> </p></body></html>
diff --git a/doc/_static/ipython_motor.html b/doc/_static/ipython_motor.html
new file mode 100644
index 0000000..5c60d85
--- /dev/null
+++ b/doc/_static/ipython_motor.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Monospace'; font-size:8pt; font-weight:400; font-style:normal;">
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#9400d3;">ITango 8.1.1</span> -- An interactive <span style=" color:#9400d3;">Tango</span> client.</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Running on top of Python 2.6.6, IPython 1.1 and PyTango 8.1.1</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">1</span><span style=" color:#000080;">]:</span> energy = Motor("energy")</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">2</span><span style=" color:#000080;">]:</span> energy</p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#8b0000;">Result [</span><span style=" font-weight:600; color:#8b0000;">2</span><span style=" color:#8b0000;">]:</span> </p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<table border="0" style=" margin:0px;" width="100%" cellspacing="2" cellpadding="2">
+<tr>
+<td width="140" rowspan="7" style=" vertical-align:middle;">
+<p align="center" style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><img src="motor.png" height="128" style="vertical-align: middle;" /></p></td>
+<td width="140">
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Name:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">motor/icepapctrl/34</span></p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Alias:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">energy</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Database:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">bcu01ctrl.esrf.fr:10000</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Type:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Motor</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Server:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Sardana/energy</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Server host:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">bcu01ctrl.esrf.fr</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Documentation:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><a href="http://www.sardana-controls.org"><span style=" text-decoration: underline; color:#0000ff;">www.sardana-controls.org</span></a></p></td></tr></table>
+<br/>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">3</span><span style=" color:#000080;">]:</span> </p></body></html>
diff --git a/doc/_static/ipython_serial.html b/doc/_static/ipython_serial.html
new file mode 100644
index 0000000..c672f42
--- /dev/null
+++ b/doc/_static/ipython_serial.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Monospace'; font-size:8pt; font-weight:400; font-style:normal;">
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#9400d3;">ITango 8.1.1</span> -- An interactive <span style=" color:#9400d3;">Tango</span> client.</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Running on top of Python 2.6.6, IPython 1.1 and PyTango 8.1.1</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">1</span><span style=" color:#000080;">]:</span> serial_line = Serial("pc01/tty/1")</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">2</span><span style=" color:#000080;">]:</span> serial_line</p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#8b0000;">Result [</span><span style=" font-weight:600; color:#8b0000;">2</span><span style=" color:#8b0000;">]:</span> </p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<table border="0" style=" margin:0px;" width="100%" cellspacing="2" cellpadding="2">
+<tr>
+<td width="140" rowspan="7" style=" vertical-align:middle;">
+<p align="center" style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><img src="serial.png" height="128" style="vertical-align: middle;" /></p></td>
+<td width="140">
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Name:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">pc01/tty/1</span></p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Alias:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">-----</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Database:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">bcu01ctrl.esrf.fr:10000</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Type:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Serial</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Server:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Serial/pc01</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Server host:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">pc01.esrf.fr</p></td></tr>
+<tr>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Documentation:</p></td>
+<td>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><a href="http://www.esrf.fr/computing/cs/tango/tango_doc/ds_doc/tango-ds/Communication/SerialLine/index.html"><span style=" text-decoration: underline; color:#0000ff;">www.tango-controls.org/devices/Serial</span></a></p></td></tr></table>
+<br/>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">3</span><span style=" color:#000080;">]:</span> </p></body></html>
diff --git a/doc/_static/ipython_tango.html b/doc/_static/ipython_tango.html
new file mode 100644
index 0000000..f9d825a
--- /dev/null
+++ b/doc/_static/ipython_tango.html
@@ -0,0 +1,314 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="qrichtext" content="1" /><style type="text/css">
+p, li { white-space: pre-wrap; }
+</style></head><body style=" font-family:'Monospace'; font-size:8pt; font-weight:400; font-style:normal;">
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#9400d3;">ITango 8.1.1</span> -- An interactive <span style=" color:#9400d3;">Tango</span> client.</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;">Running on top of Python 2.6.6, IPython 1.1 and PyTango 8.1.1</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">1</span><span style=" color:#000080;">]:</span> tango_test = TangoTest("sys/tg_test/1")</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">2</span><span style=" color:#000080;">]:</span> wave, img = tango_test.wave, tango_test.short_image_ro</p>
+<p style="-qt-paragraph-type:empty; margin:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">3</span><span style=" color:#000080;">]:</span> fig, (axis1, axis2) = plt.subplots(1, 2, figsize=(9, 3));</p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;"> ...:</span> axis2.imshow(img); axis2.set_title("Beam");</p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;"> ...:</span> axis1.plot(wave); axis1.set_title("Sine");</p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;"> ...:</span> </p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><img src="data:image/png;base64,
+iVBORw0KGgoAAAANSUhEUgAAAgkAAADSCAYAAADABFM9AAAABHNCSVQICAgI
+fAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAIABJREFUeJzt3XtcVWXa//HP
+2ggiAoIcFVBMKxVByUNaajSlBjadtNFSnx7tYE6NnWuapvEwpTU9zkyT1Thl
+zfPE1Oh0mPqFUU1FTc2kpeZpOpoUG0NOAiLKcf3+uBFPgCKw1wa+79drvWBv
+1l7rWlvX2te+131ft2Xbto2IiIjIMVxOByAiIiLeSUmCiIiINEpJgoiIiDRK
+SYKIiIg0SkmCiIiINEpJgoiIiDRKSYKcsr/85S9MmTLF6TBERKSdKEmQE3r3
+3XcZNWoUPXv2JCQkhLFjx/Lpp58ya9Ys3nzzTafDExEvEh8fT0BAAEFBQQQG
+BnLhhReya9cup8OSU6QkQZpVXFzMpZdeys9//nPKy8spKCjgoYcewt/f3+nQ
+RMQLWZbF66+/zr59+ygqKqJ///789Kc/dTosOUVKEqRZX3zxBX5+fkyfPh3L
+svD19SUlJYVhw4bx5z//mQkTJjSs63K5WLVqFWeeeSaBgYFcd911HFnQ87HH
+HiM+Pp7g4GDOO+88du7c6cQhiYiHdO/enWnTpvHNN98AcODAARYsWEBkZCSh
+oaFcc801HDhwAICSkhKmTJlCeHg4QUFBXHjhhXz33XcN20pJSeH+++9n/Pjx
+BAUFcckll1BYWMisWbPo1asXiYmJfPvtt44cZ2emJEGaNXToUGpra5k3bx5v
+vvkmRUVFza6fmZnJ5s2b+fzzz3nttdd4/fXXAXj++ed57LHHeO+99ygrKyM1
+NZXp06d74hBExMMOfTmoqKhgzZo1nHvuuQDccsst5Ofns3PnTnbv3k1ZWRn3
+3ntvw2sWLlzInj17yM/Pp0+fPsyfP/+o7a5du5a//vWv5Obmkp2dzbhx41iw
+YAF79+5l1KhR3H///Z490K7AFjmBbdu22bNnz7ZjYmJsl8tlp6am2j/88IP9
+7LPP2uPHj29Yz7Is+6OPPmp4/JOf/MR+4IEHbNu27ZSUFHv16tUNf6utrbUD
+AgLsr776ynMHIiLtrn///nZgYKAdEhJi+/n52TExMfa2bdvsyspK29/f3965
+c2fDuv/617/sPn36NLqdbdu22T169Gh4nJKSYi9btqzh8V133WWnpaU1PM7I
+yLATEhLa4Yi6NrUkyAkNGzaM5557DrfbzZdffklhYSE33XQTlmUdt250dHTD
+7wEBAVRWVgLgdru55ZZbCA0NJTQ0lLCwMAAKCgo8cxAi4hGWZfHqq6+yd+9e
+KisreeKJJ5g4cSKFhYVUVlYycuTIhutAamoqZWVlAJSWlvLf//3fxMTEEBIS
+wrnnnktlZeVRtyyjoqIafvfz8yMyMvKox4euN9J2lCRIiwwaNIi5c+eyY8eO
+Fr2uT58+PPvss+zdu7dh2b9/P+ecc047RSoi3uCSSy6hW7dufPDBB/j6+vL1
+1183XANKSkooLy8H4JFHHiE3N5ctW7ZQUlLCRx99hG3bRyUJR2rsS4q0PSUJ
+0qyvvvqKxx9/nPz8fABycnJ44YUXGD169Alfe+QJfsMNN7Bs2bKGDkzl5eX8
+/e9/b7/ARcQxR36wv/766xQVFTF48GDmzJnDHXfcQUlJCQB5eXm88847gOm/
+4OvrS1BQEGVlZfz6179udrtNJQ/StpQkSLMCAwN55513SEpKomfPnowcOZKB
+Awfy2GOPAUdn88dm9pZlNTw3e/ZsbrjhBlJTUwkODubMM89UkiDSSf34xz9u
+qJNw55138tRTTzFixAhWrlxJaGgoQ4YMaRjltH37dgBuu+02SktLCQ0NZezY
+sVxwwQWNXlOO/L25v0vbsOxWpmPz5s0jIyODyMhItm3b1ug6Cxcu5J133qF7
+9+6sXr2a5OTk1uxSREREPKDVLQlz584lMzOzyb+/9NJLfP/99+zYsYPVq1cz
+d+7c1u5SRMSjMjMzSUxMZOjQoTz88MNOhyPiMa1OEiZMmEBoaGiTf1+3bh1z
+5swBIDk5mZqaGtxud2t3KyLiEZWVlSxYsIDMzEy2bt3Kiy++yObNm50OS8Qj
+2r1PgtvtJi4uruFxbGyskgQR6TDWr19PQkICMTExdOvWjRkzZpCRkeF0WCIe
+0c0TOzm220NjnUuCggZRXq4yvSLeYODAgQ0jUbq6xr7oZGVlHbWOZUUDezwb
+mMhJicK280751e2eJMTGxpKTk8PZZ58NmBMuNjb2uPXKy3eyYYPNoEEQEgKH
+8gjbhtJS+O47+Owz+PhjePttqKmBadPg+uth8OD2i3/x4sUsXry4/XZwihRX
+yyiullEv8cNO7r3YA5x3xOP4+sXTsoAUB/arGBqXhefjyK5fDnm/VVtr9yQh
+LS2N9PR0pk+fzqZNm/Dx8SEmJqbRdRsbem9ZJmkICYHhw+Gaa0zisGMH/PWv
+cP75MHQo3HsvXHDB4eRCRKQtHPqic0hOTs5RLQuHpXgsJpGmxXN0gtq6JKHV
+fRKuuuoqzjnnHL788kvi4uJ45plnWLVqFatWrQJg2rRpxMTEkJCQwHXXXcez
+zz7b2l1iWTBsGDzwgGlhmDsXbroJUlJMS4OISFsZPXo027dvJzc3l+rqatau
+XUtqaqrTYYl4RKtbEl544YUTrrNy5crW7qZJfn4wezbMnAnPPQdXXAFTp8JD
+D0H99ACtkpKS0vqNtAPF1TKKS06Vv78/Tz75JFOmTKGuro45c+Zw1llnOR1W
+E+KdDgDFcKR4pwNotVYXU2orlmW1SZnN0lL45S/hxRfhqafg4ovbIDiRLqat
+zseuwvRbWOR0GCKNWNKqc7nTlWXu1QseewzWrIGbb4af/hQOHnQ6KhERkY6n
+0yUJh0ycCFu2QGEhnHce7N7tdEQiIiIdS6dNEsC0KqxZA5ddBmPGwL//7XRE
+IiIiHUenThLAjIS4915YtQouvRReesnpiERERDoGj1Rc9AZTp8Kbb5qfpaUw
+b57TEYmIiHi3LpMkACQnQ1YWTJ4Me/fCHXc4HZGIiIj36lJJAsAZZ8A//wkX
+Xgi1tXD33U5HJCIi4p26XJIAEBcH775rRkAEBJihkiIiInK0LpkkAMTEwD/+
+YYZHBgSoj4KIiMixumySADBggEkUUlKgd28zVFJERESMLp0kgOmj8NprkJoK
+ffuaegoiIiLSBeoknIxRo+CZZ0xLwq5dTkcjIiLiHbp8S8IhP/4xZGebOgr/
++heEhDgdkYiIiLPUknCEn/0MLrgAZs2CujqnoxEREXGWkoRj/Pa3sG8fLFni
+dCQiIiLOUpJwDF9f+NvfTB+FV191OhoRERHnKEloRFQUvPgiXH89fPWV09GI
+iIg4Q0lCE84+GxYvhpkzobLS6WhEREQ8T0lCMxYsgPh4uOcepyMRERHxPCUJ
+zbAsePppeOUVeP11p6MRERHxLCUJJ9C7N6Snw3XXQW6u09GIiIh4jpKEkzBh
+Avz0pzB7tpleWkREpCtQknCS7rsPamrg0UedjkRERMQzlCScJB8f+POfYdky
++OILp6MRERFpf0oSWmDgQFOJ8ZprTKuCiHQu8fHxJCUlkZyczJj6KWGLi4uZ
+NGkSSUlJTJkyhZKSEoejFPEcJQkttGABBAbC//yP05GISFuzLIusrCw2b97M
+hg0bAFi0aBFTp05l69atpKamsmjRIoejFPEcJQkt5HKZks0rVsD27U5HIyJt
+zbbtox6vW7eOOXPmADB79mwyMjKcCEvEEUoSTkH//vDQQ7rtINLZWJbVcGth
+5cqVABQUFBAWFgZAeHg4+fn5ToYo4lHdnA6go5o3D154wYx2uOMOp6MRkbbw
+8ccfExkZSUFBARdddBGDBw9uwauzjvg9vn4R8bTs+qVtKEk4RZYFf/wjjB0L
+V1wBAwY4HZGItFZkZCQAERERTJ8+nU8++YSIiAgKCwsJDw+noKCgYZ3jpXgs
+TpGmxXN0gvp+q7am2w2tMGgQ3HmnKbR0zG1MEelgKioqqKioAGD//v1kZmaS
+kJBAWloa6enpAKSnp5OWluZkmCIeZdnH9tJxiGVZx3UY6giqq2HkSPjFL8yM
+kSKdQUc9H1tj165dXHbZZViWRUVFBTNnzmTp0qUUFxczY8YM9uzZQ3R0NGvX
+riUkJOSo11qWBWjUg3ijJa06l1udJGRmZnLXXXdRW1vLNddcwz3HTJmYlZXF
+pZdeymmnnQbAtGnT+OUvf3l8IB34orR+PVx2GezYYeZ6EOnoOvL56AQlCeK9
+WpcktKpPQmVlJQsWLODDDz8kKiqKcePGMXnyZJKTk49a77zzzuO1115rza68
+2tlnw/TpZkrpp55yOhoREZG20ao+CevXrychIYGYmBi6devGjBkzGh1D3BW+
+kTz4IGRkmFYFERGRzqBVSYLb7SYuLq7hcWxsLG63+6h1LMvi3//+N4mJiVxw
+wQVs2bKlNbv0WsHB8PDDcNNNmilSREQ6h1bdbjD34Zo3cuRI3G43/v7+vPXW
+W1x22WXs2rWr0XUXL17c8HtKSgopKSmtCc/jZs+GVatg9Wq44QanoxE5eVlZ
+WWRlZTkdhoh4mVYlCbGxseTk5DQ8zsnJOaplASAwMLDh98mTJ+Pn50deXh7R
+0dHHbe/IJKEjsixYuRKmTIFp06C+SJuI1zs2KV+yZIlzwYiI12jV7YbRo0ez
+fft2cnNzqa6uZu3ataSmph61TmFhYcPvGzduZP/+/c0UI+n4RoyAK6+ERgZw
+iIiIdCitaknw9/fnySefZMqUKdTV1TFnzhzOOussVq1aBcD8+fN54YUX+NOf
+/gSAn58fzz//PC5X567h9Otfw5AhcP31cNZZTkcjIiJyalRMqZ2sXg1PPw0f
+fWRmjhTpSDrb+djeVCdBvFfr6iTo46udzJ0LdXVQX81VRESkw1GS0E5cLvj9
+7+G++2D/fqejERERaTklCe1o3Dg491xYscLpSERERFpOSUI7e+ghePRRyM11
+OhIREZGWUZLQzuLjTWElDYkUEZGORkmCB9x7L2RmwqZNTkciIiJy8pQkeEBw
+MCxZArffDhpVJiIiHYWSBA+ZNw+KiuDVV52ORERE5OQoSfCQbt3gt7+Fu+6C
+qiqnoxERETkxJQkeNGkSnHEGPP6405GIiIicmJIED3vkEVi+HEpKnI5ERESk
+eUoSPGzoUPjxj+E3v3E6EhERkeYpSXDA4sWwapUKLImIiHdTkuCAuDi47joz
+LFJERMRbKUlwyM9/Dn//O3zxhdORiIiINE5JgkNCQ+Huu+EXv3A6EhERkcYp
+SXDQzTfDp5/Cv//tdCQiXce8efOIiooiMTGx4bni4mImTZpEUlISU6ZMoeSI
+4UfLly9n6NChJCYm8tZbbzkRsohjlCQ4yN8fli41LQoq1yziGXPnziUzM/Oo
+5xYtWsTUqVPZunUrqampLFq0CICNGzfy8ssvs23bNjIzM5k/fz5VqoYmXYiS
+BIfNmQN798LrrzsdiUjXMGHCBEJDQ496bt26dcyZMweA2bNnk5GRAUBGRgYz
+Z87Ex8eHmJgYEhIS2LBhg8djFnGKkgSH+fjAQw+Zjoy1tU5HI9I1FRQUEBYW
+BkB4eDj5+fkA5ObmEhsb27BebGwsbrfbkRhFnKAkwQtMnQphYfC//+t0JCJy
+6rKOWLIdjEO6tmyO/r/YOt1avQVpNcuChx+GGTPg6qtNXwUR8ZyIiAgKCwsJ
+Dw+noKCAyMhIwLQc5OTkNKzndruJi4trYisp7R+oyAnF1y+HvN+qraklwUuM
+GwcjRphKjCLiWWlpaaSnpwOQnp5OWlpaw/Nr1qyhpqYGt9vN9u3bGTNmjJOh
+iniUZdve0a/esiy8JBTHbN0KkyfDN99AYKDT0UhX1pnPx6uuuor333+fwsJC
+oqKiWLp0KZdeeikzZsxgz549REdHs3btWkJCQgBYtmwZ6enpuFwuVqxYwZQp
+U47bpmVZwCIPH0lH4Q+EnnAtqASK2zmWrmhJq85lJQle5uqrISEB7rvP6Uik
+K9P52DJKEo7kwtzJDgSi63/2OYnXVQC5wIH6nzZQ3U4xdiVKEjqVr782tx6+
++gp693Y6GumqdD62jJKEQ8IxrQZRQBAQDZY/uE6iJcGuhLpiTJLgBg7W/8xr
+t2i7htYlCeq46GVOPx2uuAIeeQSWL3c6GhGRk+GLSQoigNOBOPAJAL+eEABE
+nsQmDvjBniCoq4GqaLC/xbQm9MAkC2pVcIJaErxQTo7pxLhjB0RHOx2NdEU6
+H1uma7ck+AN9gTOAaPDpA739oLtl7jJ0x/z0OcFm9mAaEQ79LKiEuiKwS4Ft
+mBYF9VloObUkdDpxcXDNNfDgg/DYY05HIyLSlG7AMEwWcBp07wX9XBCDyR1i
+MI0McZhkoTnf1P/MBmqB77tDXl8o7YFpUYgGPgXK2vwopGlqSfBSBQUweDBs
+3Ajx8U5HI12NzseW6ZotCf5AEhADrkHQoycMBkIwXRMGArGYJOFQstCc7Pqf
+u4DdQAnwAyZ5KKmC2mzMbYdtwN42PZLOTS0JnVJEBNx0EyxZAs8+63Q0IiJH
+8sG0IESCzyCI6wm9McnBMEy/xQGYL//+Nt36VIBfXbNbrAkJhDrLJBl9gCJM
+A4I/sNsPsk+D6hrMx5ZaFDxFSYIXu+MO05Hx889hyBCnoxERgcN9EOJMC0J0
+T5McnIlpMRhU/7NPHVZgDb1j8on0z8fX1XzHw9yeMZQVhlIT5Icd5mtaECwO
+tzDUdIMfBsFBH0wG8j1qUWh/ShK8WK9ecOed8Ktfwd/+5nQ0IiJgkoTTwTUQ
+uvU0rQbDgDBgOBACrqQD9AzeR1hwEX3ZDdDwszGFhBMYUE5RvzDKqoPZ0zuK
+um49TauCxeFPqmI/qB4ItTWY+xElmOYGaS+tThIyMzO56667qK2t5ZprruGe
+e+45bp2FCxfyzjvv0L17d1avXk1ycnJrd9tl3HwzDBpk+iaMHOl0NCLStfXG
+fIs/Dfy7m9aDfpiRjyPMn7rH7aNnaBn9e35HOIX0pogeHCCWpmfPLCCSCgII
+oYRC33DsXhYVw3pS1jMc/F0mDzgIVAFfuKAkBuyB9U/80N4H3aW1KkmorKxk
+wYIFfPjhh0RFRTFu3DgmT558VBLw0ksv8f3337Njxw42b97M3Llz+eyzz1od
+eFcREAC//KVZ3njD6WhEpGsLAQZBtwjo6zIPT+NwC0K/CqJjcoliD3HkEEk+
+QZQRTiF9m/kwD6eIMoIIIYIAKgjwryDbP54afKjYH2aSERsoBQpdUBsCZYMw
+QyLzUGtC+2lVkrB+/XoSEhKIiYkBYMaMGWRkZByVJKxbt445c+YAkJyc3DBR
+ypFztEvzrrvOFFf64AOYONHpaESka+oFDAKrFwRbpkBSf8wIhhCwhhykT/Ru
+IsnnDL4iiDL68T3R7CGUvYSylx4cOG6rtfjQi1IqCCCACrpTSSDlALhC68g+
+rQd1tj8Uuczkhvsw0zyU94S6/pgejjnHbVfaRquShGOnTY2NjSUrK+uE6yhJ
+aBk/PzPK4Re/gH/+00wtLSLiCKsX9KpPEgZhaiiFQ8/e++jrs5t+fE8g+xjC
+F4Syl77sJoICguvKCK8uPG5zB13+hPiWsJu+AIRQwudY7CMIgIrIAPYU9cMe
+BtRg+ioWAbk9OfG4SmmtViUJ1kl+Wh07RrOp1y1evLjh95SUFFJSUk41tE5n
+1ix46CFzy6F+FluRNpOVlXVcgi9ytEFm6dXTTM8QgmlcCABXbAWxvdz0opQI
+8unPd0RQQD++J8reQ9+9+fjuq6Vb4fG3BXr4VxAU+T0BYRWEuErYxQDi6vsv
+VNKdGN9cyuKCqdgXZvYZWr/fmG6Q0x/sYkzmUO6h96FraVWSEBsbS07O4Wae
+nJyco1oNjlzn7LPPBmi2FeHIJEGO5uMDDzxgZoe86CJwuZyOSDqTY5PyJUuW
+OBeMeL9emD6MoWBF1hAd9QN92U0vSgmmjCjy6cf39KaYmNI8/Ivq4Gug4PhN
+ufzBb5hN38pCavv64HLVUYsPZQQRXF8LoSwkmJ2xPakr8jejKEKB7zx3uF1Z
+qz5qRo8ezfbt28nNzaW6upq1a9eSmpp61DppaWn85S9/AWDTpk34+Pg09GGQ
+lrn8cpMsaDikiHjWAOB06BZu+gWE0NCS4Bt0kCDXPgAiyCcWNzHkEmTvo19x
+Lv7f1cEXmCThP8Bnxyx5wHbo9r1N7J49BNeVEcUeYthNX3KxsAmwKggILIde
+NgSb/RIPBAXR0MIh7aJVLQn+/v48+eSTTJkyhbq6OubMmcNZZ53FqlWrAJg/
+fz7Tpk3jvffeIyEhge7du/OsygeeMsuCZcvgZz+DadOgm6pciIgTQmhoSege
+UkEvVynR/EAMufQhj27U0Msuxb+sBnKB7Zg7AjswoxaPVIUpd2CBby+bkPAS
+fnD1JZo8SgmmlBD2EURQcBn7Q0Oxe/uYfaslwSNa/TGTmpp6XOvB/Pnzj3q8
+cuXK1u5G6k2aBH37wp//bEY9iIh4Axc2LuqwsIkkn6jSQlw5mCShFPgSKIaD
+uw6/xi8aXD6YgkkRwPfQK/ggfWNy2ecKrN9e8+WcpX3pu2gHY1mwfDlceSXM
+ng3+/k5HJCLSjCpMOYNCKNsBX+07/KeYYjNNA73RVAxeSt3fOqCxY031xSee
+cDoSEZGTZINda3KBjZjKBnW1oIYC76YkoYN64AEzJLJM2beIdDBqAO04lCR0
+UMOGmaGQK1Y4HYmIyEnwAd9AiLZgtAVnWuAfgDIGL6c+CR3YkiUwapSZBCoi
+wuloREQa0QMzs3QxBLjgzIDDf3JFYfoj9MVMNy1eRy0JHdiAAXDVVWZYpIiI
+kw7iTwUBHMSfPUSxp1c4dXEcrqtwOhAOPsMOL1YEZvKmMBrmgiiN9me3K4YD
+9KjfXg/q9FHlGLUkdHC//CUkJMBtt0G/fk5HIyJdQh7gBiKhIqIX+T0jCfQp
+p4gwigklggLyXZGERpYSVFoFQ4EAoDvH10mIB6KBcKiMtsjrFk0VvuwllGJ6
+s5dQCgmnqCACe7fLDKlsetZpaWNKEjq46GiYP9/celi92uloRKRz2gX0hpre
+kB1pWgaqzFJ3oBv7KoOoDOhOPlEEU4YfVfhSjTugLzEDfyAosBLLB1Mp8Vj+
+wCA4GO5DbmgU+6wg3MSSS192E8NB/CmvDqS6vDtUWVCNWbKBffuAb+oXaQ9q
+w+kE7r4bXnsNvvjC6UhEvN+8efOIiooiMTGx4bnFixcTGxtLcnIyycnJvPHG
+Gw1/W758OUOHDiUxMZG33nrLiZC9zx7MN/ofwM7zYe/e3hQRRiHhFBBBEWH8
+QB/yiCYvMJLyaD+qhlkwjOOWumFwIM6FOyKaAiuCXQygmN7kE0kBERTTm8Ly
+cOw9fvADphVht3OH3tWoJaETCAmBO++E++/XvA4iJzJ37lx+9rOf8V//9V8N
+z1mWxe23387tt99+1LobN27k5ZdfZtu2beTl5TF+/Hi+/PJL/Pz8PB22F/gG
+CIPSnhDSEyowpZb3QV1OAN8H9MMvpIoiK5zuVOJLNQfoQRV+7OsZRFCPfYT1
+Ljpuqwctfwq7meTiB/qwj0C+ox+5xFJOILsqBnAgJ8TsrwRTvbEKyK0B+zvM
+pBCaAbK9KEnoJH72Mzj9dPj0UzPiQUQaN2HCBLKzs497/tgp7QEyMjKYOXNm
+w8R0CQkJbNiwgfHjx3sgUi9VtwfyB0CedXhGRgsO9A6iKDgMfKA7lVTRnYF8
+Qw3dKKUXka588l2Rx23OxqKQcMoJZA9RfEc/igg3rRBEU1LUGzvPZSaH2o1p
+xfgBqNsLHPTkkXdJShI6iYAA04nxF78AtYiKtNzjjz/O008/zciRI/nDH/5A
+7969yc3N5Uc/+lHDOrGxsbjdXbXXXCnmW7s/HKyC/O7QEzPEMRTsr/3Y3S0O
+u5+Fq1sdceQAkE8ZYRRRTmCTWy4gnDKCKSScH+hLIeG47Vjy90RRt6uHSQz2
+ADsxpRr314JdhpnlKad9D7uLU5LQiVx7LfzP/8B778H55zsdjUjHcdNNN/Gr
+X/0KMP0TFi5cSHp6egu3knXE7/H1S2eTB/SG2hj4IRgCephOjJ8BI8AO7U5+
+XV9KwkKoCe1GGcFEkk8BkZQR3ORWC4ngIP4UEcZeQtm5byAH9vekMifY7HIH
+phUhD8irhYoyTKeI3PY/5A4nu35pG0oSOhE/P1i6FO69F/79bzMZlIicWHj4
+4Uo+8+fP5/z6LDs2NpacnMPfVN1uN3FxcU1sJaUdI/QWBzCtCT2hegR83cN8
+ivQAtpo1agp7UJvgYmfNIIpCwij3DcSXavYR1ORWS+nFfnpSWBNOeWUQpXtD
+qfuqh0kKPsf8/Br4CtOKwc76J4rb8Vg7qniOTlDfb9XWlCR0MlddBQ8/bEY7
+XHqp09GIdAz5+flERpr75S+99BIJCQkApKWlceONN3LrrbeSl5fH9u3bGTNm
+jJOheoEKIAfsXlBrwZ5QsDG1EGzgdLBrunOgnx8H84IpiwsmKmgPe31Cm92q
+uySWg/lB1Fb4Qp7L5AC7Mf0PPsfcbjh4AOq+NvunsD0PUuopSehkXC548EHT
+mnDxxeDj43REIt7lqquu4v3336ewsJC4uDiWLFnCe++9x9atW6mqqqJ///6s
+ri86MnLkSC6//HKSkpJwuVysWrUKX19fh4/AaTWYugk9wa6GgjPBNxS2A0WY
+WR33Avst7Bgf9peEs6tvEJZf89M91mX3ML9kY1oO9gLbMLlAHpB7EGq/AvIx
+TQoH2uHY5FiW3ViXXgdYltVo72JpOduG8eNNkaUjRnmJnDSdjy1jWRawyOkw
+PMwXU1N5AFinQfdQOA3TRyHCPE0c4IeZm+FEo0az63/uxLQelGCSg51AxaEW
+hFzMfQ0lCCdvSavOZbUkdEKWBb/5DVx9NVx5JfTo4XREItL5VGOaDwC7Ag6e
+AV/2htj6lpYSzJd+Xw4nC83ZWf8z+4ifhXWwvxgziqEYJQiepyShkzr3XBg5
+Ev7wB7jnHqejEZHO60tgP1ADtVHwfT8oDoAeLnPLwB/z+X7w0aOvAAAS+UlE
+QVSiW5/FQBnmNaVAaTnY+fVPfA18jxIEz9Pthk7sq6/gnHPg8881lbS0jM7H
+lumatxuO1RMzU9NAoB9YgeATbEY+hJ3EDAAHbSi0wa6C2lJMvwc3hzslyKlp
+3e0GJQmd3M03m86Mf/iD05FIR6LzsWWUJBwSiJnFKQoIAqLBCgAr7MQvtQ+C
+XYBpLXBjqim6Mc0LcuqUJEgzCgpgyBBTN+H0052ORjoKnY8toyShMT0xPRjr
+k4UTqsB0TDyIWg7akjouSjMiIuD2282QyBdfdDoaEek69tcvvpj+BCdSU7++
+eBMlCV3ArbfC4MHwr3+ZPgoiIp5TjemJKB3RSfQmkY4uIAB+/WsznbRakEVE
+5GQpSegiZs+Gigp4+WWnI5GmlJQ4HYGIyNGUJHQRPj7wyCOmZkJVldPRyLGq
+q2HcOFi/3ulIREQOU5LQhUyaBIMGwZNPOh2JHOuPf4S4OOjycweJiFfREMgu
+ZscOOP98+M9/4IjZccVBxcWmY+m778KwYU5HY+h8bBkNgRTv1bohkGpJ6GIS
+EmDGDPjVr5yORA5ZuhSuuMJ7EgQRkUPUktAFFRebAktvvw1JSU5H07V98YWZ
+sfM//4HISKejOUznY8uoJUG8l1oSpIV694ZFi+CWWzQk0km2DQsXwn33eVeC
+ICJyiJKELuqGG6CoSEMinfT3v0NurplfQ0TEG51yxcXi4mJmzJjBnj176NOn
+D2vWrCEkJOS49eLj4wkODsbHxwdfX182bNjQqoClbXTrBo8+CvPmQVoa9Ojh
+dERdy4EDplz200+Dr6/T0YiINO6UWxIWLVrE1KlT2bp1K6mpqSxa1Pj9OMuy
+yMrKYvPmzUoQvMz558PIkbBihdORdD2/+Q2MGgUXXOB0JCIiTTvljosDBw5k
+w4YNhIWFUVhYyNixY/nmm2+OW2/AgAF8+umnhIU1P1WoOko5Y9cu82H12Wdm
+nL60v+xsk5xt3gz9+jkdTeN0PraMOi6K93Ko42JBQUHDB394eDj5+fmNrmdZ
+FpMmTSIpKYmVK1ee6u6knQwYYDrP3XKL05F0HXfcYSbd8tYEQUTkkGb7JEya
+NIm8vOPn9X7wwQdPegcff/wxkZGRFBQUcNFFFzF48GAuvPDCRtddvHhxw+8p
+KSmkpKSc9H7k1N1zjxkK+frrcPHFTkfTuf3jH6YF4S9/cTqSo2VlZZGVleV0
+GCLiZVp1u2H9+vWEh4dTUFDAuHHjGr3dcKTly5cDcO+99x4fiJo3HfWPf8D1
+15uKjAEBTkfTOR08CMOHmzk0LrnE6Wiap/OxZXS7QbyXQ7cb0tLSSE9PByA9
+PZ20tLTj1qmoqKCiogKA/fv3k5mZSUJCwqnuUtrRhRfC2LHwwANOR9J5PfQQ
+DB3q/QmCiMghp9yScOQQyOjoaNauXUtISAi7d+/m+uuvJyMjg2+//ZbLL78c
+y7KoqKhg5syZLF26tPFA9M3FcT/8YG47vP+++TCTtvPll3DuueZWQ0foIKrz
+sWXUkiDeq3UtCSrLLEdZuRJefBHeew8sy+loOgfbhh/9CC67rON0EO2s52NO
+Tg6zZs1i7969VFVVce2113L33Xc3W/dl+fLlPPfcc/j4+LBixQomT5583HaV
+JIj3UllmaUMLFkB5Ofzf/zkdSefxf/8H+/apsqI38PPz44knnmDbtm1s3LiR
+p59+mi1btjRZ92Xjxo28/PLLbNu2jczMTObPn09VVZXDRyHiOUoS5Cg+PrBq
+Fdx9N+zZ43Q0HV9hoXkvV60y7604KyoqimH1020GBgaSlJREbm4u69atY86c
+OQDMnj2bjIwMADIyMpg5cyY+Pj7ExMSQkJCgonDSpShJkOOMHGnKNeubb+vd
+eitcfbV5T8W7ZGdn88knnzB+/Pgm677k5uYSGxvb8JrY2Fjcbrcj8Yo44ZTn
+bpDObdEiGDHC9E+YPt3paDqmV1+F9ethyxanI5FjlZeXM336dB599FGCg4Pb
+aKtZR/weX7+IeFp2/dI2lCRIo/z94ZlnTIJw/vlwgqracoziYtO/Y80a1Z3w
+NtXV1UybNo1Zs2Zx2WWXARAREUFhYWFD3ZfI+rm7Y2NjycnJaXit2+0mrsnh
+KSntHLnIyYjn6AT1/VZtTbcbpEnnnAMzZpgmc2mZhQvhJz+BCROcjkSOZNs2
+1157LUOHDuW2225reL6pui9paWmsWbOGmpoa3G4327dvZ8yYMY7ELuIEDYGU
+Zu3fb2on/P738OMfOx1Nx/Dqq2Z+hi1boGdPp6M5NZ31fPzwww+ZOHEiSUlJ
+9cMWzRDHMWPGNFr3BWDZsmWkp6fjcrlYsWIFU6ZMOW67GgIp3kt1EqSdffAB
+zJxpZoqsb4WVJhQVQWIi/PWvMHGi09GcOp2PLaMkQbyX6iRIO5s4Ea65xox4
+0OdG02zbzH8xc2bHThBERA5RkiAnZckSU7b5j390OhLv9dRT8O23UD+PmYhI
+h6fRDXJS/PzM9Mbjx5vRDoMHOx2Rd/niC7jvPnNrpnt3p6MREWkbakmQkzZ4
+sJklctYsUGXawyor4aqr4MEHYcgQp6MREWk7ShKkRebPh9hYuOcepyPxHr/4
+BQwYYPojiIh0JrrdIC1iWfDss6bM8PjxMG2a0xE567XX4G9/M1NAa9ZMEels
+1JIgLda7t/lgvPFG+Pprp6NxzjffwHXXwdq1qkgpIp2TkgQ5JaNGwdKlpmxz
+RYXT0XheRQVccQUsXgxjxzodjYhI+1CSIKfsxhtN4aDrruta9RNs2/TNGD7c
+zM8gItJZKUmQU2ZZpjbA1193rdoAK1bAtm2wapX6IYhI56aOi9IqPXqYuQrG
+jDHD/y6/3OmI2tff/w6/+x18/LFmdxSRzk9JgrRa377wyiuQlmaGAo4Y4XRE
+7WPTJjPMcd06aHK2YBGRTkS3G6RNjB4NTzwBU6ea0sSdTU4OXHopPPmkOVYR
+ka5ALQnSZq68EvLzYcoU+OijzjNjZH4+TJoEt91mRnOIiHQVShKkTd10E+zZ
+A6mp8N57EBzsdEStU1oKF11kEqDbb3c6GhERz9LtBmlzS5aYjoxpaVBW5nQ0
+p66iAi65BM45x9SEEBHpapQkSJuzLHj8cRg2zHwLLy11OqKWKyszsQ8YAH/4
+g4Y6ikjXpCRB2oXLZToyjhhh+iiUlDgd0ckrLjZ9EBIS4JlnzLGIiHRFuvxJ
+u3G5TIvC2WfDeeeB2+10RCeWlwc/+hGce65JcpQgiEhXpkugtCvLgt//HmbN
+gnHjYMsWpyNq2tatJqGZNs1UVdQtBhHp6jS6QdqdZcHdd0P//qYZ/89/Np0a
+vcn/+38wbx6sXAkzZjgdjYiId1CSIB4zY4apVDhjBsyeDb/+NXRz+H9gbS08
+8AD86U/w+uumJUFERAzdbhCPOuccU95482ZISTGVDJ3idsMFF8AHH8AnnyhB
+EBE5lpIE8biICDP/wcUXw1lnmVLHdXWe239dHfzxj5CcbG5/vPWWmX9CRESO
+piRBHOFywc9/Du+/D889B+PHm5kV29vHH8OECfC//wtZWXDffeDj0/77Fe+Q
+k5PDxIkTSUxM5Mwzz+Q3v/kNAIsXLyY2Npbk5GSSk5N54403Gl6zfPlyhg4d
+SmJiIm+99ZZToYs44pSThL/97W8kJCTg4+PDpk2bmlwvMzOTxMREhg4dysMP
+P3yqu3NMVlaW0yE0qrPENXQofPihmV3xyivNyIJm/judsqeeymL6dLOPa681
++0xIaPv9tJS3/jt2Vn5+fjzxxBNs27aNjRs38vTTT7NlyxYsy+L2229n8+bN
+bN68mdTUVAA2btzIyy+/zLZt28jMzGT+/PlUVVU5fBTNyXY6ABTDkbKdDqDV
+TjlJSExM5JVXXmHixIlNrlNZWcmCBQvIzMxk69atvPjii2zevPlUd+kIb72I
+d6a4XC6YOxe++srUJ7j0Ujj/fFizxpRGPlUVFWYbP/oR3HFHFmPHwpdfmlEM
+3tJ64K3/jp1VVFQUw4YNAyAwMJCkpCRyc3MBsG37uPUzMjKYOXMmPj4+xMTE
+kJCQwIYNGzwac8tkOx0AiuFI2U4H0GqnnCQMHjyYM844o9l11q9fT0JCAjEx
+MXTr1o0ZM2aQkZFxqruUTq5HDzOJ0rffwg03wLPPmr4CV15pChtt3w7NfYmr
+qoIdO0wfh5/8xLz2mWfguuvgllvgzjshIMBzxyPeLTs7m08++YQJEyYA8Pjj
+jzNkyBBmz55NcXExALm5ucTGxja8JjY2FndHqAom0kbatU+C2+0mLi6u4bFO
+MDkZvr5w1VWQmWm++V98sRl9cMUVZlbJQYNg7FjT6XDyZFOkadAg87fLL4f1
+600dhi+/hDffhKuv9p6WA/EO5eXlXHnllTz66KMEBQVx0003sXPnTv7zn/8w
+cOBAFi5ceApbzTpiyW67YEVaJJuj/y+2kt2MCy+80B42bNhxy2uvvdawTkpK
+ir1x48ZGX//888/bN954Y8PjF154wZ4/f36j6w4cONAGtGjR4gXLwIEDm7s0
+dGhVVVX25MmT7d/+9reN/j03N9c+44wzbNu27aVLl9qPPPJIw9+mTp1qf/jh
+h8e9Zvjw4Y7/m2nR0tgyfPjwVp0vzZayefvtt5v78wnFxsaSc8RA+JycnKNa
+Fo70zTfftGpfIiInYts21157LUOHDuW2225reD4/P5/IyEgAXnrpJRLqe7Wm
+paVx4403cuutt5KXl8f27dsZM2bMcdv97LPPPHMAIh7WJvXu7EY6/ACMHj2a
+7du3k5ubS2RkJGvXrmXVqlVtsUsRkRb76KOPSE9PJykpieTkZACWLVvG888/
+z9atW6mqqqJ///6sXr0agJEjR3L55ZeTlJSEy+Vi1apV+Pr6OnkIIh5l2U19
+wp/AK6+8wsKFCyksLKRXr14NY4t3797N9ddf39BB8Y033uCuu+6irq6OOXPm
+cO+997bpAYiIiEj7OOUkQURERDo3xysuelOxpfj4+IZmyEP3HYuLi5k0aRJJ
+SUlMmTKFkpKSdo9j3rx5REVFkZiY2PBcc3F4qiJcY3F5Q6W6pqroOf2eeWt1
+v4MHDzJ69GiSk5M544wzGu7NO/1+dVROXcOcuF55y7XJG65F3nDd8cg1plXd
+Hlvp4MGDdnx8vO12u+3q6mp71KhR9qZNmxyLJz4+3i4qKjrquZtvvtn+3e9+
+Z9u2bf/ud7+zFy5c2O5xfPDBB/amTZvsYcOGnTCOTz/91B41apRdU1Nju91u
+Oz4+3q6srPRYXIsXL7ZXrFhx3LqejCsvL8/etm2bbdu2vW/fPvv000+3P/vs
+M8ffs6bi8ob3rKKiwrZt266urrbPPvts+91333X8/eqInLyGOXG98pZrkzdc
+i7zhuuOJa4yjLQneWGzJPubuy7p165gzZw4As2fP9kh8EyZMIDQ09KTi8GRF
+uMbigsY7rnoyrqaq6Dn9nnlzdb8ePXoAUFVVRW1tLZGRkY6/Xx2R09cwT1+v
+vOXa5A3XIm+47njiGuNokuBtxZYsy2poJlq5ciUABQUFhIWFARAeHk5+fr4j
+sTUVhzdUhPOmSnWHquiNHz/eq94zb6vuV1dXx4gRI4iKiuL8888nISHBq96v
+jsLJa5i3XK+86f+NU+eVN1x32usa42iSYFmWk7s/zscff8ymTZt45513ePbZ
+Z/nHP/7hdEher20q1bWN8vJypk+fzqOPPkpwcLBjcRyrfar7tY7L5eKzzz7D
+7XbzwQcf8N5773k8hs7AyWuYrldHc+q88obrTnteYxxNElpSbMkTDhVTiYiI
+YPr06XzyySdERERQWFgImIz50Dqe1lQcx76Hx36zaW/h4eFYloVlWcyfP59P
+PvnEkbiqq6uZNm0as2bN4rLLLgO84z07FNfVV1/dEJe3vGcAvXr1YurUqaxf
+v94r3q+OxslrmLdcr7zl/40T55U3XHfa+xrjaJJwZLGl6upq1q5d2zBFq6dV
+VFRQUT/l4P79+8nMzCQhIYG0tDTS09MBSE9PJy0tzZH4moojLS2NNWvWUFNT
+g9vtbrIiXHs5sjnz2Ep1norLbqKKntPvWVNxOf2eFRUVsW/fPgAOHDjA22+/
+TWJiouPvV0fk1DXMm65X3vL/xtPnlTdcdzxyjWlV18o2sG7dOjshIcEeMmSI
+vWzZMsfi+Pbbb+2kpCR7+PDh9umnn27ff//9tm3bdlFRkX3hhRfaiYmJ9qRJ
+k+y9e/e2eywzZ860+/TpY/v6+tqxsbH2M88802wcDz74oD1kyBA7ISHBzszM
+9Fhcq1evtmfPnm0nJSXZgwcPtqdMmWK73W6Px/XPf/7TtizLHj58uD1ixAh7
+xIgR9htvvOH4e9ZYXOvWrXP8Pdu6das9YsQIe/jw4faZZ55pL1myxLbt5v+v
+e+rfsiNy4hrm1PXKW65N3nAt8obrjieuMSqmJCIiIo1yvJiSiIiIeCclCSIi
+ItIoJQkiIiLSKCUJIiIi0iglCSIiItIoJQkiIiLSKCUJIiIi0qj/D8+9rBqN
+ky3xAAAAAElFTkSuQmCC
+" /></p>
+<p style=" margin:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#000080;">ITango [</span><span style=" font-weight:600; color:#000080;">4</span><span style=" color:#000080;">]:</span> </p></body></html>
diff --git a/doc/_static/itango.ico b/doc/_static/itango.ico
new file mode 100644
index 0000000..eb8e2dc
Binary files /dev/null and b/doc/_static/itango.ico differ
diff --git a/doc/_static/itango.png b/doc/_static/itango.png
new file mode 100644
index 0000000..b4cd925
Binary files /dev/null and b/doc/_static/itango.png differ
diff --git a/doc/itango/itango00.png b/doc/_static/itango00.png
similarity index 100%
rename from doc/itango/itango00.png
rename to doc/_static/itango00.png
diff --git a/doc/itango/itango01.png b/doc/_static/itango01.png
similarity index 100%
rename from doc/itango/itango01.png
rename to doc/_static/itango01.png
diff --git a/doc/itango/itango02.png b/doc/_static/itango02.png
similarity index 100%
rename from doc/itango/itango02.png
rename to doc/_static/itango02.png
diff --git a/doc/itango/itango03.png b/doc/_static/itango03.png
similarity index 100%
rename from doc/itango/itango03.png
rename to doc/_static/itango03.png
diff --git a/doc/itango/itango04.png b/doc/_static/itango04.png
similarity index 100%
rename from doc/itango/itango04.png
rename to doc/_static/itango04.png
diff --git a/doc/itango/itango05.png b/doc/_static/itango05.png
similarity index 100%
rename from doc/itango/itango05.png
rename to doc/_static/itango05.png
diff --git a/doc/itango/itango06.png b/doc/_static/itango06.png
similarity index 100%
rename from doc/itango/itango06.png
rename to doc/_static/itango06.png
diff --git a/doc/_static/jive_powersupply.png b/doc/_static/jive_powersupply.png
new file mode 100644
index 0000000..f3cdc79
Binary files /dev/null and b/doc/_static/jive_powersupply.png differ
diff --git a/doc/_static/jquery-1.9.1.min.js b/doc/_static/jquery-1.9.1.min.js
new file mode 100644
index 0000000..bc1e707
--- /dev/null
+++ b/doc/_static/jquery-1.9.1.min.js
@@ -0,0 +1,20 @@
+
+/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license
+//@ sourceMappingURL=jquery.min.map
+*/
+(function (e, t) {
+ var n, r, i = typeof t, o = e.document, a = e.location, s = e.jQuery, u = e.$, l = {}, c = [], p = "1.9.1", f = c.concat, d = c.push, h = c.slice, g = c.indexOf, m = l.toString, y = l.hasOwnProperty, v = p.trim, b = function (e, t) { return new b.fn.init(e, t, r) }, x = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, w = /\S+/g, T = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, N = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/, C = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, k = /^[\],:{}\s]*$/, E = /(?:^|:|,)(?:\s*\[ [...]
+ b.event.special[e] = { delegateType: t, bindType: t, handle: function (e) {
+ var n, r = this, i = e.relatedTarget, o = e.handleObj;
+ return (!i || i !== r && !b.contains(r, i)) && (e.type = o.origType, n = o.handler.apply(this, arguments), e.type = t), n
+ }
+ }
+ }), b.support.submitBubbles || (b.event.special.submit = { setup: function () { return b.nodeName(this, "form") ? !1 : (b.event.add(this, "click._submit keypress._submit", function (e) { var n = e.target, r = b.nodeName(n, "input") || b.nodeName(n, "button") ? n.form : t; r && !b._data(r, "submitBubbles") && (b.event.add(r, "submit._submit", function (e) { e._submit_bubble = !0 }), b._data(r, "submitBubbles", !0)) }), t) }, postDispatch: function (e) { e._submit_bubble && (delete e._ [...]
+ var i, o, a, s, u, l, c, p = e.length, f = dt(t), d = [], h = 0; for (; p > h; h++) if (o = e[h], o || 0 === o) if ("object" === b.type(o)) b.merge(d, o.nodeType ? [o] : o); else if (wt.test(o)) {
+ s = s || f.appendChild(t.createElement("div")), u = (bt.exec(o) || ["", ""])[1].toLowerCase(), c = At[u] || At._default, s.innerHTML = c[1] + o.replace(vt, "<$1></$2>") + c[2], i = c[0]; while (i--) s = s.lastChild; if (!b.support.leadingWhitespace && yt.test(o) && d.push(t.createTextNode(yt.exec(o)[0])), !b.support.tbody) {
+ o = "table" !== u || xt.test(o) ? "<table>" !== c[1] || xt.test(o) ? 0 : s : s.firstChild, i = o && o.childNodes.length; while (i--) b.nodeName(l = o.childNodes[i], "tbody") && !l.childNodes.length && o.removeChild(l)
+ } b.merge(d, s.childNodes), s.textContent = ""; while (s.firstChild) s.removeChild(s.firstChild); s = f.lastChild
+ } else d.push(t.createTextNode(o)); s && f.removeChild(s), b.support.appendChecked || b.grep(Ot(d, "input"), Bt), h = 0; while (o = d[h++]) if ((!r || -1 === b.inArray(o, r)) && (a = b.contains(o.ownerDocument, o), s = Ot(f.appendChild(o), "script"), a && Mt(s), n)) { i = 0; while (o = s[i++]) kt.test(o.type || "") && n.push(o) } return s = null, f
+ }, cleanData: function (e, t) { var n, r, o, a, s = 0, u = b.expando, l = b.cache, p = b.support.deleteExpando, f = b.event.special; for (; null != (n = e[s]); s++) if ((t || b.acceptData(n)) && (o = n[u], a = o && l[o])) { if (a.events) for (r in a.events) f[r] ? b.event.remove(n, r) : b.removeEvent(n, r, a.handle); l[o] && (delete l[o], p ? delete n[u] : typeof n.removeAttribute !== i ? n.removeAttribute(u) : n[u] = null, c.push(o)) } }
+ }); var Pt, Rt, Wt, $t = /alpha\([^)]*\)/i, It = /opacity\s*=\s*([^)]*)/, zt = /^(top|right|bottom|left)$/, Xt = /^(none|table(?!-c[ea]).+)/, Ut = /^margin/, Vt = RegExp("^(" + x + ")(.*)$", "i"), Yt = RegExp("^(" + x + ")(?!px)[a-z%]+$", "i"), Jt = RegExp("^([+-])=(" + x + ")", "i"), Gt = { BODY: "block" }, Qt = { position: "absolute", visibility: "hidden", display: "block" }, Kt = { letterSpacing: 0, fontWeight: 400 }, Zt = ["Top", "Right", "Bottom", "Left"], en = ["Webkit", "O", " [...]
+})(window);
\ No newline at end of file
diff --git a/doc/_static/jssor.css b/doc/_static/jssor.css
new file mode 100644
index 0000000..908ed85
--- /dev/null
+++ b/doc/_static/jssor.css
@@ -0,0 +1,23 @@
+ /* jssor slider arrow navigator skin 03 css */
+/*
+.jssora03l (normal)
+.jssora03r (normal)
+.jssora03l:hover (normal mouseover)
+.jssora03r:hover (normal mouseover)
+.jssora03ldn (mousedown)
+.jssora03rdn (mousedown)
+*/
+.jssora03l, .jssora03r, .jssora03ldn, .jssora03rdn
+{
+ position: absolute;
+ cursor: pointer;
+ display: block;
+ background: url(../img/a03.png) no-repeat;
+ overflow:hidden;
+}
+.jssora03l { background-position: -3px -33px; }
+.jssora03r { background-position: -63px -33px; }
+.jssora03l:hover { background-position: -123px -33px; }
+.jssora03r:hover { background-position: -183px -33px; }
+.jssora03ldn { background-position: -243px -33px; }
+.jssora03rdn { background-position: -303px -33px; }
\ No newline at end of file
diff --git a/doc/_static/jssor.slider.mini.js b/doc/_static/jssor.slider.mini.js
new file mode 100644
index 0000000..b69c55a
--- /dev/null
+++ b/doc/_static/jssor.slider.mini.js
@@ -0,0 +1,2 @@
+(function(g,f,b,d,c,e,z){/*! Jssor */
+$Jssor$=g.$Jssor$=g.$Jssor$||{};new(function(){});var m=function(){var b=this,a={};b.$On=b.addEventListener=function(b,c){if(typeof c!="function")return;if(!a[b])a[b]=[];a[b].push(c)};b.$Off=b.removeEventListener=function(e,d){var b=a[e];if(typeof d!="function")return;else if(!b)return;for(var c=0;c<b.length;c++)if(d==b[c]){b.splice(c,1);return}};b.g=function(e){var c=a[e],d=[];if(!c)return;for(var b=1;b<arguments.length;b++)d.push(arguments[b]);for(var b=0;b<c.length;b++)try{c[b].apply( [...]
\ No newline at end of file
diff --git a/doc/_static/motor.png b/doc/_static/motor.png
new file mode 100644
index 0000000..abc5f20
Binary files /dev/null and b/doc/_static/motor.png differ
diff --git a/doc/_static/serial.png b/doc/_static/serial.png
new file mode 100644
index 0000000..a7eef8d
Binary files /dev/null and b/doc/_static/serial.png differ
diff --git a/doc/_static/starter.png b/doc/_static/starter.png
new file mode 100644
index 0000000..2095e90
Binary files /dev/null and b/doc/_static/starter.png differ
diff --git a/doc/_templates/index.html b/doc/_templates/index.html
index 8d53c05..12281d3 100644
--- a/doc/_templates/index.html
+++ b/doc/_templates/index.html
@@ -1,10 +1,52 @@
{% extends "layout.html" %}
{% set title = 'PyTango documentation' %}
-{% set script_files = script_files + ["_static/slideshow.js"] %}
+{% set script_files = script_files + ["_static/jquery-1.9.1.min.js", "_static/jssor.slider.mini.js"] %}
{% block body %}
+<script>
+ jQuery(document).ready(function ($) {
+
+ var _SlideshowTransitions = [
+ //Fade
+ { $Duration: 1200, $Opacity: 2 }
+ ];
+
+ var options = {
+ $AutoPlay: true,
+ $AutoPlaySteps: 1,
+ $AutoPlayInterval: 3000,
+ $PauseOnHover: 1,
+ $ArrowKeyNavigation: true,
+ $SlideDuration: 500,
+ $MinDragOffsetToSlide: 20,
+ $SlideSpacing: 0,
+ $DisplayPieces: 1,
+ $ParkingPosition: 0,
+ $UISearchMode: 1,
+ $PlayOrientation: 1,
+ $DragOrientation: 3,
+
+ $SlideshowOptions: {
+ $Class: $JssorSlideshowRunner$,
+ $Transitions: _SlideshowTransitions,
+ $TransitionsOrder: 1,
+ $ShowLink: false
+ },
+
+ $ArrowNavigatorOptions: {
+ $Class: $JssorArrowNavigator$,
+ $ChanceToShow: 1,
+ $AutoCenter: 0,
+ $Steps: 1
+ }
+ };
+
+ var jssor_gallery = new $JssorSlider$("gallery", options);
+ });
+</script>
+
<h1>Welcome to PyTango documentation!</h1>
<p>
@@ -15,20 +57,24 @@
of this in pure python.
</p>
-<div id="gallery" style="width:780px; height:525px; margin:auto;">
+<div id="gallery" style="position:relative; top:0px; left:0px; width:610px; height:415px; margin:auto;">
- <!-- 1st snap -->
- <img src="_static/banner1.png" />
- <!-- 2nd snap -->
- <img src="_static/banner2.png" />
- <!-- 3rd snap -->
- <img src="_static/banner3.png" />
+ <div u="slides" style="cursor: move; position:absolute; top:0px; left:0px; width: 610px; height: 415px; overflow: hidden;">
- <!-- 4th snap -->
- <table style="width:100%;"><tr>
- <td>
+ <div style="background:white;"><img src="_static/banner1.png"/></div>
+
+ <div style="background:white;"><iframe src="_static/ipython_tango.html" style="width:600px; height:411px;border: 0;"></iframe></div>
+
+ <div style="background:white;"><iframe src="_static/ipython_db.html" style="width:600px; height:411px;border:0;"></iframe></div>
+
+ <div style="background:white;"><iframe src="_static/ipython_motor.html" style="width:600px; height:411px;border:0;"></iframe></div>
-<div class="highlight-python"><div class="highlight"><pre>
+ <div style="background:white;"><iframe src="_static/ipython_serial.html" style="width:600px; height:411px;border:0;"></iframe></div>
+
+ <!-- 6th item -->
+ <div style="background: white;">
+ <table style="width:100%;"><tr><td>
+<div class="highlight-python"><div class="highlight"><pre style="font-size: 10px;">
<span class="c"># ----------------- server ------------------</span>
<span class="kn">import</span> <span class="nn">time</span>
@@ -52,13 +98,14 @@
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">server_run</span><span class="p">((</span><span class="n">Clock</span><span class="p">,))</span>
+
</pre></div>
</div>
</td>
<td>
-<div class="highlight-python"><div class="highlight"><pre>
+<div class="highlight-python"><div class="highlight"><pre style="font-size: 10px;">
<span class="c">$ # ---------------- client -----------------</span>
<span class="n">$ python</span>
@@ -73,27 +120,29 @@
<span class="gp">>>> </span><span class="n">clock</span><span class="o">.</span><span class="n">read_attribute</span><span class="p">(</span><span class="s">"time"</span><span class="p">)</span><span class="o">.</span><span class="n">value</span>
<span class="go">1384447252.037578</span>
+<span class="gp">>>> </span><span class="n">clock</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s">"%H:%M:%S"</span><span class="p">)</span>
+<span class="go">'17:41:50'</span>
+
<span class="gp">>>> </span><span class="n">clock</span><span class="o">.</span><span class="n">command_inout</span><span class="p">(</span><span class="s">"strftime"</span><span class="p">,</span>
<span class="gp">... </span> <span class="s">"%H:%M:%S"</span><span class="p">)</span>
-<span class="go">'17:41:50'</span>
+<span class="go">'17:43:12'</span>
<span class="gp">>>> </span><span class="n">clock</span><span class="o">.</span><span class="n">status</span><span class="p">()</span>
-<span class="go">The device is in UNKNOWN state.</span>
+<span class="go">'The device is in UNKNOWN state.'</span>
+</pre></div></div>
+ </td></tr></table>
+ </div>
+ <!-- 7th item -->
+ <div>
-</pre></div>
-</div>
-
- </td>
- </tr></table>
-
- <!-- 5th snap -->
- <table style="width:100%;"><tr>
+ <table style="width:100%; background: white;"><tr>
<td>
-<div class="highlight-python"><div class="highlight"><pre>
+<div class="highlight-python"><div class="highlight"><pre style="font-size: 10px;">
<span class="gp">>>> </span><span class="c"># ---------- gevent client -----------</span>
+
<span class="gp">>>> </span><span class="kn">from</span> <span class="nn">PyTango.gevent</span> <span class="kn">import</span> <span class="n">DeviceProxy</span>
<span class="gp">>>> </span><span class="n">dev</span> <span class="o">=</span> <span class="n">DeviceProxy</span><span class="p">(</span><span class="s">"sys/tg_test/1"</span><span class="p">)</span>
@@ -128,8 +177,9 @@
</td>
<td>
-<div class="highlight-python"><div class="highlight"><pre>
+<div class="highlight-python"><div class="highlight"><pre style="font-size: 10px;">
<span class="gp">>>> </span><span class="c"># ----- concurrent.futures client -----</span>
+
<span class="gp">>>> </span><span class="kn">from</span> <span class="nn">PyTango.futures</span> <span class="kn">import</span> <span class="n">DeviceProxy</span>
<span class="gp">>>> </span><span class="n">dev</span> <span class="o">=</span> <span class="n">DeviceProxy</span><span class="p">(</span><span class="s">"sys/tg_test/1"</span><span class="p">)</span>
@@ -164,6 +214,44 @@
</td>
</tr></table>
+ </div>
+
+ </div> <!-- end of slide items -->
+
+ <!-- Arrow Navigator Skin Begin -->
+ <style>
+ /* jssor slider arrow navigator skin 03 css */
+ /*
+ .jssora03l (normal)
+ .jssora03r (normal)
+ .jssora03l:hover (normal mouseover)
+ .jssora03r:hover (normal mouseover)
+ .jssora03ldn (mousedown)
+ .jssora03rdn (mousedown)
+ */
+ .jssora03l, .jssora03r, .jssora03ldn, .jssora03rdn
+ {
+ position: absolute;
+ cursor: pointer;
+ display: block;
+ background: url(_static/arrow03.png) no-repeat;
+ overflow:hidden;
+ }
+ .jssora03l { background-position: -3px -33px; }
+ .jssora03r { background-position: -63px -33px; }
+ .jssora03l:hover { background-position: -123px -33px; }
+ .jssora03r:hover { background-position: -183px -33px; }
+ .jssora03ldn { background-position: -243px -33px; }
+ .jssora03rdn { background-position: -303px -33px; }
+
+ </style>
+ <!-- Arrow Left -->
+ <span u="arrowleft" class="jssora03l" style="width: 55px; height: 55px; top: 123px; left: 8px;">
+ </span>
+ <!-- Arrow Right -->
+ <span u="arrowright" class="jssora03r" style="width: 55px; height: 55px; top: 123px; right: 8px">
+ </span>
+
</div>
<p>
@@ -184,6 +272,8 @@
<a href="http://www.tango-controls.org/static/PyTango/latest/doc/html">Latest stable</a>
+ <a href="http://www.tango-controls.org/static/PyTango/v811/doc/html">8.1.1</a>
+
<a href="http://www.tango-controls.org/static/PyTango/v803/doc/html">8.0.3</a>
<a href="http://www.tango-controls.org/static/PyTango/v723/doc/html">7.2.3</a>
diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html
index 0e1e248..dece097 100644
--- a/doc/_templates/layout.html
+++ b/doc/_templates/layout.html
@@ -7,5 +7,7 @@
<li><a href="{{ pathto('index') }}">home</a>| </li>
<li><a href="{{ pathto('start') }}">getting started</a>| </li>
<li><a href="{{ pathto('quicktour') }}">quick tour</a>| </li>
+ <li><a href="{{ pathto('howto') }}">how to</a>| </li>
+ <li><a href="{{ pathto('faq') }}">FAQ</a>| </li>
<li><a href="{{ pathto('contents') }}">documentation </a> »</li>
{% endblock %}
diff --git a/doc/api.rst b/doc/api.rst
index 745ab10..122da04 100644
--- a/doc/api.rst
+++ b/doc/api.rst
@@ -1,7 +1,7 @@
.. currentmodule:: PyTango
-.. _api:
+.. _pytango-api:
===========
PyTango API
@@ -10,12 +10,12 @@ PyTango API
.. automodule:: PyTango
.. toctree::
- :maxdepth: 2
+ :maxdepth: 1
data_types
- client/index
+ client_api/index
+ server_api/index
database
- server/index
encoded
utilities
exception
diff --git a/doc/client/attribute_proxy.rst b/doc/client_api/attribute_proxy.rst
similarity index 100%
rename from doc/client/attribute_proxy.rst
rename to doc/client_api/attribute_proxy.rst
diff --git a/doc/client/device_proxy.rst b/doc/client_api/device_proxy.rst
similarity index 100%
rename from doc/client/device_proxy.rst
rename to doc/client_api/device_proxy.rst
diff --git a/doc/client_api/green.rst b/doc/client_api/green.rst
new file mode 100644
index 0000000..9048861
--- /dev/null
+++ b/doc/client_api/green.rst
@@ -0,0 +1,21 @@
+
+.. pytango-client-green-api:
+
+=========
+Green API
+=========
+
+Summary:
+ * :func:`PyTango.get_green_mode`
+ * :func:`PyTango.set_green_mode`
+ * :func:`PyTango.futures.DeviceProxy`
+ * :func:`PyTango.gevent.DeviceProxy`
+
+.. autofunction:: PyTango.get_green_mode
+
+.. autofunction:: PyTango.set_green_mode
+
+.. autofunction:: PyTango.futures.DeviceProxy
+
+.. autofunction:: PyTango.gevent.DeviceProxy
+
diff --git a/doc/client/group.rst b/doc/client_api/group.rst
similarity index 100%
rename from doc/client/group.rst
rename to doc/client_api/group.rst
diff --git a/doc/client/index.rst b/doc/client_api/index.rst
similarity index 92%
rename from doc/client/index.rst
rename to doc/client_api/index.rst
index 317614a..b5ee594 100644
--- a/doc/client/index.rst
+++ b/doc/client_api/index.rst
@@ -7,5 +7,6 @@ Client API
device_proxy
attribute_proxy
group
+ green
miscellaneous
other
diff --git a/doc/client_api/miscellaneous.rst b/doc/client_api/miscellaneous.rst
new file mode 100644
index 0000000..27905d5
--- /dev/null
+++ b/doc/client_api/miscellaneous.rst
@@ -0,0 +1,144 @@
+.. currentmodule:: PyTango
+
+API util
+--------
+
+.. autoclass:: ApiUtil
+ :members:
+
+Information classes
+-------------------
+
+See also `Event configuration information`_
+
+Attribute
+~~~~~~~~~
+
+.. autoclass:: AttributeAlarmInfo
+ :members:
+
+.. autoclass:: AttributeDimension
+ :members:
+
+.. autoclass:: AttributeInfo
+ :members:
+
+.. autoclass:: AttributeInfoEx
+ :members:
+
+see also :class:`AttributeInfo`
+
+.. autoclass:: DeviceAttributeConfig
+ :members:
+
+Command
+~~~~~~~
+
+.. autoclass:: DevCommandInfo
+ :members:
+
+.. autoclass:: CommandInfo
+ :members:
+
+Other
+~~~~~
+
+.. autoclass:: DeviceInfo
+ :members:
+
+.. autoclass:: LockerInfo
+ :members:
+
+.. autoclass:: PollDevice
+ :members:
+
+
+Storage classes
+---------------
+
+Attribute: DeviceAttribute
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autoclass:: DeviceAttribute
+ :members:
+
+
+Command: DeviceData
+~~~~~~~~~~~~~~~~~~~
+
+Device data is the type used internally by Tango to deal with command parameters
+and return values. You don't usually need to deal with it, as command_inout
+will automatically convert the parameters from any other type and the result
+value to another type.
+
+You can still use them, using command_inout_raw to get the result in a DeviceData.
+
+You also may deal with it when reading command history.
+
+.. autoclass:: DeviceData
+ :members:
+
+
+Callback related classes
+------------------------
+
+If you subscribe a callback in a DeviceProxy, it will be run with a parameter.
+This parameter depends will be of one of the following classes depending on
+the callback type.
+
+.. autoclass:: AttrReadEvent
+ :members:
+
+.. autoclass:: AttrWrittenEvent
+ :members:
+
+.. autoclass:: CmdDoneEvent
+ :members:
+
+
+Event related classes
+---------------------
+
+Event configuration information
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autoclass:: AttributeEventInfo
+ :members:
+
+.. autoclass:: ArchiveEventInfo
+ :members:
+
+.. autoclass:: ChangeEventInfo
+ :members:
+
+.. autoclass:: PeriodicEventInfo
+ :members:
+
+Event arrived structures
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autoclass:: EventData
+ :members:
+
+.. autoclass:: AttrConfEventData
+ :members:
+
+.. autoclass:: DataReadyEventData
+ :members:
+
+
+History classes
+---------------
+
+.. autoclass:: DeviceAttributeHistory
+ :show-inheritance:
+ :members:
+
+See :class:`DeviceAttribute`.
+
+.. autoclass:: DeviceDataHistory
+ :show-inheritance:
+ :members:
+
+See :class:`DeviceData`.
+
diff --git a/doc/client/other.rst b/doc/client_api/other.rst
similarity index 100%
rename from doc/client/other.rst
rename to doc/client_api/other.rst
diff --git a/doc/conf.py b/doc/conf.py
index 302e1b5..fcd04e0 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -29,6 +29,7 @@ extensions = ['sphinx.ext.pngmath',
'sphinx.ext.autodoc',
'sphinx.ext.doctest',
'sphinx.ext.intersphinx',
+ 'sphinx.ext.todo',
'ipython_console_highlighting',
'tango_console_highlighting']
@@ -253,6 +254,9 @@ intersphinx_mapping = {
'http://docs.sqlalchemy.org/en/rel_0_7/' : None,
}
+todo_include_todos = True
+
+
def copy_spaces(origin):
r = ''
for x in xrange(len(origin)):
diff --git a/doc/contents.rst b/doc/contents.rst
index dacf3b2..f17733c 100644
--- a/doc/contents.rst
+++ b/doc/contents.rst
@@ -9,18 +9,17 @@ Contents
.. toctree::
:maxdepth: 2
+ :titlesonly:
start
quicktour
- itango/index
+ ITango <itango>
+ green
API <api>
+ How to <howto>
+ FAQ <faq>
TEP <tep>
- faq
History of changes <revision>
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
-
**Last update:** |today|
diff --git a/doc/data_types.rst b/doc/data_types.rst
index 01d17cc..423c3f1 100644
--- a/doc/data_types.rst
+++ b/doc/data_types.rst
@@ -1,4 +1,4 @@
-.. currentmodule:: PyTang
+.. currentmodule:: PyTango
.. _pytango-data-types:
diff --git a/doc/exception.rst b/doc/exception.rst
index e31e74d..9371d9b 100644
--- a/doc/exception.rst
+++ b/doc/exception.rst
@@ -3,8 +3,10 @@
.. highlight:: python
:linenothreshold: 4
-Exception Handling
-==================
+.. _pytango-exception-api:
+
+Exception API
+=============
Exception definition
--------------------
@@ -42,40 +44,34 @@ all of which containing the following kind of key-value pairs:
::
- # Protect the script from Exceptions raised by the Tango or python itself
+ import PyTango
+
+ # How to protect the script from exceptions raised by the Tango
try:
- # Get proxy on the tangotest1 device
- print "Getting DeviceProxy "
- tangotest = DeviceProxy("tango/tangotest/1")
-
- #Catch Tango and Systems Exceptions
- except DevFailed:
- exctype , value = sys.exc_info()[:2]
- print "Failed with exception ! " , exctype
- for err in value:
- print " reason" , err.reason
- print " description" , err.desc
- print " origin" , err.origin
- print " severity" , err.severity
+ # Get proxy on a non existing device should throw an exception
+ device = DeviceProxy("non/existing/device")
+ except DevFailed as df:
+ print("Failed to create proxy to non/existing/device:\n%s" % df)
+
Throwing exception in a device server
-------------------------------------
-The C++ Tango::Except class with its most important methods have been wrapped to Python.
-Therefore, in a Python device server, you have the following methods to throw, re-throw or
-print a Tango::DevFailed exception :
+The C++ :class:`~PyTango::Except` class with its most important methods have
+been wrapped to Python. Therefore, in a Python device server, you have the
+following methods to throw, re-throw or print a Tango::DevFailed exception :
-- *throw_exception()* which is a static method
-- *re_throw_exception()* which is also a static method
-- *print_exception()* which is also a static method
+- :meth:`~PyTango.Except.throw_exception` which is a static method
+- :meth:`~PyTango.Except.re_throw_exception` which is also a static method
+- :meth:`~PyTango.Except.print_exception` which is also a static method
-The following code is an example of a command method requesting a command on a sub-device and re-throwing
-the exception in case of::
+The following code is an example of a command method requesting a command on a
+sub-device and re-throwing the exception in case of::
try:
dev.command_inout("SubDevCommand")
- except PyTango.DevFailed, e:
- PyTango.Except.re_throw_exception(e,
+ except PyTango.DevFailed as df:
+ PyTango.Except.re_throw_exception(df,
"MyClass_CommandFailed",
"Sub device command SubdevCommand failed",
"Command()")
diff --git a/doc/faq.rst b/doc/faq.rst
index d3b4908..c559ec7 100644
--- a/doc/faq.rst
+++ b/doc/faq.rst
@@ -1,5 +1,7 @@
.. currentmodule:: PyTango
+.. _pytango-faq:
+
FAQ
===
@@ -7,8 +9,7 @@ Answers to general Tango questions can be found at http://www.tango-controls.org
Please also check http://www.tango-controls.org/howtos for a list of Tango howtos
-Where are the usual bjam files?
--------------------------------
+**Where are the usual bjam files?**
Starting from PyTango 7.0.0 the prefered way to build PyTango is using the standard
python distutils package. This means that:
@@ -19,8 +20,7 @@ python distutils package. This means that:
Please check the compilation chapter for details on how to build PyTango.
-I got a libbost_python error when I try to import PyTango module
-----------------------------------------------------------------
+**I got a libbost_python error when I try to import PyTango module**
doing:
>>> import PyTango
@@ -48,8 +48,7 @@ To see which boost python file PyTango needs type::
/lib64/ld-linux-x86-64.so.2 (0x00007f3940a4c000)
-My python code uses PyTango 3.0.4 API. How do I change to 7.0.0 API?
---------------------------------------------------------------------
+**My python code uses PyTango 3.0.4 API. How do I change to 7.0.0 API?**
To ease migration effort, PyTango 7 provides an alternative module called
PyTango3.
@@ -69,8 +68,7 @@ since the PyTango team cannot assure the maintainability of the PyTango3 module.
Please find below a basic set of rules to migrate from PyTango 3.0.x to 7:
-General rule of thumb for data types
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*General rule of thumb for data types*
The first important thing to be aware of when migrating from PyTango <= 3.0.4 to
PyTango >= 7 is that the data type mapping from tango to python and vice versa is
@@ -162,8 +160,8 @@ reference, the proper code would be::
if not da.data_format is PyTango.AttrDataFormat.SCALAR:
print "array_attr is NOT a scalar attribute"
-Server
-~~~~~~
+*Server*
+
#. replace `PyTango.PyUtil` with :class:`Util`
@@ -176,8 +174,7 @@ Server
in PyTango 7 the methods have been renamed to **dev_state()** and **dev_status()** in
order to match the C++ API.
-General
-~~~~~~~
+*General*
#. AttributeValue does **NOT** exist anymore.
- the result of a read_attribute call on a :class:`DeviceProxy` / :class:`Group`
@@ -224,118 +221,15 @@ General
print e.args[0].reason
-Optional
-~~~~~~~~
+*Optional*
The following is a list of API improvements. Some where added for performance
reasons, others to allow for a more pythonic interface, others still to reflect
more adequately the C++ interface. They are not mandatory since the original
interface will still be available.
-Server side V3 to V4 upgrade
-############################
-
-If you want your server to support the V4 interface provided by Tango 7
-instead of the V3 provided by Tango 6:
-
-- replace the inheritance of your device class from :class:`Device_3Impl` to :class:`Device_4Impl`
-- in the `init_device` method replace the call::
-
- Device_3Impl.init_device(self)
-
- with::
-
- Device_4Impl.init_device(self)
-
- or better yet, if your device class only inherits from :class:`Device_4Impl`::
-
- super(<your class>, self).init_device()
-
-Improved server side image attribute read API
-#############################################
-
-In PyTango <= 3.0.4, to set the value of an image attribute you needed it
-as a flat list. Consider you want to set as value the following image::
-
- # Image:
- # | 1 2 |
- # | 3 4 |
-
-In order to tell tango the dimensions of the image you had to specify them as::
-
- image = [ 1, 2, 3, 4]
- dim_x = 2
- dim_y = 2
- attr.set_value(image, dim_x, dim_y)
-
-In PyTango 8 it is still supported, but the preferred way is to use a
-sequence of sequences (instead of a flat sequence), so the dimensions
-are inherent and not needed anymore::
-
- image = [ [1, 2], [3, 4]]
- attr.set_value(image)
-
-If you use a numpy array as the sequence of sequences you can get better
-performance::
-
- image = numpy.array([ [1, 2], [3, 4]], dtype=numpy.int32)
- attr.set_value(image)
-
-Likewise, calls to::
-
- PyTango.set_attribute_value_date_quality(attr, value, date, quality, dim_x, dim_y)
-
-can be replaced with::
+**Why is there a "-Wstrict-prototypes" warning when I compile PyTango?**
- attr.set_value_date_quality(value, date, quality)
-
-Improved server side attribute write API
-########################################
-
-Imagine the following value is written to our IMAGE attribute::
-
- # Image:
- # | 1 2 |
- # | 3 4 |
-
-This is what you would do with PyTango <= 3.0.4::
-
- flatList = []
- attr.get_write_value(flatList)
- print "flatList =", flatList
- # flatList = [ 1, 2, 3, 4 ]
-
-You can still do it with PyTango 8. However I recommend::
-
- image = attr.get_write_value()
- print "image =", image
- # image = numpy.array([[1, 2], [3, 4]])
-
-If PyTango 8 is compiled without numpy support, you will get a sequence
-of sequences, which makes more sense than a flat list.
-
-If PyTango 8 is compiled with numpy support it does not only makes more sense
-but it is also considerably **faster and memory friendlier**.
-
-If PyTango is compiled with numpy support but you prefer a list of lists for
-some attribute, you can do::
-
- image = attr.get_write_value(PyTango.ExtractAs.List)
- print "image =", image
- # image = [[1, 2], [3, 4]]
-
-Also the SCALAR attribute case is much **cleaner** now. Instead of::
-
- data = []
- attr.get_write_value(data)
- actualData = data[0]
-
-You can just write::
-
- actualData = attr.get_write_value()
-
-Why is there a "-Wstrict-prototypes" warning when I compile PyTango?
---------------------------------------------------------------------
The PyTango prefered build system (distutils) uses the same flags used to compile
Python to compile PyTango. It happens that Python is compiled as a pure C library
@@ -349,8 +243,8 @@ For reference here is the complete error message you may have:
Do not worry about this warning since the compiler is ignoring the presence of this flag
in the compilation.
-Why are there so many warnings when generating the documentation?
------------------------------------------------------------------
+**Why are there so many warnings when generating the documentation?**
+
PyTango uses boost python for the binding between C++ and Python and sphinx for
document generation.
When sphinx generates the PyTango API documentation it uses introspection to search
diff --git a/doc/client/miscellaneous.rst b/doc/green.rst
similarity index 75%
rename from doc/client/miscellaneous.rst
rename to doc/green.rst
index 0eaba34..00cbeff 100644
--- a/doc/client/miscellaneous.rst
+++ b/doc/green.rst
@@ -1,7 +1,7 @@
.. currentmodule:: PyTango
-Green objects
--------------
+Green mode
+----------
PyTango supports cooperative green Tango objects. Since version 8.1 two *green*
modes have been added: :obj:`~PyTango.GreenMode.Futures` and
@@ -13,8 +13,8 @@ The :obj:`~PyTango.GreenMode.Gevent` mode uses the well known gevent_ library.
Currently, in version 8.1, only :class:`DeviceProxy` has been modified to work
in a green cooperative way. If the work is found to be useful, the same can
-be implemented in the future for :class:`AttributeProxy` and even
-to :class:`Database`.
+be implemented in the future for :class:`AttributeProxy`, :class:`Database`,
+:class:`Group` or even in the server side.
You can set the PyTango green mode at a global level. Set the environment
variable :envvar:`PYTANGO_GREEN_MODE` to either *futures* or *gevent*
@@ -264,171 +264,3 @@ Here is another example using :meth:`~DeviceProxy.read_attribute`::
This is, in fact, one of the major bonus of working with :mod:`gevent` when
compared with :mod:`concurrent.futures`
-
-Green API
-~~~~~~~~~
-
-Summary:
- * :func:`PyTango.get_green_mode`
- * :func:`PyTango.set_green_mode`
- * :func:`PyTango.futures.DeviceProxy`
- * :func:`PyTango.gevent.DeviceProxy`
-
-.. autofunction:: PyTango.get_green_mode
-
-.. autofunction:: PyTango.set_green_mode
-
-.. autofunction:: PyTango.futures.DeviceProxy
-
-.. autofunction:: PyTango.gevent.DeviceProxy
-
-
-Low level API
-#############
-
-.. autofunction:: get_device_proxy
-
-.. autofunction:: get_attribute_proxy
-
-API util
---------
-
-.. autoclass:: PyTango.ApiUtil
- :members:
-
-Information classes
--------------------
-
-See also `Event configuration information`_
-
-Attribute
-~~~~~~~~~
-
-.. autoclass:: PyTango.AttributeAlarmInfo
- :members:
-
-.. autoclass:: PyTango.AttributeDimension
- :members:
-
-.. autoclass:: PyTango.AttributeInfo
- :members:
-
-.. autoclass:: PyTango.AttributeInfoEx
- :members:
-
-see also :class:`PyTango.AttributeInfo`
-
-.. autoclass:: PyTango.DeviceAttributeConfig
- :members:
-
-Command
-~~~~~~~
-
-.. autoclass:: PyTango.DevCommandInfo
- :members:
-
-.. autoclass:: PyTango.CommandInfo
- :members:
-
-Other
-~~~~~
-
-.. autoclass:: PyTango.DeviceInfo
- :members:
-
-.. autoclass:: PyTango.LockerInfo
- :members:
-
-.. autoclass:: PyTango.PollDevice
- :members:
-
-
-Storage classes
----------------
-
-Attribute: DeviceAttribute
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. autoclass:: DeviceAttribute
- :members:
-
-
-Command: DeviceData
-~~~~~~~~~~~~~~~~~~~
-
-Device data is the type used internally by Tango to deal with command parameters
-and return values. You don't usually need to deal with it, as command_inout
-will automatically convert the parameters from any other type and the result
-value to another type.
-
-You can still use them, using command_inout_raw to get the result in a DeviceData.
-
-You also may deal with it when reading command history.
-
-.. autoclass:: DeviceData
- :members:
-
-
-Callback related classes
-------------------------
-
-If you subscribe a callback in a DeviceProxy, it will be run with a parameter.
-This parameter depends will be of one of the following classes depending on
-the callback type.
-
-.. autoclass:: PyTango.AttrReadEvent
- :members:
-
-.. autoclass:: PyTango.AttrWrittenEvent
- :members:
-
-.. autoclass:: PyTango.CmdDoneEvent
- :members:
-
-
-Event related classes
----------------------
-
-Event configuration information
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. autoclass:: PyTango.AttributeEventInfo
- :members:
-
-.. autoclass:: PyTango.ArchiveEventInfo
- :members:
-
-.. autoclass:: PyTango.ChangeEventInfo
- :members:
-
-.. autoclass:: PyTango.PeriodicEventInfo
- :members:
-
-Event arrived structures
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. autoclass:: PyTango.EventData
- :members:
-
-.. autoclass:: PyTango.AttrConfEventData
- :members:
-
-.. autoclass:: PyTango.DataReadyEventData
- :members:
-
-
-History classes
----------------
-
-.. autoclass:: PyTango.DeviceAttributeHistory
- :show-inheritance:
- :members:
-
-See :class:`DeviceAttribute`.
-
-.. autoclass:: PyTango.DeviceDataHistory
- :show-inheritance:
- :members:
-
-See :class:`DeviceData`.
-
diff --git a/doc/server/index.rst b/doc/howto.rst
similarity index 72%
copy from doc/server/index.rst
copy to doc/howto.rst
index d437c04..c6a4599 100644
--- a/doc/server/index.rst
+++ b/doc/howto.rst
@@ -2,37 +2,578 @@
.. highlight:: python
:linenothreshold: 3
-
-.. _server:
-
-Server API
-==========
+.. _pytango-howto:
+
+======
+How to
+======
+
+This is a small list of how-tos specific to PyTango. A more general Tango how-to
+list can be found `here <http://www.tango-controls.org/howtos>`_.
+
+
+Check the default TANGO host
+----------------------------
+
+The default TANGO host can be defined using the environment variable
+:envvar:`TANGO_HOST` or in a `tangorc` file
+(see `Tango environment variables <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node11.html#SECTION0011123000000000000000>`_
+for complete information)
+
+To check what is the current value that TANGO uses for the default configuration
+simple do::
+
+ >>> import PyTango
+ >>> PyTango.ApiUtil.get_env_var("TANGO_HOST")
+ 'homer.simpson.com:10000'
+
+Check TANGO version
+-------------------
+
+There are two library versions you might be interested in checking:
+The PyTango version::
+
+ >>> import PyTango
+ >>> PyTango.__version__
+ '8.1.1'
+
+ >>> PyTango.__version_info__
+ (8, 1, 1, 'final', 0)
+
+... and the Tango C++ library version that PyTango was compiled with::
+
+ >>> import PyTango
+ >>> PyTango.constants.TgLibVers
+ '8.1.2'
+
+
+Report a bug
+------------
+
+Bugs can be reported as tickets in `TANGO Source forge <https://sourceforge.net/p/tango-cs/bugs/>`_.
+
+When making a bug report don't forget to select *PyTango* in **Category**.
+
+It is also helpfull if you can put in the ticket description the PyTango information.
+It can be a dump of::
+
+ $ python -c "from PyTango.utils import info; print(info())"
+
+
+Work with Groups
+----------------
+
+.. todo::
+ write this how to
+
+Handle errors
+-------------
+
+.. todo::
+ write this how to
+
+.. _pytango-howto-server:
+
+Write a server
+--------------
+
+Before reading this chapter you should be aware of the TANGO basic concepts.
This chapter does not explain what a Tango device or a device server is.
-This is explained in details in "The Tango control system manual" available at
-http://www.tango-controls.org/TangoKernel.
-The device server described in the following example is a Tango device server
-with one Tango class called *PyDsExp*. This class has two commands called
-*IOLong* and *IOStringArray* and two attributes called *Long_attr* and
-*Short_attr_rw*.
+This is explained in details in the
+`Tango control system manual <http://www.tango-controls.org/TangoKernel>`_
+
+Since version 8.1, PyTango provides a helper module which simplifies the
+development of a Tango device server. This helper is provided through the
+:mod:`PyTango.server` module.
+
+Here is a simple example on how to write a *Clock* device server using the
+high level API
+
+.. code-block:: python
+ :linenos:
+
+ import time
+ from PyTango.server import run
+ from PyTango.server import Device, DeviceMeta
+ from PyTango.server import attribute, command
+
+
+ class Clock(Device):
+ __metaclass__ = DeviceMeta
+
+ @attribute
+ def time(self):
+ return time.time()
+
+ @command(dtype_in=str, dtype_out=str)
+ def strftime(self, format):
+ return time.strftime(format)
+
+
+ if __name__ == "__main__":
+ run([Clock])
+
+
+**line 2-4**
+ import the necessary symbols
+
+**line 7**
+ tango device class definition. A Tango device must inherit from
+ :class:`PyTango.server.Device`
+
+**line 8**
+ mandatory *magic* line. A Tango device must define the metaclass as
+ :class:`PyTango.server.DeviceClass`. This has to be done due to a limitation
+ on boost-python
+
+**line 10-12**
+ definition of the *time* attribute. By default, attributes are double, scalar,
+ read-only. Check the :class:`~PyTango.server.attribute` for the complete
+ list of attribute options.
+
+**line 14-16**
+ the method *strftime* is exported as a Tango command. In receives a string
+ as argument and it returns a string. If a method is to be exported as a
+ Tango command, it must be decorated as such with the
+ :func:`~PyTango.server.command` decorator
+
+**line 20**
+ start the Tango run loop. The mandatory argument is a list of python classes
+ that are to be exported as Tango classes. Check :func:`~PyTango.server.run`
+ for the complete list of options
+
+Here is a more complete example on how to write a *PowerSupply* device server
+using the high level API. The example contains:
+
+#. a read-only double scalar attribute called *voltage*
+#. a read/write double scalar expert attribute *current*
+#. a read-only double image attribute called *noise*
+#. a *ramp* command
+#. a *host* device property
+#. a *port* class property
+
+.. code-block:: python
+ :linenos:
+
+ from time import time
+ from numpy.random import random_sample
+
+ from PyTango import AttrQuality, AttrWriteType, DispLevel, run
+ from PyTango.server import Device, DeviceMeta, attribute, command
+ from PyTango.server import class_property, device_property
+
+
+ class PowerSupply(Device):
+ __metaclass__ = DeviceMeta
+
+ current = attribute(label="Current", dtype=float,
+ display_level=DispLevel.EXPERT,
+ access=AttrWriteType.READ_WRITE,
+ unit="A", format="8.4f",
+ min_value=0.0, max_value=8.5,
+ min_alarm=0.1, max_alarm=8.4,
+ min_warning=0.5, max_warning=8.0,
+ fget="get_current", fset="set_current",
+ doc="the power supply current")
+
+ noise = attribute(label="Noise", dtype=((float,),),
+ max_dim_x=1024, max_dim_y=1024,
+ fget="get_noise")
+
+ host = device_property(dtype=str)
+ port = class_property(dtype=int, default_value=9788)
+
+ @attribute
+ def voltage(self):
+ self.info_stream("get voltage(%s, %d)" % (self.host, self.port))
+ return 10.0
+
+ def get_current(self):
+ return 2.3456, time(), AttrQuality.ATTR_WARNING
+
+ def set_current(self, current):
+ print("Current set to %f" % current)
+
+ def get_noise(self):
+ return random_sample((1024, 1024))
+
+ @command(dtype_in=float)
+ def ramp(self, value):
+ print("Ramping up...")
+
+
+ if __name__ == "__main__":
+ run([PowerSupply])
+
+
+.. note::
+ the ``__metaclass__`` statement is mandatory due to a limitation in the
+ *boost-python* library used by PyTango.
+
+ If you are using python 3 you can write instead::
+
+ class PowerSupply(Device, metaclass=DeviceMeta)
+ pass
+
+.. _logging:
+
+Server logging
+--------------
+
+This chapter instructs you on how to use the tango logging API (log4tango) to
+create tango log messages on your device server.
+
+The logging system explained here is the Tango Logging Service (TLS). For
+detailed information on how this logging system works please check:
+
+ * `3.5 The tango logging service <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node4.html#sec:The-Tango-Logging>`_
+ * `9.3 The tango logging service <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node9.html#SECTION00930000000000000000>`_
+
+The easiest way to start seeing log messages on your device server console is
+by starting it with the verbose option. Example::
+
+ python PyDsExp.py PyDs1 -v4
+
+This activates the console tango logging target and filters messages with
+importance level DEBUG or more.
+The links above provided detailed information on how to configure log levels
+and log targets. In this document we will focus on how to write log messages on
+your device server.
+
+Basic logging
+~~~~~~~~~~~~~
+
+The most basic way to write a log message on your device is to use the
+:class:`~PyTango.server.Device` logging related methods:
+
+ * :meth:`~PyTango.server.Device.debug_stream`
+ * :meth:`~PyTango.server.Device.info_stream`
+ * :meth:`~PyTango.server.Device.warn_stream`
+ * :meth:`~PyTango.server.Device.error_stream`
+ * :meth:`~PyTango.server.Device.fatal_stream`
+
+Example::
+
+ def read_voltage(self):
+ self.info_stream("read voltage attribute")
+ # ...
+ return voltage_value
+
+This will print a message like::
+
+ 1282206864 [-1215867200] INFO test/power_supply/1 read voltage attribute
+
+every time a client asks to read the *voltage* attribute value.
+
+The logging methods support argument list feature (since PyTango 8.1). Example::
+
+ def read_voltage(self):
+ self.info_stream("read_voltage(%s, %d)", self.host, self.port)
+ # ...
+ return voltage_value
+
+
+Logging with print statement
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+*This feature is only possible since PyTango 7.1.3*
+
+It is possible to use the print statement to log messages into the tango logging
+system. This is achieved by using the python's print extend form sometimes
+refered to as *print chevron*.
+
+Same example as above, but now using *print chevron*::
+
+ def read_voltage(self, the_att):
+ print >>self.log_info, "read voltage attribute"
+ # ...
+ return voltage_value
+
+Or using the python 3k print function::
+
+ def read_Long_attr(self, the_att):
+ print("read voltage attribute", file=self.log_info)
+ # ...
+ return voltage_value
+
+
+Logging with decorators
+~~~~~~~~~~~~~~~~~~~~~~~
+
+*This feature is only possible since PyTango 7.1.3*
+
+PyTango provides a set of decorators that place automatic log messages when
+you enter and when you leave a python method. For example::
+
+ @PyTango.DebugIt()
+ def read_Long_attr(self, the_att):
+ the_att.set_value(self.attr_long)
+
+will generate a pair of log messages each time a client asks for the 'Long_attr'
+value. Your output would look something like::
+
+ 1282208997 [-1215965504] DEBUG test/pydsexp/1 -> read_Long_attr()
+ 1282208997 [-1215965504] DEBUG test/pydsexp/1 <- read_Long_attr()
+
+Decorators exist for all tango log levels:
+ * :class:`PyTango.DebugIt`
+ * :class:`PyTango.InfoIt`
+ * :class:`PyTango.WarnIt`
+ * :class:`PyTango.ErrorIt`
+ * :class:`PyTango.FatalIt`
+
+The decorators receive three optional arguments:
+ * show_args - shows method arguments in log message (defaults to False)
+ * show_kwargs shows keyword method arguments in log message (defaults to False)
+ * show_ret - shows return value in log message (defaults to False)
+
+Example::
+
+ @PyTango.DebugIt(show_args=True, show_ret=True)
+ def IOLong(self, in_data):
+ return in_data * 2
+
+will output something like::
+
+ 1282221947 [-1261438096] DEBUG test/pydsexp/1 -> IOLong(23)
+ 1282221947 [-1261438096] DEBUG test/pydsexp/1 46 <- IOLong()
+
+
+Multiple device classes (Python and C++) in a server
+----------------------------------------------------
+
+Within the same python interpreter, it is possible to mix several Tango classes.
+Let's say two of your colleagues programmed two separate Tango classes in two
+separated python files: A :class:`PLC` class in a :file:`PLC.py`::
+
+ # PLC.py
+
+ from PyTango.server import Device, DeviceMeta, run
+
+ class PLC(Device):
+ __metaclass__ = DeviceMeta
+
+ # bla, bla my PLC code
+
+ if __name__ == "__main__":
+ run([PLC])
+
+... and a :class:`IRMirror` in a :file:`IRMirror.py`::
+
+ # IRMirror.py
+
+ from PyTango.server import Device, DeviceMeta, run
+
+ class IRMirror(Device):
+ __metaclass__ = DeviceMeta
+
+ # bla, bla my IRMirror code
+
+ if __name__ == "__main__":
+ run([IRMirror])
+
+You want to create a Tango server called `PLCMirror` that is able to contain
+devices from both PLC and IRMirror classes. All you have to do is write
+a :file:`PLCMirror.py` containing the code::
+
+ # PLCMirror.py
+
+ from PyTango.server import run
+ from PLC import PLC
+ from IRMirror import IRMirror
+
+ run([PLC, IRMirror])
+
+It is also possible to add C++ Tango class in a Python device server as soon as:
+ 1. The Tango class is in a shared library
+ 2. It exist a C function to create the Tango class
+
+For a Tango class called MyTgClass, the shared library has to be called
+MyTgClass.so and has to be in a directory listed in the LD_LIBRARY_PATH
+environment variable. The C function creating the Tango class has to be called
+_create_MyTgClass_class() and has to take one parameter of type "char \*" which
+is the Tango class name. Here is an example of the main function of the same
+device server than before but with one C++ Tango class called SerialLine::
+
+ import PyTango
+ import sys
+
+ if __name__ == '__main__':
+ py = PyTango.Util(sys.argv)
+ util.add_class('SerialLine', 'SerialLine', language="c++")
+ util.add_class(PLCClass, PLC, 'PLC')
+ util.add_class(IRMirrorClass, IRMirror, 'IRMirror')
+
+ U = PyTango.Util.instance()
+ U.server_init()
+ U.server_run()
+
+:Line 6: The C++ class is registered in the device server
+:Line 7 and 8: The two Python classes are registered in the device server
+
+Create attributes dynamically
+-----------------------------
+
+It is also possible to create dynamic attributes within a Python device server.
+There are several ways to create dynamic attributes. One of the way, is to
+create all the devices within a loop, then to create the dynamic attributes and
+finally to make all the devices available for the external world. In C++ device
+server, this is typically done within the <Device>Class::device_factory() method.
+In Python device server, this method is generic and the user does not have one.
+Nevertheless, this generic device_factory method calls a method named dyn_attr()
+allowing the user to create his dynamic attributes. It is simply necessary to
+re-define this method within your <Device>Class and to create the dynamic
+attribute within this method:
+
+ ``dyn_attr(self, dev_list)``
+
+ where dev_list is a list containing all the devices created by the
+ generic device_factory() method.
+
+There is another point to be noted regarding dynamic attribute within Python
+device server. The Tango Python device server core checks that for each
+attribute it exists methods named <attribute_name>_read and/or
+<attribute_name>_write and/or is_<attribute_name>_allowed. Using dynamic
+attribute, it is not possible to define these methods because attributes name
+and number are known only at run-time.
+To address this issue, the Device_3Impl::add_attribute() method has a diferent
+signature for Python device server which is:
+
+ ``add_attribute(self, attr, r_meth = None, w_meth = None, is_allo_meth = None)``
+
+attr is an instance of the Attr class, r_meth is the method which has to be
+executed with the attribute is read, w_meth is the method to be executed
+when the attribute is written and is_allo_meth is the method to be executed
+to implement the attribute state machine. The method passed here as argument
+as to be class method and not object method. Which argument you have to use
+depends on the type of the attribute (A WRITE attribute does not need a
+read method). Note, that depending on the number of argument you pass to this
+method, you may have to use Python keyword argument. The necessary methods
+required by the Tango Python device server core will be created automatically
+as a forward to the methods given as arguments.
+
+Here is an example of a device which has a TANGO command called
+*createFloatAttribute*. When called, this command creates a new scalar floating
+point attribute with the specified name::
+
+
+ from PyTango import Util, Attr
+ from PyTango.server import DeviceMeta, Device, command
+
+ class MyDevice(Device):
+ __metaclass__ = DeviceMeta
+
+ @command(dtype_in=str)
+ def CreateFloatAttribute(self, attr_name):
+ attr = Attr(attr_name, PyTango.DevDouble)
+ self.add_attribute(attr, self.read_General, self.write_General)
+
+ def read_General(self, attr):
+ self.info_stream("Reading attribute %s", attr.get_name())
+ attr.set_value(99.99)
+
+ def write_General(self, attr):
+ self.info_stream("Writting attribute %s", attr.get_name())
+
+
+Create/Delete devices dynamically
+---------------------------------
+
+*This feature is only possible since PyTango 7.1.2*
+
+Starting from PyTango 7.1.2 it is possible to create devices in a device server
+"en caliente". This means that you can create a command in your "management device"
+of a device server that creates devices of (possibly) several other tango classes.
+There are two ways to create a new device which are described below.
+
+Tango imposes a limitation: the tango class(es) of the device(s) that is(are)
+to be created must have been registered before the server starts.
+If you use the high level API, the tango class(es) must be listed in the call
+to :func:`~PyTango.server.run`. If you use the lower level server API, it must
+be done using individual calls to :meth:`~PyTango.Util.add_class`.
+
+
+Dynamic device from a known tango class name
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you know the tango class name but you don't have access to the :class:`PyTango.DeviceClass`
+(or you are too lazy to search how to get it ;-) the way to do it is call
+:meth:`~PyTango.Util.create_device` / :meth:`~PyTango.Util.delete_device`.
+Here is an example of implementing a tango command on one of your devices that
+creates a device of some arbitrary class (the example assumes the tango commands
+'CreateDevice' and 'DeleteDevice' receive a parameter of type DevVarStringArray
+with two strings. No error processing was done on the code for simplicity sake)::
+
+ from PyTango import Util
+ from PyTango.server import DeviceMeta, Device, command
+
+ class MyDevice(Device):
+ __metaclass__ = DeviceMeta
+
+ @command(dtype_in=[str])
+ def CreateDevice(self, pars):
+ klass_name, dev_name = pars
+ util = Util.instance()
+ util.create_device(klass_name, dev_name, alias=None, cb=None)
+
+ @command(dtype_in=[str])
+ def DeleteDevice(self, pars):
+ klass_name, dev_name = pars
+ util = Util.instance()
+ util.delete_device(klass_name, dev_name)
+
+An optional callback can be registered that will be executed after the device is
+registed in the tango database but before the actual device object is created
+and its init_device method is called. It can be used, for example, to initialize
+some device properties.
+
+Dynamic device from a known tango class
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Importing python modules
-------------------------
+If you already have access to the :class:`~PyTango.DeviceClass` object that
+corresponds to the tango class of the device to be created you can call directly
+the :meth:`~PyTango.DeviceClass.create_device` / :meth:`~PyTango.DeviceClass.delete_device`.
+For example, if you wish to create a clone of your device, you can create a
+tango command called *Clone*::
-To write a Python script which is a Tango device server, you need to import
-two modules which are:
+ class MyDevice(PyTango.Device_4Impl):
+
+ def fill_new_device_properties(self, dev_name):
+ prop_names = db.get_device_property_list(self.get_name(), "*")
+ prop_values = db.get_device_property(self.get_name(), prop_names.value_string)
+ db.put_device_property(dev_name, prop_values)
+
+ # do the same for attributes...
+ ...
+
+ def Clone(self, dev_name):
+ klass = self.get_device_class()
+ klass.create_device(dev_name, alias=None, cb=self.fill_new_device_properties)
+
+ def DeleteSibling(self, dev_name):
+ klass = self.get_device_class()
+ klass.delete_device(dev_name)
+
+Note that the cb parameter is optional. In the example it is given for
+demonstration purposes only.
-1. The :mod:`PyTango` module which is the Python to C++ interface
-2. The Python classical :mod:`sys` module
+.. _server:
-This could be done with code like (supposing the PYTHONPATH environment variable
-is correctly set)::
+Write a server (original API)
+-----------------------------
- import PyTango
- import sys
+This chapter describes how to develop a PyTango device server using the
+original PyTango server API. This API mimics the C++ API and is considered
+low level.
+You should write a server using this API if you are using code generated by
+`Pogo tool <http://www.esrf.eu/computing/cs/tango/tango_doc/tools_doc/pogo_doc/index.html>`_
+or if for some reason the high level API helper doesn't provide a feature
+you need (in that case think of writing a mail to tango mailing list explaining
+what you cannot do).
The main part of a Python device server
----------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The rule of this part of a Tango device server is to:
@@ -68,7 +609,7 @@ The following is a typical code for this main function::
Run the device server loop
The PyDsExpClass class in Python
---------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The rule of this class is to :
@@ -122,7 +663,7 @@ constructor. An example of such a contructor is ::
The device type is set at line 3.
Defining commands
------------------
+~~~~~~~~~~~~~~~~~
As shown in the previous example, commands have to be defined in a :class:`dict`
called *cmd_list* as a data member of the xxxClass class of the Tango class.
@@ -152,7 +693,7 @@ this :class:`dict` are summarized in the following array:
+-------------------+----------------------+------------------------------------------+
Defining attributes
--------------------
+~~~~~~~~~~~~~~~~~~~
As shown in the previous example, attributes have to be defined in a :class:`dict`
called **attr_list** as a data
@@ -217,7 +758,7 @@ array:
+-------------------+-----------------------------------+------------------------------------------+
The PyDsExp class in Python
----------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
The rule of this class is to implement methods executed by commands and attributes.
In our example, the code of this class looks like::
@@ -480,284 +1021,4 @@ write the Short_attr_rw attribute::
store the value written in the device object. Our attribute is a scalar
short attribute so the return value is an int
-.. _logging:
-
-Logging
-#######
-
-This chapter instructs you on how to use the tango logging API (log4tango) to
-create tango log messages on your device server.
-
-The logging system explained here is the Tango Logging Service (TLS). For
-detailed information on how this logging system works please check:
-
- * `3.5 The tango logging service <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node4.html#sec:The-Tango-Logging>`_
- * `9.3 The tango logging service <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node9.html#SECTION00930000000000000000>`_
-
-The easiest way to start seeing log messages on your device server console is
-by starting it with the verbose option. Example::
-
- python PyDsExp.py PyDs1 -v4
-
-This activates the console tango logging target and filters messages with
-importance level DEBUG or more.
-The links above provided detailed information on how to configure log levels
-and log targets. In this document we will focus on how to write log messages on
-your device server.
-
-Basic logging
-~~~~~~~~~~~~~
-
-The most basic way to write a log message on your device is to use the
-:class:`PyTango.DeviceImpl` logging related methods:
-
- * :meth:`PyTango.DeviceImpl.debug_stream`
- * :meth:`PyTango.DeviceImpl.info_stream`
- * :meth:`PyTango.DeviceImpl.warn_stream`
- * :meth:`PyTango.DeviceImpl.error_stream`
- * :meth:`PyTango.DeviceImpl.fatal_stream`
-
-Example::
-
- def read_Long_attr(self, the_att):
- self.info_stream("read attribute name Long_attr")
- the_att.set_value(self.attr_long)
-
-This will print a message like::
-
- 1282206864 [-1215867200] INFO test/pydsexp/1 read attribute name Long_attr
-
-every time a client asks to read the 'Long_attr' attribute value.
-
-Logging with print statement
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-*This feature is only possible since PyTango 7.1.3*
-
-It is possible to use the print statement to log messages into the tango logging
-system. This is achieved by using the python's print extend form sometimes
-refered to as *print chevron*.
-
-Same example as above, but now using *print chevron*::
-
- def read_Long_attr(self, the_att):
- print >>self.log_info, "read attribute name Long_attr"
- the_att.set_value(self.attr_long)
-
-Or using the python 3k print function::
-
- def read_Long_attr(self, the_att):
- print("read attribute name Long_attr", file=self.log_info)
- the_att.set_value(self.attr_long)
-
-Logging with decorators
-~~~~~~~~~~~~~~~~~~~~~~~
-
-*This feature is only possible since PyTango 7.1.3*
-
-PyTango provides a set of decorators that place automatic log messages when
-you enter and when you leave a python method. For example::
-
- @PyTango.DebugIt()
- def read_Long_attr(self, the_att):
- the_att.set_value(self.attr_long)
-
-will generate a pair of log messages each time a client asks for the 'Long_attr'
-value. Your output would look something like::
-
- 1282208997 [-1215965504] DEBUG test/pydsexp/1 -> read_Long_attr()
- 1282208997 [-1215965504] DEBUG test/pydsexp/1 <- read_Long_attr()
-
-Decorators exist for all tango log levels:
- * :class:`PyTango.DebugIt`
- * :class:`PyTango.InfoIt`
- * :class:`PyTango.WarnIt`
- * :class:`PyTango.ErrorIt`
- * :class:`PyTango.FatalIt`
-
-The decorators receive three optional arguments:
- * show_args - shows method arguments in log message (defaults to False)
- * show_kwargs shows keyword method arguments in log message (defaults to False)
- * show_ret - shows return value in log message (defaults to False)
-
-Example::
-
- @PyTango.DebugIt(show_args=True, show_ret=True)
- def IOLong(self, in_data):
- return in_data * 2
-
-will output something like::
-
- 1282221947 [-1261438096] DEBUG test/pydsexp/1 -> IOLong(23)
- 1282221947 [-1261438096] DEBUG test/pydsexp/1 46 <- IOLong()
-
-Dynamic devices
-###############
-
-*This feature is only possible since PyTango 7.1.2*
-
-Starting from PyTango 7.1.2 it is possible to create devices in a device server
-"en caliente". This means that you can create a command in your "management device"
-of a device server that creates devices of (possibly) several other tango classes.
-There are two ways to create a new device which are described below.
-
-Dynamic device from a known tango class name
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you know the tango class name but you don't have access to the :class:`PyTango.DeviceClass`
-(or you are too lazy to search how to get it ;-) the way to do it is call
-:meth:`PyTango.Util.create_device` / :meth:`PyTango.Util.delete_device`.
-Here is an example of implementing a tango command on one of your devices that
-creates a device of some arbitrary class (the example assumes the tango commands
-'CreateDevice' and 'DeleteDevice' receive a parameter of type DevVarStringArray
-with two strings. No error processing was done on the code for simplicity sake)::
-
- class MyDevice(PyTango.Device_4Impl):
-
- def CreateDevice(self, pars):
- klass_name, dev_name = pars
- util = PyTango.Util.instance()
- util.create_device(klass_name, dev_name, alias=None, cb=None)
-
- def DeleteDevice(self, pars):
- klass_name, dev_name = pars
- util = PyTango.Util.instance()
- util.delete_device(klass_name, dev_name)
-
-An optional callback can be registered that will be executed after the device is
-registed in the tango database but before the actual device object is created and its
-init_device method is called. You can, for example, initialize some device properties
-here.
-
-Dynamic device from a known tango class
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you already have access to the :class:`PyTango.DeviceClass` object that
-corresponds to the tango class of the device to be created you can call directly
-the :meth:`PyTango.DeviceClass.create_device` / :meth:`PyTango.DeviceClass.delete_device`.
-For example, if you wish to create a clone of your device, you can create a
-tango command called Clone::
-
- class MyDevice(PyTango.Device_4Impl):
-
- def fill_new_device_properties(self, dev_name):
- prop_names = db.get_device_property_list(self.get_name(), "*")
- prop_values = db.get_device_property(self.get_name(), prop_names.value_string)
- db.put_device_property(dev_name, prop_values)
-
- # do the same for attributes...
- ...
-
- def Clone(self, dev_name):
- klass = self.get_device_class()
- klass.create_device(dev_name, alias=None, cb=self.fill_new_device_properties)
-
- def DeleteSibling(self, dev_name):
- klass = self.get_device_class()
- klass.delete_device(dev_name)
-
-Note that the cb parameter is optional. In the example it is given for
-demonstration purposes only.
-
-Dynamic attributes
-##################
-
-It is also possible to create dynamic attributes within a Python device server.
-There are several ways to create dynamic attributes. One of the way, is to
-create all the devices within a loop, then to create the dynamic attributes and
-finally to make all the devices available for the external world. In C++ device
-server, this is typically done within the <Device>Class::device_factory() method.
-In Python device server, this method is generic and the user does not have one.
-Nevertheless, this generic device_factory method calls a method named dyn_attr()
-allowing the user to create his dynamic attributes. It is simply necessary to
-re-define this method within your <Device>Class and to create the dynamic
-attribute within this method:
-
- ``dyn_attr(self, dev_list)``
-
- where dev_list is a list containing all the devices created by the
- generic device_factory() method.
-
-There is another point to be noted regarding dynamic attribute within Python
-device server. The Tango Python device server core checks that for each
-attribute it exists methods named <attribute_name>_read and/or
-<attribute_name>_write and/or is_<attribute_name>_allowed. Using dynamic
-attribute, it is not possible to define these methods because attributes name
-and number are known only at run-time.
-To address this issue, the Device_3Impl::add_attribute() method has a diferent
-signature for Python device server which is:
-
- ``add_attribute(self, attr, r_meth = None, w_meth = None, is_allo_meth = None)``
-
- attr is an instance of the Attr class, r_meth is the method which has to be
- executed with the attribute is read, w_meth is the method to be executed
- when the attribute is written and is_allo_meth is the method to be executed
- to implement the attribute state machine. The method passed here as argument
- as to be class method and not object method. Which argument you have to use
- depends on the type of the attribute (A WRITE attribute does not need a
- read method). Note, that depending on the number of argument you pass to this
- method, you may have to use Python keyword argument. The necessary methods
- required by the Tango Python device server core will be created automatically
- as a forward to the methods given as arguments.
-
-Mixing Tango classes (Python and C++) in a Python Tango device server
----------------------------------------------------------------------
-
-Within the same python interpreter, it is possible to mix several Tango classes.
-Here is an example of the main function of a device server with two Tango classes
-called IRMiror and PLC::
-
- import PyTango
- import sys
-
- if __name__ == '__main__':
- util = PyTango.Util(sys.argv)
- util.add_class(PLCClass, PLC, 'PLC')
- util.add_class(IRMirrorClass, IRMirror, 'IRMirror')
-
- U = PyTango.Util.instance()
- U.server_init()
- U.server_run()
-
-:Line 6: The Tango class PLC is registered in the device server
-:Line 7: The Tango class IRMirror is registered in the device server
-
-It is also possible to add C++ Tango class in a Python device server as soon as:
- 1. The Tango class is in a shared library
- 2. It exist a C function to create the Tango class
-
-For a Tango class called MyTgClass, the shared library has to be called
-MyTgClass.so and has to be in a directory listed in the LD_LIBRARY_PATH
-environment variable. The C function creating the Tango class has to be called
-_create_MyTgClass_class() and has to take one parameter of type "char \*" which
-is the Tango class name. Here is an example of the main function of the same
-device server than before but with one C++ Tango class called SerialLine::
-
- import PyTango
- import sys
-
- if __name__ == '__main__':
- py = PyTango.Util(sys.argv)
- util.add_class('SerialLine', 'SerialLine', language="c++")
- util.add_class(PLCClass, PLC, 'PLC')
- util.add_class(IRMirrorClass, IRMirror, 'IRMirror')
-
- U = PyTango.Util.instance()
- U.server_init()
- U.server_run()
-
-:Line 6: The C++ class is registered in the device server
-:Line 7 and 8: The two Python classes are registered in the device server
-Server API
-----------
-
-.. toctree::
- :maxdepth: 2
-
- server
- device
- device_class
- logging
- attribute
- util
diff --git a/doc/server/index.rst b/doc/howto.rst~
similarity index 72%
rename from doc/server/index.rst
rename to doc/howto.rst~
index d437c04..06001b5 100644
--- a/doc/server/index.rst
+++ b/doc/howto.rst~
@@ -2,37 +2,583 @@
.. highlight:: python
:linenothreshold: 3
-
-.. _server:
-
-Server API
-==========
+.. _pytango-howto:
+
+======
+How to
+======
+
+This is a small list of how-tos specific to PyTango. A more general Tango how-to
+list can be found `here <http://www.tango-controls.org/howtos>`_.
+
+
+Check the default TANGO host
+----------------------------
+
+The default TANGO host can be defined using the environment variable
+:envvar:`TANGO_HOST` or in a `tangorc` file.
+(see `Tango environment variables <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node11.html#SECTION0011123000000000000000>`_
+for complete information)
+To check what is the current value that TANGO uses for the default configuration
+simple do::
+
+ >>> import PyTango
+ >>> PyTango.ApiUtil.get_env_var("TANGO_HOST")
+ 'homer.simpson.com:10000'
+
+
+Work with Groups
+----------------
+
+.. todo::
+ write this how to
+
+Handle errors
+-------------
+
+.. todo::
+ write this how to
+
+Check TANGO version
+-------------------
+
+There are two library versions you might be interested in checking:
+The PyTango version::
+
+ >>> import PyTango
+ >>> PyTango.__version__
+ '8.1.1'
+
+ >>> PyTango.__version_info__
+ (8, 1, 1, 'final', 0)
+
+... and the Tango C++ library version that PyTango was compiled with::
+
+ >>> import PyTango
+ >>> PyTango.constants.TgLibVers
+ '8.1.2'
+
+
+Report a bug
+------------
+
+Bugs can be reported as tickets in `TANGO Source forge <https://sourceforge.net/p/tango-cs/bugs/>`_.
+
+When making a bug report don't forget to select *PyTango* in **Category**.
+
+It is also helpfull if you can put in the ticket description the PyTango information.
+It can be a dump of::
+
+ $ python -c "from PyTango.utils import info; print(info())"
+
+.. _pytango-howto-server:
+
+Write a server
+--------------
+
+Before reading this chapter you should be aware of the TANGO basic concepts.
This chapter does not explain what a Tango device or a device server is.
-This is explained in details in "The Tango control system manual" available at
-http://www.tango-controls.org/TangoKernel.
-The device server described in the following example is a Tango device server
-with one Tango class called *PyDsExp*. This class has two commands called
-*IOLong* and *IOStringArray* and two attributes called *Long_attr* and
-*Short_attr_rw*.
+This is explained in details in the
+`Tango control system manual <http://www.tango-controls.org/TangoKernel>`_
+
+Since version 8.1, PyTango provides a helper module which simplifies the
+development of a Tango device server. This helper is provided through the
+:mod:`PyTango.server` module.
+
+Here is a simple example on how to write a *Clock* device server using the
+high level API
+
+.. code-block:: python
+ :linenos:
+
+ import time
+ from PyTango.server import run
+ from PyTango.server import Device, DeviceMeta
+ from PyTango.server import attribute, command
+
+
+ class Clock(Device):
+ __metaclass__ = DeviceMeta
+
+ @attribute
+ def time(self):
+ return time.time()
+
+ @command(dtype_in=str, dtype_out=str)
+ def strftime(self, format):
+ return time.strftime(format)
+
+
+ if __name__ == "__main__":
+ run([Clock])
+
+
+**line 2-4**
+ import the necessary symbols
+
+**line 7**
+ tango device class definition. A Tango device must inherit from
+ :class:`PyTango.server.Device`
+
+**line 8**
+ mandatory *magic* line. A Tango device must define the metaclass as
+ :class:`PyTango.server.DeviceClass`. This has to be done due to a limitation
+ on boost-python
+
+**line 10**
+ definition of the *time* attribute. By default, attributes are double, scalar,
+ read-only. Check the :class:`~PyTango.server.attribute` for the complete
+ list of attribute options.
+
+**line 12-13**
+ the method that is called when a client reads the *time* attribute from this
+ device. By default, Tango expects a method called ``read_<attribute name>``
+ to exist for every attribute
+
+**line 15-17**
+ the method *strftime* is exported as a Tango command. In receives a string
+ as argument and it returns a string. If a method is to be exported as a
+ Tango command, it must be decorated as such with the
+ :func:`~PyTango.server.command` decorator
+
+**line 21**
+ start the Tango run loop. The mandatory argument is a list of python classes
+ that are to be exported as Tango classes. Check :func:`~PyTango.server.run`
+ for the complete list of options
+
+Here is a more complete example on how to write a *PowerSupply* device server
+using the high level API. The example contains:
+
+#. a read-only double scalar attribute called *voltage*
+#. a read/write double scalar expert attribute *current*
+#. a read-only double image attribute called *noise*
+#. a *ramp* command
+#. a *host* device property
+#. a *port* class property
+
+.. code-block:: python
+ :linenos:
+
+ from time import time
+ from numpy.random import random_sample
+
+ from PyTango import AttrQuality, AttrWriteType, DispLevel, run
+ from PyTango.server import Device, DeviceMeta, attribute, command
+ from PyTango.server import class_property, device_property
+
+
+ class PowerSupply(Device):
+ __metaclass__ = DeviceMeta
+
+ voltage = attribute()
+
+ current = attribute(label="Current", dtype=float,
+ display_level=DispLevel.EXPERT,
+ access=AttrWriteType.READ_WRITE,
+ unit="A", format="8.4f",
+ min_value=0.0, max_value=8.5,
+ min_alarm=0.1, max_alarm=8.4,
+ min_warning=0.5, max_warning=8.0,
+ fget="get_current", fset="set_current",
+ doc="the power supply current")
+
+ noise = attribute(label="Noise", dtype=((float,),),
+ max_dim_x=1024, max_dim_y=1024,
+ fget="get_noise")
+
+ host = device_property(dtype=str)
+ port = class_property(dtype=int, default_value=9788)
+
+ def read_voltage(self):
+ self.info_stream("get voltage(%s, %d)" % (self.host, self.port))
+ return 10.0
+
+ def get_current(self):
+ return 2.3456, time(), AttrQuality.ATTR_WARNING
+
+ def set_current(self, current):
+ print("Current set to %f" % current)
+
+ def get_noise(self):
+ return random_sample((1024, 1024))
+
+ @command(dtype_in=float)
+ def ramp(self, value):
+ print("Ramping up...")
+
+
+ if __name__ == "__main__":
+ run([PowerSupply])
+
+
+.. note::
+ the ``__metaclass__`` statement is mandatory due to a limitation in the
+ *boost-python* library used by PyTango.
+
+ If you are using python 3 you can write instead::
+
+ class PowerSupply(Device, metaclass=DeviceMeta)
+ pass
+
+.. _logging:
+
+Server logging
+--------------
+
+This chapter instructs you on how to use the tango logging API (log4tango) to
+create tango log messages on your device server.
+
+The logging system explained here is the Tango Logging Service (TLS). For
+detailed information on how this logging system works please check:
+
+ * `3.5 The tango logging service <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node4.html#sec:The-Tango-Logging>`_
+ * `9.3 The tango logging service <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node9.html#SECTION00930000000000000000>`_
+
+The easiest way to start seeing log messages on your device server console is
+by starting it with the verbose option. Example::
+
+ python PyDsExp.py PyDs1 -v4
+
+This activates the console tango logging target and filters messages with
+importance level DEBUG or more.
+The links above provided detailed information on how to configure log levels
+and log targets. In this document we will focus on how to write log messages on
+your device server.
+
+Basic logging
+~~~~~~~~~~~~~
+
+The most basic way to write a log message on your device is to use the
+:class:`~PyTango.server.Device` logging related methods:
+
+ * :meth:`~PyTango.server.Device.debug_stream`
+ * :meth:`~PyTango.server.Device.info_stream`
+ * :meth:`~PyTango.server.Device.warn_stream`
+ * :meth:`~PyTango.server.Device.error_stream`
+ * :meth:`~PyTango.server.Device.fatal_stream`
+
+Example::
+
+ def read_voltage(self):
+ self.info_stream("read voltage attribute")
+ # ...
+ return voltage_value
+
+This will print a message like::
+
+ 1282206864 [-1215867200] INFO test/power_supply/1 read voltage attribute
+
+every time a client asks to read the *voltage* attribute value.
+
+The logging methods support argument list feature (since PyTango 8.1). Example::
+
+ def read_voltage(self):
+ self.info_stream("read_voltage(%s, %d)", self.host, self.port)
+ # ...
+ return voltage_value
+
+
+Logging with print statement
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+*This feature is only possible since PyTango 7.1.3*
+
+It is possible to use the print statement to log messages into the tango logging
+system. This is achieved by using the python's print extend form sometimes
+refered to as *print chevron*.
+
+Same example as above, but now using *print chevron*::
+
+ def read_voltage(self, the_att):
+ print >>self.log_info, "read voltage attribute"
+ # ...
+ return voltage_value
+
+Or using the python 3k print function::
+
+ def read_Long_attr(self, the_att):
+ print("read voltage attribute", file=self.log_info)
+ # ...
+ return voltage_value
+
+
+Logging with decorators
+~~~~~~~~~~~~~~~~~~~~~~~
+
+*This feature is only possible since PyTango 7.1.3*
+
+PyTango provides a set of decorators that place automatic log messages when
+you enter and when you leave a python method. For example::
+
+ @PyTango.DebugIt()
+ def read_Long_attr(self, the_att):
+ the_att.set_value(self.attr_long)
+
+will generate a pair of log messages each time a client asks for the 'Long_attr'
+value. Your output would look something like::
+
+ 1282208997 [-1215965504] DEBUG test/pydsexp/1 -> read_Long_attr()
+ 1282208997 [-1215965504] DEBUG test/pydsexp/1 <- read_Long_attr()
+
+Decorators exist for all tango log levels:
+ * :class:`PyTango.DebugIt`
+ * :class:`PyTango.InfoIt`
+ * :class:`PyTango.WarnIt`
+ * :class:`PyTango.ErrorIt`
+ * :class:`PyTango.FatalIt`
+
+The decorators receive three optional arguments:
+ * show_args - shows method arguments in log message (defaults to False)
+ * show_kwargs shows keyword method arguments in log message (defaults to False)
+ * show_ret - shows return value in log message (defaults to False)
+
+Example::
+
+ @PyTango.DebugIt(show_args=True, show_ret=True)
+ def IOLong(self, in_data):
+ return in_data * 2
+
+will output something like::
+
+ 1282221947 [-1261438096] DEBUG test/pydsexp/1 -> IOLong(23)
+ 1282221947 [-1261438096] DEBUG test/pydsexp/1 46 <- IOLong()
+
+
+Multiple device classes (Python and C++) in a server
+----------------------------------------------------
+
+Within the same python interpreter, it is possible to mix several Tango classes.
+Let's say two of your colleagues programmed two separate Tango classes in two
+separated python files: A :class:`PLC` class in a :file:`PLC.py`::
+
+ # PLC.py
+
+ from PyTango.server import Device, DeviceMeta, run
+
+ class PLC(Device):
+ __metaclass__ = DeviceMeta
+
+ # bla, bla my PLC code
+
+ if __name__ == "__main__":
+ run([PLC])
+
+... and a :class:`IRMirror` in a :file:`IRMirror.py`::
+
+ # IRMirror.py
+
+ from PyTango.server import Device, DeviceMeta, run
+
+ class IRMirror(Device):
+ __metaclass__ = DeviceMeta
+
+ # bla, bla my IRMirror code
+
+ if __name__ == "__main__":
+ run([IRMirror])
+
+You want to create a Tango server called `PLCMirror` that is able to contain
+devices from both PLC and IRMirror classes. All you have to do is write
+a :file:`PLCMirror.py` containing the code::
+
+ # PLCMirror.py
+
+ from PyTango.server import run
+ from PLC import PLC
+ from IRMirror import IRMirror
+
+ run([PLC, IRMirror])
+
+It is also possible to add C++ Tango class in a Python device server as soon as:
+ 1. The Tango class is in a shared library
+ 2. It exist a C function to create the Tango class
+
+For a Tango class called MyTgClass, the shared library has to be called
+MyTgClass.so and has to be in a directory listed in the LD_LIBRARY_PATH
+environment variable. The C function creating the Tango class has to be called
+_create_MyTgClass_class() and has to take one parameter of type "char \*" which
+is the Tango class name. Here is an example of the main function of the same
+device server than before but with one C++ Tango class called SerialLine::
+
+ import PyTango
+ import sys
+
+ if __name__ == '__main__':
+ py = PyTango.Util(sys.argv)
+ util.add_class('SerialLine', 'SerialLine', language="c++")
+ util.add_class(PLCClass, PLC, 'PLC')
+ util.add_class(IRMirrorClass, IRMirror, 'IRMirror')
+
+ U = PyTango.Util.instance()
+ U.server_init()
+ U.server_run()
+
+:Line 6: The C++ class is registered in the device server
+:Line 7 and 8: The two Python classes are registered in the device server
+
+Create attributes dynamically
+-----------------------------
+
+It is also possible to create dynamic attributes within a Python device server.
+There are several ways to create dynamic attributes. One of the way, is to
+create all the devices within a loop, then to create the dynamic attributes and
+finally to make all the devices available for the external world. In C++ device
+server, this is typically done within the <Device>Class::device_factory() method.
+In Python device server, this method is generic and the user does not have one.
+Nevertheless, this generic device_factory method calls a method named dyn_attr()
+allowing the user to create his dynamic attributes. It is simply necessary to
+re-define this method within your <Device>Class and to create the dynamic
+attribute within this method:
+
+ ``dyn_attr(self, dev_list)``
+
+ where dev_list is a list containing all the devices created by the
+ generic device_factory() method.
+
+There is another point to be noted regarding dynamic attribute within Python
+device server. The Tango Python device server core checks that for each
+attribute it exists methods named <attribute_name>_read and/or
+<attribute_name>_write and/or is_<attribute_name>_allowed. Using dynamic
+attribute, it is not possible to define these methods because attributes name
+and number are known only at run-time.
+To address this issue, the Device_3Impl::add_attribute() method has a diferent
+signature for Python device server which is:
+
+ ``add_attribute(self, attr, r_meth = None, w_meth = None, is_allo_meth = None)``
+
+attr is an instance of the Attr class, r_meth is the method which has to be
+executed with the attribute is read, w_meth is the method to be executed
+when the attribute is written and is_allo_meth is the method to be executed
+to implement the attribute state machine. The method passed here as argument
+as to be class method and not object method. Which argument you have to use
+depends on the type of the attribute (A WRITE attribute does not need a
+read method). Note, that depending on the number of argument you pass to this
+method, you may have to use Python keyword argument. The necessary methods
+required by the Tango Python device server core will be created automatically
+as a forward to the methods given as arguments.
+
+Here is an example of a device which has a TANGO command called
+*createFloatAttribute*. When called, this command creates a new scalar floating
+point attribute with the specified name::
+
+
+ from PyTango import Util, Attr
+ from PyTango.server import DeviceMeta, Device, command
+
+ class MyDevice(Device):
+ __metaclass__ = DeviceMeta
+
+ @command(dtype_in=str)
+ def CreateFloatAttribute(self, attr_name):
+ attr = Attr(attr_name, PyTango.DevDouble)
+ self.add_attribute(attr, self.read_General, self.write_General)
+
+ def read_General(self, attr):
+ self.info_stream("Reading attribute %s", attr.get_name())
+ attr.set_value(99.99)
+
+ def write_General(self, attr):
+ self.info_stream("Writting attribute %s", attr.get_name())
+
+
+Create/Delete devices dynamically
+---------------------------------
+
+*This feature is only possible since PyTango 7.1.2*
+
+Starting from PyTango 7.1.2 it is possible to create devices in a device server
+"en caliente". This means that you can create a command in your "management device"
+of a device server that creates devices of (possibly) several other tango classes.
+There are two ways to create a new device which are described below.
+
+Tango imposes a limitation: the tango class(es) of the device(s) that is(are)
+to be created must have been registered before the server starts.
+If you use the high level API, the tango class(es) must be listed in the call
+to :func:`~PyTango.server.run`. If you use the lower level server API, it must
+be done using individual calls to :meth:`~PyTango.Util.add_class`.
+
+
+Dynamic device from a known tango class name
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you know the tango class name but you don't have access to the :class:`PyTango.DeviceClass`
+(or you are too lazy to search how to get it ;-) the way to do it is call
+:meth:`~PyTango.Util.create_device` / :meth:`~PyTango.Util.delete_device`.
+Here is an example of implementing a tango command on one of your devices that
+creates a device of some arbitrary class (the example assumes the tango commands
+'CreateDevice' and 'DeleteDevice' receive a parameter of type DevVarStringArray
+with two strings. No error processing was done on the code for simplicity sake)::
+
+ from PyTango import Util
+ from PyTango.server import DeviceMeta, Device, command
+
+ class MyDevice(Device):
+ __metaclass__ = DeviceMeta
+
+ @command(dtype_in=[str])
+ def CreateDevice(self, pars):
+ klass_name, dev_name = pars
+ util = Util.instance()
+ util.create_device(klass_name, dev_name, alias=None, cb=None)
+
+ @command(dtype_in=[str])
+ def DeleteDevice(self, pars):
+ klass_name, dev_name = pars
+ util = Util.instance()
+ util.delete_device(klass_name, dev_name)
+
+An optional callback can be registered that will be executed after the device is
+registed in the tango database but before the actual device object is created
+and its init_device method is called. It can be used, for example, to initialize
+some device properties.
+
+Dynamic device from a known tango class
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Importing python modules
-------------------------
+If you already have access to the :class:`~PyTango.DeviceClass` object that
+corresponds to the tango class of the device to be created you can call directly
+the :meth:`~PyTango.DeviceClass.create_device` / :meth:`~PyTango.DeviceClass.delete_device`.
+For example, if you wish to create a clone of your device, you can create a
+tango command called *Clone*::
-To write a Python script which is a Tango device server, you need to import
-two modules which are:
+ class MyDevice(PyTango.Device_4Impl):
+
+ def fill_new_device_properties(self, dev_name):
+ prop_names = db.get_device_property_list(self.get_name(), "*")
+ prop_values = db.get_device_property(self.get_name(), prop_names.value_string)
+ db.put_device_property(dev_name, prop_values)
+
+ # do the same for attributes...
+ ...
+
+ def Clone(self, dev_name):
+ klass = self.get_device_class()
+ klass.create_device(dev_name, alias=None, cb=self.fill_new_device_properties)
+
+ def DeleteSibling(self, dev_name):
+ klass = self.get_device_class()
+ klass.delete_device(dev_name)
+
+Note that the cb parameter is optional. In the example it is given for
+demonstration purposes only.
-1. The :mod:`PyTango` module which is the Python to C++ interface
-2. The Python classical :mod:`sys` module
+.. _server:
-This could be done with code like (supposing the PYTHONPATH environment variable
-is correctly set)::
+Write a server (original API)
+-----------------------------
- import PyTango
- import sys
+This chapter describes how to develop a PyTango device server using the
+original PyTango server API. This API mimics the C++ API and is considered
+low level.
+You should write a server using this API if you are using code generated by
+`Pogo tool <http://www.esrf.eu/computing/cs/tango/tango_doc/tools_doc/pogo_doc/index.html>`_
+or if for some reason the high level API helper doesn't provide a feature
+you need (in that case think of writing a mail to tango mailing list explaining
+what you cannot do).
The main part of a Python device server
----------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The rule of this part of a Tango device server is to:
@@ -68,7 +614,7 @@ The following is a typical code for this main function::
Run the device server loop
The PyDsExpClass class in Python
---------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The rule of this class is to :
@@ -122,7 +668,7 @@ constructor. An example of such a contructor is ::
The device type is set at line 3.
Defining commands
------------------
+~~~~~~~~~~~~~~~~~
As shown in the previous example, commands have to be defined in a :class:`dict`
called *cmd_list* as a data member of the xxxClass class of the Tango class.
@@ -152,7 +698,7 @@ this :class:`dict` are summarized in the following array:
+-------------------+----------------------+------------------------------------------+
Defining attributes
--------------------
+~~~~~~~~~~~~~~~~~~~
As shown in the previous example, attributes have to be defined in a :class:`dict`
called **attr_list** as a data
@@ -217,7 +763,7 @@ array:
+-------------------+-----------------------------------+------------------------------------------+
The PyDsExp class in Python
----------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
The rule of this class is to implement methods executed by commands and attributes.
In our example, the code of this class looks like::
@@ -480,284 +1026,4 @@ write the Short_attr_rw attribute::
store the value written in the device object. Our attribute is a scalar
short attribute so the return value is an int
-.. _logging:
-
-Logging
-#######
-
-This chapter instructs you on how to use the tango logging API (log4tango) to
-create tango log messages on your device server.
-
-The logging system explained here is the Tango Logging Service (TLS). For
-detailed information on how this logging system works please check:
-
- * `3.5 The tango logging service <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node4.html#sec:The-Tango-Logging>`_
- * `9.3 The tango logging service <http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/ds_prog/node9.html#SECTION00930000000000000000>`_
-
-The easiest way to start seeing log messages on your device server console is
-by starting it with the verbose option. Example::
-
- python PyDsExp.py PyDs1 -v4
-
-This activates the console tango logging target and filters messages with
-importance level DEBUG or more.
-The links above provided detailed information on how to configure log levels
-and log targets. In this document we will focus on how to write log messages on
-your device server.
-
-Basic logging
-~~~~~~~~~~~~~
-
-The most basic way to write a log message on your device is to use the
-:class:`PyTango.DeviceImpl` logging related methods:
-
- * :meth:`PyTango.DeviceImpl.debug_stream`
- * :meth:`PyTango.DeviceImpl.info_stream`
- * :meth:`PyTango.DeviceImpl.warn_stream`
- * :meth:`PyTango.DeviceImpl.error_stream`
- * :meth:`PyTango.DeviceImpl.fatal_stream`
-
-Example::
-
- def read_Long_attr(self, the_att):
- self.info_stream("read attribute name Long_attr")
- the_att.set_value(self.attr_long)
-
-This will print a message like::
-
- 1282206864 [-1215867200] INFO test/pydsexp/1 read attribute name Long_attr
-
-every time a client asks to read the 'Long_attr' attribute value.
-
-Logging with print statement
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-*This feature is only possible since PyTango 7.1.3*
-
-It is possible to use the print statement to log messages into the tango logging
-system. This is achieved by using the python's print extend form sometimes
-refered to as *print chevron*.
-
-Same example as above, but now using *print chevron*::
-
- def read_Long_attr(self, the_att):
- print >>self.log_info, "read attribute name Long_attr"
- the_att.set_value(self.attr_long)
-
-Or using the python 3k print function::
-
- def read_Long_attr(self, the_att):
- print("read attribute name Long_attr", file=self.log_info)
- the_att.set_value(self.attr_long)
-
-Logging with decorators
-~~~~~~~~~~~~~~~~~~~~~~~
-
-*This feature is only possible since PyTango 7.1.3*
-
-PyTango provides a set of decorators that place automatic log messages when
-you enter and when you leave a python method. For example::
-
- @PyTango.DebugIt()
- def read_Long_attr(self, the_att):
- the_att.set_value(self.attr_long)
-
-will generate a pair of log messages each time a client asks for the 'Long_attr'
-value. Your output would look something like::
-
- 1282208997 [-1215965504] DEBUG test/pydsexp/1 -> read_Long_attr()
- 1282208997 [-1215965504] DEBUG test/pydsexp/1 <- read_Long_attr()
-
-Decorators exist for all tango log levels:
- * :class:`PyTango.DebugIt`
- * :class:`PyTango.InfoIt`
- * :class:`PyTango.WarnIt`
- * :class:`PyTango.ErrorIt`
- * :class:`PyTango.FatalIt`
-
-The decorators receive three optional arguments:
- * show_args - shows method arguments in log message (defaults to False)
- * show_kwargs shows keyword method arguments in log message (defaults to False)
- * show_ret - shows return value in log message (defaults to False)
-
-Example::
-
- @PyTango.DebugIt(show_args=True, show_ret=True)
- def IOLong(self, in_data):
- return in_data * 2
-
-will output something like::
-
- 1282221947 [-1261438096] DEBUG test/pydsexp/1 -> IOLong(23)
- 1282221947 [-1261438096] DEBUG test/pydsexp/1 46 <- IOLong()
-
-Dynamic devices
-###############
-
-*This feature is only possible since PyTango 7.1.2*
-
-Starting from PyTango 7.1.2 it is possible to create devices in a device server
-"en caliente". This means that you can create a command in your "management device"
-of a device server that creates devices of (possibly) several other tango classes.
-There are two ways to create a new device which are described below.
-
-Dynamic device from a known tango class name
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you know the tango class name but you don't have access to the :class:`PyTango.DeviceClass`
-(or you are too lazy to search how to get it ;-) the way to do it is call
-:meth:`PyTango.Util.create_device` / :meth:`PyTango.Util.delete_device`.
-Here is an example of implementing a tango command on one of your devices that
-creates a device of some arbitrary class (the example assumes the tango commands
-'CreateDevice' and 'DeleteDevice' receive a parameter of type DevVarStringArray
-with two strings. No error processing was done on the code for simplicity sake)::
-
- class MyDevice(PyTango.Device_4Impl):
-
- def CreateDevice(self, pars):
- klass_name, dev_name = pars
- util = PyTango.Util.instance()
- util.create_device(klass_name, dev_name, alias=None, cb=None)
-
- def DeleteDevice(self, pars):
- klass_name, dev_name = pars
- util = PyTango.Util.instance()
- util.delete_device(klass_name, dev_name)
-
-An optional callback can be registered that will be executed after the device is
-registed in the tango database but before the actual device object is created and its
-init_device method is called. You can, for example, initialize some device properties
-here.
-
-Dynamic device from a known tango class
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If you already have access to the :class:`PyTango.DeviceClass` object that
-corresponds to the tango class of the device to be created you can call directly
-the :meth:`PyTango.DeviceClass.create_device` / :meth:`PyTango.DeviceClass.delete_device`.
-For example, if you wish to create a clone of your device, you can create a
-tango command called Clone::
-
- class MyDevice(PyTango.Device_4Impl):
-
- def fill_new_device_properties(self, dev_name):
- prop_names = db.get_device_property_list(self.get_name(), "*")
- prop_values = db.get_device_property(self.get_name(), prop_names.value_string)
- db.put_device_property(dev_name, prop_values)
-
- # do the same for attributes...
- ...
-
- def Clone(self, dev_name):
- klass = self.get_device_class()
- klass.create_device(dev_name, alias=None, cb=self.fill_new_device_properties)
-
- def DeleteSibling(self, dev_name):
- klass = self.get_device_class()
- klass.delete_device(dev_name)
-
-Note that the cb parameter is optional. In the example it is given for
-demonstration purposes only.
-
-Dynamic attributes
-##################
-
-It is also possible to create dynamic attributes within a Python device server.
-There are several ways to create dynamic attributes. One of the way, is to
-create all the devices within a loop, then to create the dynamic attributes and
-finally to make all the devices available for the external world. In C++ device
-server, this is typically done within the <Device>Class::device_factory() method.
-In Python device server, this method is generic and the user does not have one.
-Nevertheless, this generic device_factory method calls a method named dyn_attr()
-allowing the user to create his dynamic attributes. It is simply necessary to
-re-define this method within your <Device>Class and to create the dynamic
-attribute within this method:
-
- ``dyn_attr(self, dev_list)``
-
- where dev_list is a list containing all the devices created by the
- generic device_factory() method.
-
-There is another point to be noted regarding dynamic attribute within Python
-device server. The Tango Python device server core checks that for each
-attribute it exists methods named <attribute_name>_read and/or
-<attribute_name>_write and/or is_<attribute_name>_allowed. Using dynamic
-attribute, it is not possible to define these methods because attributes name
-and number are known only at run-time.
-To address this issue, the Device_3Impl::add_attribute() method has a diferent
-signature for Python device server which is:
-
- ``add_attribute(self, attr, r_meth = None, w_meth = None, is_allo_meth = None)``
-
- attr is an instance of the Attr class, r_meth is the method which has to be
- executed with the attribute is read, w_meth is the method to be executed
- when the attribute is written and is_allo_meth is the method to be executed
- to implement the attribute state machine. The method passed here as argument
- as to be class method and not object method. Which argument you have to use
- depends on the type of the attribute (A WRITE attribute does not need a
- read method). Note, that depending on the number of argument you pass to this
- method, you may have to use Python keyword argument. The necessary methods
- required by the Tango Python device server core will be created automatically
- as a forward to the methods given as arguments.
-
-Mixing Tango classes (Python and C++) in a Python Tango device server
----------------------------------------------------------------------
-
-Within the same python interpreter, it is possible to mix several Tango classes.
-Here is an example of the main function of a device server with two Tango classes
-called IRMiror and PLC::
-
- import PyTango
- import sys
-
- if __name__ == '__main__':
- util = PyTango.Util(sys.argv)
- util.add_class(PLCClass, PLC, 'PLC')
- util.add_class(IRMirrorClass, IRMirror, 'IRMirror')
-
- U = PyTango.Util.instance()
- U.server_init()
- U.server_run()
-
-:Line 6: The Tango class PLC is registered in the device server
-:Line 7: The Tango class IRMirror is registered in the device server
-
-It is also possible to add C++ Tango class in a Python device server as soon as:
- 1. The Tango class is in a shared library
- 2. It exist a C function to create the Tango class
-
-For a Tango class called MyTgClass, the shared library has to be called
-MyTgClass.so and has to be in a directory listed in the LD_LIBRARY_PATH
-environment variable. The C function creating the Tango class has to be called
-_create_MyTgClass_class() and has to take one parameter of type "char \*" which
-is the Tango class name. Here is an example of the main function of the same
-device server than before but with one C++ Tango class called SerialLine::
-
- import PyTango
- import sys
-
- if __name__ == '__main__':
- py = PyTango.Util(sys.argv)
- util.add_class('SerialLine', 'SerialLine', language="c++")
- util.add_class(PLCClass, PLC, 'PLC')
- util.add_class(IRMirrorClass, IRMirror, 'IRMirror')
-
- U = PyTango.Util.instance()
- U.server_init()
- U.server_run()
-
-:Line 6: The C++ class is registered in the device server
-:Line 7 and 8: The two Python classes are registered in the device server
-Server API
-----------
-
-.. toctree::
- :maxdepth: 2
-
- server
- device
- device_class
- logging
- attribute
- util
diff --git a/doc/itango/highlights.rst b/doc/itango.rst
similarity index 91%
rename from doc/itango/highlights.rst
rename to doc/itango.rst
index 02575e8..97931c1 100644
--- a/doc/itango/highlights.rst
+++ b/doc/itango.rst
@@ -1,13 +1,81 @@
+.. highlight:: python
+ :linenothreshold: 4
+
+.. _itango:
+
+======
+ITango
+======
+
+ITango is a PyTango CLI based on IPython_. It is designed to be used as an
+IPython profile.
+
+ITango is available since PyTango 7.1.2
+
+You can start ITango by typing on the command line::
+
+ $ itango
+
+or the equivalent::
+
+ $ ipython --profile=tango
+
+and you should get something like this:
+
+.. image:: _static/itango00.png
+ :align: center
+ :width: 75%
+
+
+.. _itango-features:
+
+Features
+--------
+
+ITango works like a normal python console, but it gives you in addition a nice
+set of features from IPython_ like:
+
+ - proper (bash-like) command completion
+ - automatic expansion of python variables, functions, types
+ - command history (with up/down arrow keys, %hist command)
+ - help system ( object? syntax, help(object))
+ - persistently store your favorite variables
+ - color modes
+
+(for a complete list checkout the `IPython web page <http://ipython.org/>`_)
+
+Plus an additional set o Tango_ specific features:
+
+ - automatic import of Tango objects to the console namespace (:mod:`PyTango`
+ module, :class:`~PyTango.DeviceProxy` (=Device),
+ :class:`~PyTango.Database`, :class:`~PyTango.Group`
+ and :class:`~PyTango.AttributeProxy` (=Attribute))
+ - device name completion
+ - attribute name completion
+ - automatic tango object member completion
+ - list tango devices, classes, servers
+ - customized tango error message
+ - tango error introspection
+ - switch database
+ - refresh database
+ - list tango devices, classes
+ - store favorite tango objects
+ - store favorite tango devices
+ - tango color modes
+
+Check the :ref:`itango-highlights` to see how to put these feature to good use
+:-)
+
.. currentmodule:: PyTango
.. _itango-highlights:
Highlights
-==========
+----------
Tab completion
---------------
+~~~~~~~~~~~~~~
ITango exports many tango specific objects to the console namespace.
These include:
@@ -40,7 +108,7 @@ These include:
ITango [4]: Att<tab>
Attribute AttributeError AttributeProxy
- - The tango :class:`PyTango.Database` object to which the itango session is
+ - The Tango :class:`Database` object to which the itango session is
currently connected
.. sourcecode:: itango
@@ -49,7 +117,7 @@ These include:
Result [1]: Database(homer, 10000)
Device name completion
-----------------------
+~~~~~~~~~~~~~~~~~~~~~~
ITango knows the complete list of device names (including alias) for the current
tango database. This means that when you try to create a new Device, by pressing
@@ -66,7 +134,7 @@ tango database. This means that when you try to create a new Device, by pressing
ITango [2]: test = Device("sys/tg_test/1")
Attribute name completion
--------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~
ITango can inspect the list of attributes in case the device server for the device
where the attribute resides is running.
@@ -113,7 +181,7 @@ where the attribute resides is running.
w_value = 0]
Automatic tango object member completion
-----------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When you create a new tango object, (ex.: a device), itango is able to find out
dynamically which are the members of this device (including tango commands
@@ -153,7 +221,7 @@ and attributes if the device is currently running)
Result [3]: 56.433
Tango classes as :class:`DeviceProxy`
----------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ITango exports all known tango classes as python alias to :class:`DeviceProxy`.
This way, if you want to create a device of class which you already know
@@ -187,7 +255,7 @@ class 'Libera' will show up as possible completions.
ITango [1]: bpm1 = Libera("BO01/DI/BPM-01")
Customized device representation
---------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When you use ipython >= 0.11 with a Qt console frontend::
@@ -197,7 +265,7 @@ typing a variable containing a tango device object followend by :kbd:`Enter`
will present you with a customized representation of the object instead of the
usual :func:`repr` :
- .. image:: itango06.png
+ .. image:: _static/itango06.png
You can customize the icon that itango displays for a specific device.
The first thing to do is to copy the image file into
@@ -225,7 +293,7 @@ the class property value, if defined)::
List tango devices, classes, servers
---------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ITango provides a set of magic functions (ipython lingo) that allow you to check
for the list tango devices, classes and servers which are registered in the
@@ -272,7 +340,7 @@ current database.
MacroServer/tcoutinho Simulator/BL99
Customized tango error message and introspection
-----------------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ITango intercepts tango exceptions that occur when you do tango operations
(ex.: write an attribute with a value outside the allowed limits) and tries to
@@ -303,7 +371,7 @@ the magic command 'tango_error'.
severity = PyTango._PyTango.ErrSeverity.ERR]]
Switching database
----------------------
+~~~~~~~~~~~~~~~~~~
You can switch database simply by executing the 'switchdb <host> [<port>]' magic
command.
@@ -339,7 +407,7 @@ command.
Database(marge, 10005)
Refreshing the database
---------------------------
+~~~~~~~~~~~~~~~~~~~~~~~
When itango starts up or when the database is switched, a query is made to the
tango Database device server which provides all necessary data. This
@@ -355,7 +423,7 @@ all tango information from the database.
ITango [1]: refreshdb
Storing your favorite tango objects for later usage
--------------------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. note::
This feature is not available if you have installed IPython 0.11!
@@ -384,7 +452,7 @@ then store these for the next time you startup IPython_ with itango profile.
DeviceProxy(motor/bl99/1)
Adding itango to your own ipython profile
---------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Adding itango to the ipython default profile
##################################################
@@ -581,10 +649,10 @@ Then start your CLI with::
and you will have something like this
-.. image:: itango02.png
+.. image:: _static/itango02.png
Advanced event monitoring
--------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~
.. note::
This chapter has a pending update. The contents only apply to
diff --git a/doc/itango/features.rst b/doc/itango/features.rst
deleted file mode 100644
index 8a5b66f..0000000
--- a/doc/itango/features.rst
+++ /dev/null
@@ -1,40 +0,0 @@
-
-.. _itango-features:
-
-Features
-========
-
-ITango works like a normal python console, but it gives you in addition a nice
-set of features from IPython_ like:
-
- - proper (bash-like) command completion
- - automatic expansion of python variables, functions, types
- - command history (with up/down arrow keys, %hist command)
- - help system ( object? syntax, help(object))
- - persistently store your favorite variables
- - color modes
-
-(for a complete list checkout the `IPython web page <http://ipython.org/>`_)
-
-Plus an additional set o Tango_ specific features:
-
- - automatic import of Tango objects to the console namespace (:mod:`PyTango`
- module, :class:`~PyTango.DeviceProxy` (=Device),
- :class:`~PyTango.Database`, :class:`~PyTango.Group`
- and :class:`~PyTango.AttributeProxy` (=Attribute))
- - device name completion
- - attribute name completion
- - automatic tango object member completion
- - list tango devices, classes, servers
- - customized tango error message
- - tango error introspection
- - switch database
- - refresh database
- - list tango devices, classes
- - store favorite tango objects
- - store favorite tango devices
- - tango color modes
-
-Check the :ref:`itango-highlights` to see how to put these feature to good use
-:-)
-
diff --git a/doc/itango/index.rst b/doc/itango/index.rst
deleted file mode 100644
index 25f123b..0000000
--- a/doc/itango/index.rst
+++ /dev/null
@@ -1,32 +0,0 @@
-.. highlight:: python
- :linenothreshold: 4
-
-.. _itango:
-
-ITango
-======
-
-ITango is a PyTango CLI based on IPython_. It is designed to be used as an
-IPython profile.
-
-ITango is available since PyTango 7.1.2
-
-You can start ITango by typing on the command line::
-
- $ itango
-
-or the equivalent::
-
- $ ipython --profile=tango
-
-and you should get something like this:
-
-.. image:: itango00.png
- :align: center
- :width: 75%
-
-.. toctree::
- :maxdepth: 1
-
- features
- highlights
diff --git a/doc/quicktour.rst b/doc/quicktour.rst
index 1ed0cbc..cd346a2 100644
--- a/doc/quicktour.rst
+++ b/doc/quicktour.rst
@@ -1,502 +1,251 @@
-.. _quick-tour:
+.. _pytango-quick-tour:
Quick tour
-============
+==========
This quick tour will guide you through the first steps on using PyTango.
-This is the new quick tour guide based on the :ref:`itango` console.
-You can still find the old version of this tour based on a simple python
-console :ref:`here <quick-tour-old>`.
-Quick tour on the client side
------------------------------
+Fundamental TANGO concepts
+--------------------------
-Check PyTango version
-~~~~~~~~~~~~~~~~~~~~~
+Before you begin there are some fundamental TANGO concepts you should be aware of.
-Start an IPython_ tango console with::
+Tango consists basically of a set of **devices** running somewhere on the network.
- $ itango
+A device is identified by a unique case insensitive name in the format
+*<domain>/<family>/<member>*. Examples: `LAB-01/PowerSupply/01`,
+`ID21/OpticsHutch/energy`.
-and type:
+Each device has a series of *attributes*, *properties* and *commands*.
- .. sourcecode:: itango
+An attribute is identified by a name in a device. It has a value that can
+be read. Some attributes can also be changed (read-write attributes).
- ITango [1]: PyTango.__version__
- Result [1]: '8.0.0'
+A property is identified by a name in a device. Usually, devices properties are
+used to provide a way to configure a device.
- ITango [2]: PyTango.__version_long__
- Result [2]: '8.0.0dev0'
+A command is also identified by a name. A command may or not receive a parameter
+and may or not return a value when it is executed.
- ITango [3]: PyTango.__version_number__
- Result [3]: 800
+Any device has **at least** a *State* and *Status* attributes and *State*,
+*Status* and *Init* commands. Reading the *State* or *Status* attributes has
+the same effect as executing the *State* or *Status* commands.
- ITango [4]: PyTango.__version_description__
- Result [4]: 'This version implements the C++ Tango 8.0 API.'
+Each device as an associated *TANGO Class*. Most of the times the TANGO class
+has the same name as the object oriented programming class which implements it
+but that is not mandatory.
-or alternatively:
+TANGO devices *live* inside a operating system process called *TANGO Device Server*.
+This server acts as a container of devices. A device server can host multiple
+devices of multiple TANGO classes. Devices are, therefore, only accessible when
+the corresponding TANGO Device Server is running.
- .. sourcecode:: itango
+A special TANGO device server called the *TANGO Database Server* will act as
+a naming service between TANGO servers and clients. This server has a known
+address where it can be reached. The machines that run TANGO Device Servers
+and/or TANGO clients, should export an environment variable called
+:envvar:`TANGO_HOST` that points to the TANGO Database server address. Example:
+``TANGO_HOST=homer.lab.eu:10000``
- ITango [1]: PyTango.Release.version
- Result [1]: '8.0.0'
+Minimum setup
+-------------
- ITango [2]: PyTango.Release.version_long
- Result [2]: '8.0.0dev0'
+This chapter assumes you have already installed PyTango.
- ITango [3]: PyTango.Release.version_number
- Result [3]: 800
+To explore PyTango you should have a running Tango system. If you are working in
+a facility/institute that uses Tango, this has probably already been prepared
+for you. You need to ask your facility/institute tango contact for the
+:envvar:`TANGO_HOST` variable where Tango system is running.
- ITango [4]: PyTango.Release.version_description
- Result [4]: 'This version implements the C++ Tango 8.0 API.'
+If you are working in an isolate machine you first need to make sure the Tango
+system is installed and running (`Tango howtos <http://www.tango-controls.org/howtos>`_).
-.. tip::
+Most examples here connect to a device called *sys/tg_test/1* that runs in a
+TANGO server called *TangoTest* with the instance name *test*.
+This server comes with the TANGO installation. The TANGO installation
+also registers the *test* instance. All you have to do is start the TangoTest
+server on a console::
- When typing, try pressing <tab>. Since ITango has autocomplete embedded you
- should get a list of possible completions. Example::
-
- PyTango.Release.<tab>
-
- Should get a list of all members of :class:`PyTango.Release` class.
+ $ TangoTest test
+ Ready to accept request
-Check Tango C++ version
-~~~~~~~~~~~~~~~~~~~~~~~
+.. note::
+ if you receive a message saying that the server is already running,
+ it just means that somebody has already started the test server so you don't
+ need to do anything.
+
+Client
+------
+
+Finally you can get your hands dirty. The first thing to do is start a python
+console and import the :mod:`PyTango` module. The following example shows
+how to create a proxy to an existing TANGO device, how to read and write
+attributes and execute commands from a python console::
+
+ >>> import PyTango
+
+ >>> # create a device object
+ >>> test_device = PyTango.DeviceProxy("sys/tg_test/1")
+
+ >>> # every device has a state and status which can be checked with:
+ >>> print(test_device.state())
+ RUNNING
+
+ >>> print(test_device.status())
+ The device is in RUNNING state.
+
+ >>> # this device has an attribute called "long_scalar". Let's see which value it has...
+ >>> data = test_device.read_attribute("long_scalar")
+
+ >>> # ...PyTango provides a shortcut to do the same:
+ >>> data = test_device["long_scalar"]
+
+ >>> # the result of reading an attribute is a DeviceAttribute python object.
+ >>> # It has a member called "value" which contains the value of the attribute
+ >>> data.value
+ 136
-From a client (This is only possible since PyTango 7.0.0)
+ >>> # Check the complete DeviceAttribute members:
+ >>> print(data)
+ DeviceAttribute[
+ data_format = SCALAR
+ dim_x = 1
+ dim_y = 0
+ has_failed = False
+ is_empty = False
+ name = 'long_scalar'
+ nb_read = 1
+ nb_written = 1
+ quality = ATTR_VALID
+ r_dimension = AttributeDimension(dim_x = 1, dim_y = 0)
+ time = TimeVal(tv_nsec = 0, tv_sec = 1399450183, tv_usec = 323990)
+ type = DevLong
+ value = 136
+ w_dim_x = 1
+ w_dim_y = 0
+ w_dimension = AttributeDimension(dim_x = 1, dim_y = 0)
+ w_value = 0]
- .. sourcecode:: itango
+ >>> # PyTango provides a handy pythonic shortcut to read the attribute value:
+ >>> test_device.long_scalar
+ 136
- ITango [1]: import PyTango.constants
+ >>> # Setting an attribute value is equally easy:
+ >>> test_device.write_attribute("long_scalar", 8776)
- ITango [2]: PyTango.constants.TgLibVers
- Result [2]: '8.0.0'
+ >>> # ... and a handy shortcut to do the same exists as well:
+ >>> test_device.long_scalar = 8776
-From a server you can alternatively do::
-
- u = PyTango.Util.instance()
- tg_cpp_lib_ver = u.get_tango_lib_release()
-
+ >>> # TangoTest has a command called "DevDouble" which receives a number
+ >>> # as parameter and returns the same number as a result. Let's
+ >>> # execute this command:
+ >>> test_device.command_inout("DevDouble", 45.67)
+ 45.67
-Test the connection to the Device and get it's current state
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ >>> # PyTango provides a handy shortcut: it exports commands as device methods:
+ >>> test_device.DevDouble(45.67)
+ 45.67
-One of the most basic examples is to get a reference to a device and
-determine if it is running or not.
+ >>> # Introspection: check the list of attributes:
+ >>> test_device.get_attribute_list()
+ ['ampli', 'boolean_scalar', 'double_scalar', '...', 'State', 'Status']
+
+ >>>
- .. sourcecode:: itango
-
- ITango [1]: # What is a DeviceProxy, really?
- ITango [1]: DeviceProxy?
- DeviceProxy is the high level Tango object which provides the client with
- an easy-to-use interface to TANGO devices. DeviceProxy provides interfaces
- to all TANGO Device interfaces.The DeviceProxy manages timeouts, stateless
- connections and reconnection if the device server is restarted. To create
- a DeviceProxy, a Tango Device name must be set in the object constructor.
+This is just the tip of the iceberg. Check the :class:`~PyTango.DeviceProxy` for
+the complete API.
- Example :
- dev = PyTango.DeviceProxy("sys/tg_test/1")
-
- ITango [2]: tangotest = DeviceProxy("sys/tg_test/1")
+PyTango comes with an integrated IPython_ based console called :ref:`itango`.
+It provides helpers to simplify console usage. You can use this console instead
+of the traditional python console. Be aware, though, that many of the *tricks*
+you can do in an :ref:`itango` console cannot be done in a python program.
- ITango [3]: # ping it
- ITango [4]: tangotest.ping()
- Result [4]: 110
+Server
+------
- ITango [5]: # Lets test the state
- ITango [6]: tangotest.state()
- Result [6]: PyTango._PyTango.DevState.RUNNING
+Since PyTango 8.1 it has become much easier to program a Tango device server.
+PyTango provides some helpers that allow developers to simplify the programming
+of a Tango device server.
- ITango [7]: # And now the status
- ITango [8]: tangotest.status()
- Result [8]: 'The device is in RUNNING state.'
+Before creating a server you need to decide:
-.. note::
+1. The name of the device server (example: `PowerSupplyDS`). This will be
+ the mandatory name of your python file.
+2. The Tango Class name of your device (example: `PowerSupply`). In our
+ example we will use the same name as the python class name.
+3. the list of attributes of the device, their data type, access (read-only vs
+ read-write), data_format (scalar, 1D, 2D)
+4. the list of commands, their parameters and their result
- Did you notice that you didn't write `PyTango.DeviceProxy` but instead just
- `DeviceProxy` ? This is because :ref:`itango` automatically exports the
- :class:`~PyTango.DeviceProxy`, :class:`~PyTango.AttributeProxy`,
- :class:`~PyTango.Database` and :class:`~PyTango.Group` classes to the
- namespace. If you are writting code outside :ref:`itango` you **MUST**
- use the `PyTango` module prefix.
+In our example we will write a fake power supply device server. The server
+will be called `PowerSupplyDS`. There will be a class called `PowerSupply`
+which will have attributes:
-.. tip::
+* *voltage* (scalar, read-only, numeric)
+* *current* (scalar, read_write, numeric, expert mode)
+* *noise* (2D, read-only, numeric)
- When typing the device name in the :class:`~PyTango.DeviceProxy` creation
- line, try pressing the <tab> key. You should get a list of devices::
-
- tangotest = DeviceProxy("sys<tab>
-
- Better yet (and since the Tango Class of 'sys/tg_test/1' is 'TangoTest'),
- try doing::
-
- tangotest = TangoTest("<tab>
+commands:
- Now the list of devices should be reduced to the ones that belong to the
- 'TangoTest' class. Note that TangoTest only works in ITango. If you are
- writting code outside :ref:`itango` you **MUST** use
- :class:`PyTango.DeviceProxy` instead.
-
-Execute commands with scalar arguments on a Device
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-As you can see in the following example, when scalar types are used, PyTango
-automatically manages the data types, and writing scripts is quite easy.
+* *TurnOn* (argument: None, result: None)
+* *TurnOff* (argument: None, result: None)
+* *Ramp* (param: scalar, numeric; result: bool)
- .. sourcecode:: itango
-
- ITango [1]: tangotest = TangoTest("sys/tg_test/1")
+properties:
- ITango [2]: # classical way
- ITango [2]: r = tangotest.command_inout("DevString", "Hello, world!")
+* *host* (string representing the host name of the actual power supply)
+* *port* (port number in the host with default value = 9788)
- ITango [3]: print "Result of execution of DevString command =", r
- Result of execution of DevString command = Hello, world!
+Here is the code for the :file:`PowerSupplyDS.py`
- ITango [4]: # 'pythonic' way
- ITango [5]: tangotest.DevString("Hello, world!")
- Result [5]: 'Hello, world!'
-
- ITango [6]: # type is automatically managed by PyTango
- ITango [7]: tangotest.DevULong(12456)
- Result [7]: 12456
+.. literalinclude:: _static/PowerSupplyDS.py
+ :linenos:
-Execute commands with more complex types
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Check the :ref:`high level server API <pytango-hlapi>` for the complete
+reference API. The :ref:`write a server how to <pytango-howto-server>` can help
+as well.
-In this case you have to use put your arguments data in the correct python
-structures.
+Before running this brand new server we need to register it in the Tango system.
+You can do it with Jive (`Jive->Edit->Create server`):
- .. sourcecode:: itango
-
- ITango [1]: tangotest = TangoTest("sys/tg_test/1")
-
- ITango [2]: argin = [1, 2, 3], ["Hello", "World"]
-
- ITango [3]: tango_test.DevVarLongArray(argin)
- Result [3]: [array([1, 2, 3]), ['Hello', 'World']]
-
-.. note::
- notice that the command returns a list of two elements. The first element is
- a :class:`numpy.ndarray` (assuming PyTango is compiled with numpy_ support).
- This is because PyTango does a best effort to convert all numeric array types
- to numpy_ arrays.
-
-Reading and writing attributes
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. image:: _static/jive_powersupply.png
-Basic read/write attribute operations.
+... or in a python script::
- .. sourcecode:: itango
-
- ITango [1]: # Read a scalar attribute
- ITango [2]: print tangotest.read_attribute("long_scalar")
- DeviceAttribute[
- data_format = PyTango._PyTango.AttrDataFormat.SCALAR
- dim_x = 1
- dim_y = 0
- has_failed = False
- is_empty = False
- name = 'long_scalar'
- nb_read = 1
- nb_written = 1
- quality = PyTango._PyTango.AttrQuality.ATTR_VALID
- r_dimension = AttributeDimension(dim_x = 1, dim_y = 0)
- time = TimeVal(tv_nsec = 0, tv_sec = 1281084943, tv_usec = 461730)
- type = PyTango._PyTango.CmdArgType.DevLong
- value = 239
- w_dim_x = 1
- w_dim_y = 0
- w_dimension = AttributeDimension(dim_x = 1, dim_y = 0)
- w_value = 0]
-
- ITango [3]: # Read a spectrum attribute
- ITango [4]: print tangotest.read_attribute("double_spectrum")
- DeviceAttribute[
- data_format = PyTango._PyTango.AttrDataFormat.SPECTRUM
- dim_x = 20
- dim_y = 0
- has_failed = False
- is_empty = False
- name = 'double_spectrum'
- nb_read = 20
- nb_written = 20
- quality = PyTango._PyTango.AttrQuality.ATTR_VALID
- r_dimension = AttributeDimension(dim_x = 20, dim_y = 0)
- time = TimeVal(tv_nsec = 0, tv_sec = 1281085195, tv_usec = 244760)
- type = PyTango._PyTango.CmdArgType.DevDouble
- value = array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.,
- 11., 12., 13., 14., 15., 16., 17., 18., 19.])
- w_dim_x = 20
- w_dim_y = 0
- w_dimension = AttributeDimension(dim_x = 20, dim_y = 0)
- w_value = array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.,
- 11., 12., 13., 14., 15., 16., 17., 18., 19.])]
-
- ITango [5]: # Write a scalar attribute
- ITango [6]: scalar_value = 18
- ITango [7]: tangotest.write_attribute("long_scalar", scalar_value)
-
- ITango [8]: # Write a spectrum attribute
- ITango [9]: spectrum_value = numpy.random.rand(100)*10
- ITango [10]: tangotest.write_attribute("double_spectrum", spectrum_value)
-
-
- ITango [11]: # Write an image attribute
- ITango [12]: image_value = numpy.random.randint(0,10,size=(10,10))
- ITango [13]: tangotest.write_attribute("long_image", image_value)
-
-.. tip::
-
- If you are only interested in the attribute's read value you can do insted:
-
- .. sourcecode:: itango
-
- ITango [1]: tangotest.long_scalar
- Result [1]: 239
+ >>> import PyTango
- The same is valid for writting a new value to an attribute:
+ >>> dev_info = PyTango.DbDevInfo()
+ >>> dev_info.server = "PowerSupplyDS/test"
+ >>> dev_info._class = "PowerSupply"
+ >>> dev_info.name = "test/power_supply/1"
- .. sourcecode:: itango
-
- ITango [1]: tangotest.long_scalar = 18
-
-.. note::
+ >>> db = PyTango.Database()
+ >>> db.add_device(dev_info)
- If PyTango is compiled with numpy support the values got when reading
- a spectrum or an image will be numpy arrays. This results in a faster and
- more memory efficient PyTango. You can also use numpy to specify the values when
- writing attributes, especially if you know the exact attribute type.::
+After, you can run the server on a console with::
- # Creating an unitialized double spectrum of 1000 elements
- spectrum_value = PyTango.numpy_spectrum(PyTango.DevDouble, 1000)
+ $ python PowerSupplyDS.py test
+ Ready to accept request
- # Creating an spectrum with a range
- # Note that I do NOT use PyTango.DevLong here, BUT PyTango.NumpyType.DevLong
- # numpy functions do not understand normal python types, so there's a
- # translation available in PyTango.NumpyType
- spectrum_value = numpy.arange(5, 1000, 2, PyTango.NumpyType.DevLong)
+Now you can access it from a python console::
- # Creating a 2x2 long image from an existing one
- image_value = PyTango.numpy_image(PyTango.DevLong, [[1,2],[3,4]])
+ >>> import PyTango
-Registering devices
-~~~~~~~~~~~~~~~~~~~
+ >>> power_supply = PyTango.DeviceProxy("test/power/supply/1")
+ >>> power_supply.state()
+ STANDBY
-Defining devices in the Tango DataBase:
-
- .. sourcecode:: itango
-
- ITango [1]: # The 3 devices name we want to create
- ITango [2]: # Note: these 3 devices will be served by the same DServer
- ITango [3]: new_device_name1="px1/tdl/mouse1"
- ITango [4]: new_device_name2="px1/tdl/mouse2"
- ITango [5]: new_device_name3="px1/tdl/mouse3"
-
- ITango [6]: # Define the Tango Class served by this DServer
- ITango [7]: new_device_info_mouse = PyTango.DbDevInfo()
- ITango [8]: new_device_info_mouse._class = "Mouse"
- ITango [9]: new_device_info_mouse.server = "ds_Mouse/server_mouse"
-
- ITango [10]: # add the first device
- ITango [11]: new_device_info_mouse.name = new_device_name1
- ITango [12]: db.add_device(new_device_info_mouse)
-
- ITango [13]: # add the next device
- ITango [14]: new_device_info_mouse.name = new_device_name2
- ITango [15]: db.add_device(new_device_info_mouse)
-
- ITango [16]: # add the third device
- ITango [17]: new_device_info_mouse.name = new_device_name3
- ITango [18]: db.add_device(new_device_info_mouse)
-
-Setting up Device properties
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-A more complex example using python subtilities.
-The following python script example (containing some functions and instructions
-manipulating a Galil motor axis device server) gives an idea of how the Tango
-API should be accessed from Python.
-
- .. sourcecode:: itango
+ >>> power_supply.current = 2.3
- ITango [1]: # connecting to the motor axis device
- ITango [2]: axis1 = DeviceProxy ("microxas/motorisation/galilbox")
-
- ITango [3]: # Getting Device Properties
- ITango [4]: property_names = ["AxisBoxAttachement",
- ....: "AxisEncoderType",
- ....: "AxisNumber",
- ....: "CurrentAcceleration",
- ....: "CurrentAccuracy",
- ....: "CurrentBacklash",
- ....: "CurrentDeceleration",
- ....: "CurrentDirection",
- ....: "CurrentMotionAccuracy",
- ....: "CurrentOvershoot",
- ....: "CurrentRetry",
- ....: "CurrentScale",
- ....: "CurrentSpeed",
- ....: "CurrentVelocity",
- ....: "EncoderMotorRatio",
- ....: "logging_level",
- ....: "logging_target",
- ....: "UserEncoderRatio",
- ....: "UserOffset"]
-
- ITango [5]: axis_properties = axis1.get_property(property_names)
- ITango [6]: for prop in axis_properties.keys():
- ....: print "%s: %s" % (prop, axis_properties[prop][0])
-
- ITango [7]: # Changing Properties
- ITango [8]: axis_properties["AxisBoxAttachement"] = ["microxas/motorisation/galilbox"]
- ITango [9]: axis_properties["AxisEncoderType"] = ["1"]
- ITango [10]: axis_properties["AxisNumber"] = ["6"]
- ITango [11]: axis1.put_property(axis_properties)
-
- ITango [12]: # Reading attributes
- ITango [13]: att_list = axis.get_attribute_list()
- ITango [14]: for att in att_list:
- ....: att_val = axis.read_attribute(att)
- ....: print "%s: %s" % (att.name, att_val.value)
-
- ITango [15]: # Changing some attribute values
- ITango [16]: axis1.write_attribute("AxisBackslash", 0.5)
- ITango [17]: axis1.write_attribute("AxisDirection", 1.0)
- ITango [18]: axis1.write_attribute("AxisVelocity", 1000.0)
- ITango [19]: axis1.write_attribute("AxisOvershoot", 500.0)
-
- ITango [20]: # Testing some device commands
- ITango [21]: pos1=axis1.read_attribute("AxisCurrentPosition")
- ITango [22]: axis1.command_inout("AxisBackward")
- ITango [23]: while pos1.value > 1000.0:
- ....: pos1 = axis1.read_attribute("AxisCurrentPosition")
- ....: print "position axis 1 = ", pos1.value
-
- ITango [24]: axis1.command_inout("AxisStop")
-
-Quick tour on the server side
------------------------------
-
-To write a tango device server in python, you must first import the
-:mod:`PyTango` module in your code.
-
-Below is the python code for a Tango device server with two commands and two
-attributes. The commands are:
-
-1. IOLOng which receives a Tango Long and return it multiply by 2. This command
- is allowed only if the device is in the ON state.
-
-2. IOStringArray which receives an array of Tango strings and which returns it
- but in the reverse order. This command is only allowed if the device is in
- the ON state.
-
-The attributes are:
-
-1. Long_attr wich is a Tango long attribute, Scalar and Read only with a
- minimum alarm set to 1000 and a maximum alarm set to 1500
-
-2. Short_attr_rw which is a Tango short attribute, Scalar and Read/Write
-
-The following code is the complete device server code::
-
- import PyTango
-
- class PyDsExp(PyTango.Device_4Impl):
-
- def __init__(self,cl,name):
- PyTango.Device_4Impl.__init__(self,cl,name)
- self.debug_stream('In PyDsExp __init__')
- PyDsExp.init_device(self)
-
- def init_device(self):
- self.debug_stream('In Python init_device method')
- self.set_state(PyTango.DevState.ON)
- self.attr_short_rw = 66
- self.attr_long = 1246
-
- def delete_device(self):
- self.debug_stream('[delete_device] for device %s ' % self.get_name())
-
- #------------------------------------------------------------------
- # COMMANDS
- #------------------------------------------------------------------
-
- def is_IOLong_allowed(self):
- return self.get_state() == PyTango.DevState.ON
-
- def IOLong(self, in_data):
- self.debug_stream('[IOLong::execute] received number %s' % str(in_data))
- in_data = in_data * 2;
- self.debug_stream('[IOLong::execute] return number %s' % str(in_data))
- return in_data;
-
- def is_IOStringArray_allowed(self):
- return self.get_state() == PyTango.DevState.ON
-
- def IOStringArray(self, in_data):
- l = range(len(in_data)-1, -1, -1);
- out_index=0
- out_data=[]
- for i in l:
- self.debug_stream('[IOStringArray::execute] received String' % in_data[out_index])
- out_data.append(in_data[i])
- self.debug_stream('[IOStringArray::execute] return String %s' %out_data[out_index])
- out_index += 1
- self.y = out_data
- return out_data
-
- #------------------------------------------------------------------
- # ATTRIBUTES
- #------------------------------------------------------------------
-
- def read_attr_hardware(self, data):
- self.debug_stream('In read_attr_hardware')
+ >>> power_supply.current
+ 2.3
- def read_Long_attr(self, the_att):
- self.debug_stream('[PyDsExp::read_attr] attribute name Long_attr')
- the_att.set_value(self.attr_long)
-
- def read_Short_attr_rw(self, the_att):
- self.debug_stream('[PyDsExp::read_attr] attribute name Short_attr_rw')
- the_att.set_value(self.attr_short_rw)
-
- def write_Short_attr_rw(self, the_att):
- self.debug_stream('In write_Short_attr_rw for attribute %s' % the_att.get_name())
- data = the_att.get_write_value()
- self.attr_short_rw = data[0]
-
-
- class PyDsExpClass(PyTango.DeviceClass):
-
- def __init__(self, name):
- PyTango.DeviceClass.__init__(self, name)
- self.set_type("PyDsExp")
-
- cmd_list = { 'IOLong' : [ [ PyTango.ArgType.DevLong, "Number" ],
- [ PyTango.ArgType.DevLong, "Number * 2" ] ],
- 'IOStringArray' : [ [ PyTango.ArgType.DevVarStringArray, "Array of string" ],
- [ PyTango.ArgType.DevVarStringArray, "This reversed array"] ],
- }
-
- attr_list = { 'Long_attr' : [ [ PyTango.ArgType.DevLong ,
- PyTango.AttrDataFormat.SCALAR ,
- PyTango.AttrWriteType.READ],
- { 'min alarm' : 1000, 'max alarm' : 1500 } ],
-
- 'Short_attr_rw' : [ [ PyTango.ArgType.DevShort,
- PyTango.AttrDataFormat.SCALAR,
- PyTango.AttrWriteType.READ_WRITE ] ]
- }
+ >>> power_supply.PowerOn()
+ >>> power_supply.Ramp(2.1)
+ True
-
- def main():
- PyTango.server_run({"PyDsExp" : (PyDsExpClass, PyDsExp)})
-
- if __name__ == '__main__':
- main()
-
-.. toctree::
- :hidden:
+ >>> power_supply.state()
+ ON
- Quick tour (original) <quicktour_old>
+Next steps: Check out the :ref:`pytango-api`.
diff --git a/doc/revision.rst b/doc/revision.rst
index 0a2d7ae..94f3f57 100644
--- a/doc/revision.rst
+++ b/doc/revision.rst
@@ -1,15 +1,17 @@
-.. _revision:
+.. _pytango-history-changes:
-Revision
---------
+==================
+History of changes
+==================
:Contributers: T\. Coutinho
:Last Update: |today|
-.. _history-modifications:
+.. _pytango-revisions:
-History of modifications:
+Document revisions
+-------------------
+----------+----------------------------------------------------------------------------------+-----------------------------------------------------+-----------------------+
| Date | Revision | Description | Author |
@@ -73,10 +75,12 @@ History of modifications:
+----------+----------------------------------------------------------------------------------+-----------------------------------------------------+-----------------------+
| 28/08/13 | `8.13 <http://www.tango-controls.org/static/PyTango/v723/doc/html/index.html>`_ | Update to PyTango 7.2.4 | T\. Coutinho |
+----------+----------------------------------------------------------------------------------+-----------------------------------------------------+-----------------------+
-| 27/11/13 | `8.18 <http://www.tango-controls.org/static/PyTango/v810/doc/html/index.html>`_ | Update to PyTango 8.1.1 | T\. Coutinho |
+| 27/11/13 | `8.18 <http://www.tango-controls.org/static/PyTango/v811/doc/html/index.html>`_ | Update to PyTango 8.1.1 | T\. Coutinho |
++----------+----------------------------------------------------------------------------------+-----------------------------------------------------+-----------------------+
+| __/02/14 | `8.19 <http://www.tango-controls.org/static/PyTango/v812/doc/html/index.html>`_ | Update to PyTango 8.1.2 | T\. Coutinho |
+----------+----------------------------------------------------------------------------------+-----------------------------------------------------+-----------------------+
-.. _version-history:
+.. _pytango-version-history:
Version history
---------------
@@ -84,6 +88,20 @@ Version history
+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| version | Changes |
+==========+===================================================================================================================================================================+
+| 8.1.2 | Features: |
+| | - `98: PyTango.server.server_run needs additional post_init_callback parameter <https://sourceforge.net/p/tango-cs/feature-requests/98>`_ |
+| | - `102: DevEncoded attribute should support a python buffer object <https://sourceforge.net/p/tango-cs/feature-requests/102>`_ |
+| | - `103: Make creation of *EventData objects possible in PyTango <https://sourceforge.net/p/tango-cs/feature-requests/103>`_ |
+| | |
+| | Bug fixes: |
+| | - `641: python3 error handling issue <https://sourceforge.net/p/tango-cs/bugs/641/>`_ |
+| | - `648: PyTango unicode method parameters fail <https://sourceforge.net/p/tango-cs/bugs/648/>`_ |
+| | - `649: write_attribute of spectrum/image fails in PyTango without numpy <https://sourceforge.net/p/tango-cs/bugs/649/>`_ |
+| | - `650: [pytango] 8.1.1 not compatible with ipyton 1.2.0-rc1 <https://sourceforge.net/p/tango-cs/bugs/650/>`_ |
+| | - `651: PyTango segmentation fault when run a DS that use attr_data.py <https://sourceforge.net/p/tango-cs/bugs/651/>`_ |
+| | - `660: command_inout_asynch (polling mode) fails <https://sourceforge.net/p/tango-cs/bugs/660/>`_ |
+| | - `666: PyTango shutdown sometimes blocks. <https://sourceforge.net/p/tango-cs/bugs/666/>`_ |
++----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 8.1.1 | Features: |
| | - Implemented tango C++ 8.1 API |
| | |
diff --git a/doc/server/server.rst b/doc/server/server.rst
deleted file mode 100644
index 2eb660b..0000000
--- a/doc/server/server.rst
+++ /dev/null
@@ -1,109 +0,0 @@
-
-.. currentmodule:: PyTango.server
-
-.. _pytango-hlapi:
-
-High Level API
-==============
-
-This module provides a high level device server API. It implements
-:ref:`TEP1 <pytango-TEP1>`. It exposes an easier API for developing a Tango
-device server.
-
-Here is a simple example on how to write a *Clock* device server using the
-high level API::
-
- import time
- from PyTango.server import server_run
- from PyTango.server import Device, DeviceMeta
- from PyTango.server import attribute, command
-
- class Clock(Device):
- __metaclass__ = DeviceMeta
-
- time = attribute()
-
- def read_time(self):
- return time.time()
-
- @command(din_type=str, dout_type=str)
- def strftime(self, format):
- return time.strftime(format)
-
- if __name__ == "__main__":
- server_run((Clock,))
-
-Here is a more complete example on how to write a *PowerSupply* device server
-using the high level API. The example contains:
-
-#. a read-only double scalar attribute called *voltage*
-#. a read/write double scalar expert attribute *current*
-#. a read-only double image attribute called *noise*
-#. a *ramp* command
-#. a *host* device property
-#. a *port* class property
-
-.. code-block:: python
- :linenos:
-
- from time import time
-
- from PyTango import AttrQuality, AttrWriteType, DispLevel, server_run
- from PyTango.server import Device, DeviceMeta, attribute, command
- from PyTango.server import class_property, device_property
-
- class PowerSupply(Device):
- __metaclass__ = DeviceMeta
-
- voltage = attribute()
-
- current = attribute(label="Current",
- dtype=float,
- display_level=DispLevel.EXPERT,
- access=AttrWriteType.READ_WRITE,
- unit="A",
- format="8.4f",
- min_value=0.0, max_value=8.5,
- min_alarm=0.1, max_alarm=8.4,
- min_warning=0.5, max_warning=8.0,
- fget="get_current",
- fset="set_current",
- doc="the power supply current")
-
- noise = attribute(label="Noise",
- dtype=((float,),),
- max_dim_x=1024, max_dim_y=1024,
- fget="get_noise")
-
- host = device_property(dtype=str)
- port = class_property(dtype=int, default_value=9788)
-
- def read_voltage(self):
- self.info_stream("get voltage(%s, %d)" %(self.host, self.port))
- return 10.0
-
- def get_current(self):
- return 2.3456, time(), AttrQuality.ATTR_WARNING
-
-
-*Pretty cool, uh?*
-
-API
----
-
-.. automodule:: PyTango.server
-
- .. autoclass:: Device
- :show-inheritance:
- :inherited-members:
- :members:
-
- .. autoclass:: attribute
-
- .. autofunction:: command
-
- .. autoclass:: device_property
-
- .. autoclass:: class_property
-
- .. autofunction:: server_run
diff --git a/doc/server/attribute.rst b/doc/server_api/attribute.rst
similarity index 100%
rename from doc/server/attribute.rst
rename to doc/server_api/attribute.rst
diff --git a/doc/server/device.rst b/doc/server_api/device.rst
similarity index 94%
rename from doc/server/device.rst
rename to doc/server_api/device.rst
index 0815b24..ce1c421 100644
--- a/doc/server/device.rst
+++ b/doc/server_api/device.rst
@@ -1,5 +1,5 @@
-Device classes
-==============
+Device
+======
.. currentmodule:: PyTango
diff --git a/doc/server/device_class.rst b/doc/server_api/device_class.rst
similarity index 100%
rename from doc/server/device_class.rst
rename to doc/server_api/device_class.rst
diff --git a/doc/server_api/index.rst b/doc/server_api/index.rst
new file mode 100644
index 0000000..f2a28ef
--- /dev/null
+++ b/doc/server_api/index.rst
@@ -0,0 +1,14 @@
+.. currentmodule:: PyTango
+
+Server API
+----------
+
+.. toctree::
+ :maxdepth: 2
+
+ server
+ device
+ device_class
+ logging
+ attribute
+ util
diff --git a/doc/server/logging.rst b/doc/server_api/logging.rst
similarity index 100%
rename from doc/server/logging.rst
rename to doc/server_api/logging.rst
diff --git a/doc/server_api/server.rst b/doc/server_api/server.rst
new file mode 100644
index 0000000..9a3a65b
--- /dev/null
+++ b/doc/server_api/server.rst
@@ -0,0 +1,303 @@
+
+.. currentmodule:: PyTango.server
+
+.. _pytango-hlapi:
+
+High level server API
+=====================
+
+.. automodule:: PyTango.server
+
+.. hlist::
+
+ * :class:`~PyTango.server.Device`
+ * :class:`~PyTango.server.attribute`
+ * :class:`~PyTango.server.command`
+ * :class:`~PyTango.server.device_property`
+ * :class:`~PyTango.server.class_property`
+ * :func:`~PyTango.server.run`
+ * :func:`~PyTango.server.server_run`
+
+This module provides a high level device server API. It implements
+:ref:`TEP1 <pytango-TEP1>`. It exposes an easier API for developing a Tango
+device server.
+
+Here is a simple example on how to write a *Clock* device server using the
+high level API::
+
+ import time
+ from PyTango.server import run
+ from PyTango.server import Device, DeviceMeta
+ from PyTango.server import attribute, command
+
+
+ class Clock(Device):
+ __metaclass__ = DeviceMeta
+
+ time = attribute()
+
+ def read_time(self):
+ return time.time()
+
+ @command(din_type=str, dout_type=str)
+ def strftime(self, format):
+ return time.strftime(format)
+
+
+ if __name__ == "__main__":
+ run((Clock,))
+
+
+Here is a more complete example on how to write a *PowerSupply* device server
+using the high level API. The example contains:
+
+#. a read-only double scalar attribute called *voltage*
+#. a read/write double scalar expert attribute *current*
+#. a read-only double image attribute called *noise*
+#. a *ramp* command
+#. a *host* device property
+#. a *port* class property
+
+.. code-block:: python
+ :linenos:
+
+ from time import time
+ from numpy.random import random_sample
+
+ from PyTango import AttrQuality, AttrWriteType, DispLevel, server_run
+ from PyTango.server import Device, DeviceMeta, attribute, command
+ from PyTango.server import class_property, device_property
+
+ class PowerSupply(Device):
+ __metaclass__ = DeviceMeta
+
+ voltage = attribute()
+
+ current = attribute(label="Current", dtype=float,
+ display_level=DispLevel.EXPERT,
+ access=AttrWriteType.READ_WRITE,
+ unit="A", format="8.4f",
+ min_value=0.0, max_value=8.5,
+ min_alarm=0.1, max_alarm=8.4,
+ min_warning=0.5, max_warning=8.0,
+ fget="get_current", fset="set_current",
+ doc="the power supply current")
+
+ noise = attribute(label="Noise", dtype=((float,),),
+ max_dim_x=1024, max_dim_y=1024,
+ fget="get_noise")
+
+ host = device_property(dtype=str)
+ port = class_property(dtype=int, default_value=9788)
+
+ def read_voltage(self):
+ self.info_stream("get voltage(%s, %d)" % (self.host, self.port))
+ return 10.0
+
+ def get_current(self):
+ return 2.3456, time(), AttrQuality.ATTR_WARNING
+
+ def set_current(self, current):
+ print("Current set to %f" % current)
+
+ def get_noise(self):
+ return random_sample((1024, 1024))
+
+ @command(dtype_in=float)
+ def ramp(self, value):
+ print("Ramping up...")
+
+ if __name__ == "__main__":
+ server_run((PowerSupply,))
+
+*Pretty cool, uh?*
+
+.. note::
+ the ``__metaclass__`` statement is mandatory due to a limitation in the
+ *boost-python* library used by PyTango.
+
+ If you are using python 3 you can write instead::
+
+ class PowerSupply(Device, metaclass=DeviceMeta)
+ pass
+
+.. _pytango-hlapi-datatypes:
+
+.. rubric:: Data types
+
+When declaring attributes, properties or commands, one of the most important
+information is the data type. It is given by the keyword argument *dtype*.
+In order to provide a more *pythonic* interface, this argument is not restricted
+to the :obj:`~PyTango.CmdArgType` options.
+
+For example, to define a *SCALAR* :obj:`~PyTango.CmdArgType.DevLong`
+attribute you have several possibilities:
+
+#. :obj:`int`
+#. 'int'
+#. 'int32'
+#. 'integer'
+#. :obj:`PyTango.CmdArgType.DevLong`
+#. 'DevLong'
+#. :obj:`numpy.int32`
+
+To define a *SPECTRUM* attribute simply wrap the scalar data type in any
+python sequence:
+
+* using a *tuple*: ``(:obj:`int`,)`` or
+* using a *list*: ``[:obj:`int`]`` or
+* any other sequence type
+
+To define an *IMAGE* attribute simply wrap the scalar data type in any
+python sequence of sequences:
+
+* using a *tuple*: ``((:obj:`int`,),)`` or
+* using a *list*: ``[[:obj:`int`]]`` or
+* any other sequence type
+
+Below is the complete table of equivalences.
+
+======================================== ========================================
+dtype argument converts to tango type
+======================================== ========================================
+ ``None`` ``DevVoid``
+ ``'None'`` ``DevVoid``
+ ``DevVoid`` ``DevVoid``
+ ``'DevVoid'`` ``DevVoid``
+
+ ``DevState`` ``DevState``
+ ``'DevState'`` ``DevState``
+
+ :py:obj:`bool` ``DevBoolean``
+ ``'bool'`` ``DevBoolean``
+ ``'boolean'`` ``DevBoolean``
+ ``DevBoolean`` ``DevBoolean``
+ ``'DevBoolean'`` ``DevBoolean``
+ :py:obj:`numpy.bool_` ``DevBoolean``
+
+ ``'char'`` ``DevUChar``
+ ``'chr'`` ``DevUChar``
+ ``'byte'`` ``DevUChar``
+ ``chr`` ``DevUChar``
+ ``DevUChar`` ``DevUChar``
+ ``'DevUChar'`` ``DevUChar``
+ :py:obj:`numpy.uint8` ``DevUChar``
+
+ ``'int16'`` ``DevShort``
+ ``DevShort`` ``DevShort``
+ ``'DevShort'`` ``DevShort``
+ :py:obj:`numpy.int16` ``DevShort``
+
+ ``'uint16'`` ``DevUShort``
+ ``DevUShort`` ``DevUShort``
+ ``'DevUShort'`` ``DevUShort``
+ :py:obj:`numpy.uint16` ``DevUShort``
+
+ :py:obj:`int` ``DevLong``
+ ``'int'`` ``DevLong``
+ ``'int32'`` ``DevLong``
+ ``DevLong`` ``DevLong``
+ ``'DevLong'`` ``DevLong``
+ :py:obj:`numpy.int32` ``DevLong``
+
+ ``'uint'`` ``DevULong``
+ ``'uint32'`` ``DevULong``
+ ``DevULong`` ``DevULong``
+ ``'DevULong'`` ``DevULong``
+ :py:obj:`numpy.uint32` ``DevULong``
+
+ ``'int64'`` ``DevLong64``
+ ``DevLong64`` ``DevLong64``
+ ``'DevLong64'`` ``DevLong64``
+ :py:obj:`numpy.int64` ``DevLong64``
+
+ ``'uint64'`` ``DevULong64``
+ ``DevULong64`` ``DevULong64``
+ ``'DevULong64'`` ``DevULong64``
+ :py:obj:`numpy.uint64` ``DevULong64``
+
+ ``DevInt`` ``DevInt``
+ ``'DevInt'`` ``DevInt``
+
+ ``'float32'`` ``DevFloat``
+ ``DevFloat`` ``DevFloat``
+ ``'DevFloat'`` ``DevFloat``
+ :py:obj:`numpy.float32` ``DevFloat``
+
+ :py:obj:`float` ``DevDouble``
+ ``'double'`` ``DevDouble``
+ ``'float'`` ``DevDouble``
+ ``'float64'`` ``DevDouble``
+ ``DevDouble`` ``DevDouble``
+ ``'DevDouble'`` ``DevDouble``
+ :py:obj:`numpy.float64` ``DevDouble``
+
+ :py:obj:`str` ``DevString``
+ ``'str'`` ``DevString``
+ ``'string'`` ``DevString``
+ ``'text'`` ``DevString``
+ ``DevString`` ``DevString``
+ ``'DevString'`` ``DevString``
+
+ :py:obj:`bytearray` ``DevEncoded``
+ ``'bytearray'`` ``DevEncoded``
+ ``'bytes'`` ``DevEncoded``
+ ``DevEncoded`` ``DevEncoded``
+ ``'DevEncoded'`` ``DevEncoded``
+
+ ``DevVarBooleanArray`` ``DevVarBooleanArray``
+ ``'DevVarBooleanArray'`` ``DevVarBooleanArray``
+
+ ``DevVarCharArray`` ``DevVarCharArray``
+ ``'DevVarCharArray'`` ``DevVarCharArray``
+
+ ``DevVarShortArray`` ``DevVarShortArray``
+ ``'DevVarShortArray'`` ``DevVarShortArray``
+
+ ``DevVarLongArray`` ``DevVarLongArray``
+ ``'DevVarLongArray'`` ``DevVarLongArray``
+
+ ``DevVarLong64Array`` ``DevVarLong64Array``
+ ``'DevVarLong64Array'`` ``DevVarLong64Array``
+
+ ``DevVarULong64Array`` ``DevVarULong64Array``
+ ``'DevVarULong64Array'`` ``DevVarULong64Array``
+
+ ``DevVarFloatArray`` ``DevVarFloatArray``
+ ``'DevVarFloatArray'`` ``DevVarFloatArray``
+
+ ``DevVarDoubleArray`` ``DevVarDoubleArray``
+ ``'DevVarDoubleArray'`` ``DevVarDoubleArray``
+
+ ``DevVarUShortArray`` ``DevVarUShortArray``
+ ``'DevVarUShortArray'`` ``DevVarUShortArray``
+
+ ``DevVarULongArray`` ``DevVarULongArray``
+ ``'DevVarULongArray'`` ``DevVarULongArray``
+
+ ``DevVarStringArray`` ``DevVarStringArray``
+ ``'DevVarStringArray'`` ``DevVarStringArray``
+
+ ``DevVarLongStringArray`` ``DevVarLongStringArray``
+ ``'DevVarLongStringArray'`` ``DevVarLongStringArray``
+
+ ``DevVarDoubleStringArray`` ``DevVarDoubleStringArray``
+ ``'DevVarDoubleStringArray'`` ``DevVarDoubleStringArray``
+======================================== ========================================
+
+.. autoclass:: Device
+ :show-inheritance:
+ :inherited-members:
+ :members:
+
+.. autoclass:: attribute
+
+.. autofunction:: command
+
+.. autoclass:: device_property
+
+.. autoclass:: class_property
+
+.. autofunction:: run
+
+.. autofunction:: server_run
diff --git a/doc/server/util.rst b/doc/server_api/util.rst
similarity index 100%
rename from doc/server/util.rst
rename to doc/server_api/util.rst
diff --git a/doc/start.rst b/doc/start.rst
index 4408937..48d6df9 100644
--- a/doc/start.rst
+++ b/doc/start.rst
@@ -6,8 +6,11 @@
Getting started
===============
-Quick installation: Linux
--------------------------
+Installing
+----------
+
+Linux
+~~~~~
PyTango is available on linux as an official debian/ubuntu package::
@@ -23,8 +26,8 @@ RPM packages are also available for RHEL & CentOS:
* `RHEL 6/CentOS 6 32bits <ftp://ftp.maxlab.lu.se/pub/maxlab/packages/el6/i386/repoview/index.html>`_
* `RHEL 6/CentOS 6 64bits <ftp://ftp.maxlab.lu.se/pub/maxlab/packages/el6/x86_64/repoview/index.html>`_
-From PyPi
-~~~~~~~~~
+PyPi
+~~~~
You can also install the latest version from `PyPi`_.
@@ -43,8 +46,8 @@ or easy_install::
$ easy_install -U PyTango
-Quick installation: Windows
----------------------------
+Windows
+~~~~~~~
First, make sure `Python`_ and `numpy`_ are installed.
@@ -55,9 +58,8 @@ Windows XP/Vista/7/8. The complete list of binaries can be downloaded from
Select the proper windows package, download it and finally execute the
installion wizard.
-
-Compiling & installing
-----------------------
+Compiling
+---------
Linux
~~~~~
@@ -95,8 +97,8 @@ Since it is rarely needed and the instructions are so complicated, I have
choosen to place the how-to in a separate text file. You can find it in the
source package under :file:`doc/windows_notes.txt`.
-Testing your installation
--------------------------
+Testing
+-------
If you have IPython_ installed, the best way to test your PyTango installation
is by starting the new PyTango CLI called :ref:`itango` by typing on the command
@@ -121,3 +123,4 @@ python console and type:
>>> PyTango.Release.version
'8.0.2'
+Next steps: Check out the :ref:`pytango-quick-tour`.
diff --git a/setup.py b/setup.py
index d77114d..98ffe99 100644
--- a/setup.py
+++ b/setup.py
@@ -47,17 +47,19 @@ except:
try:
import numpy
-except:
+except ImportError:
numpy = None
is64 = 8 * struct.calcsize("P") == 64
def pkg_config(*packages, **config):
- config_map = {"-I": "include_dirs",
- "-L": "library_dirs",
- "-l": "libraries"}
- cmd = ["pkg-config", "--libs", "--cflags-only-I", " ".join(packages)]
+ config_map = {
+ "-I": "include_dirs",
+ "-L": "library_dirs",
+ "-l": "libraries",
+ }
+ cmd = ["pkg-config", "--cflags-only-I", " ".join(packages)]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
result = proc.wait()
result = str(proc.communicate()[0].decode("utf-8"))
@@ -67,8 +69,8 @@ def pkg_config(*packages, **config):
if value not in config_values:
config_values.append(value)
return config
-
-
+
+
def abspath(*path):
"""A method to determine absolute path for a given relative path to the
directory where this setup.py script is located"""
@@ -105,7 +107,7 @@ def get_c_numpy():
if os.path.isdir(inc):
return inc
-
+
def has_c_numpy():
return get_c_numpy() is not None
@@ -119,9 +121,12 @@ def has_numpy(with_src=True):
def get_script_files():
- FILTER_OUT = (), # "winpostinstall.py",
+ FILTER_OUT = [] # "winpostinstall.py",
+
+ if os.name != "nt":
+ FILTER_OUT.append("pytango_winpostinstall.py")
- scripts_dir = abspath('scripts')
+ scripts_dir = abspath("scripts")
scripts = []
items = os.listdir(scripts_dir)
for item in items:
@@ -154,21 +159,23 @@ def add_lib(name, dirs, sys_libs, env_name=None, lib_name=None, inc_suffix=None)
return
else:
inc_dir = os.path.join(ENV, 'include')
+ dirs['include_dirs'].append(inc_dir)
if inc_suffix is not None:
inc_dir = os.path.join(inc_dir, inc_suffix)
+ dirs['include_dirs'].append(inc_dir)
+
lib_dirs = [os.path.join(ENV, 'lib')]
if is64:
lib64_dir = os.path.join(ENV, 'lib64')
if os.path.isdir(lib64_dir):
lib_dirs.insert(0, lib64_dir)
+ dirs['library_dirs'].extend(lib_dirs)
if lib_name.startswith('lib'):
lib_name = lib_name[3:]
- dirs['include_dirs'].append(inc_dir)
- dirs['library_dirs'].extend(lib_dirs)
dirs['libraries'].append(lib_name)
-
+
class build(dftbuild):
user_options = dftbuild.user_options + \
@@ -202,7 +209,7 @@ class build(dftbuild):
dftbuild.run(self)
if self.strip_lib:
- if os.name == 'posix':
+ if 'posix' in os.name:
has_objcopy = os.system("type objcopy") == 0
if has_objcopy:
d = abspath(self.build_lib, "PyTango")
@@ -260,7 +267,7 @@ class build_ext(dftbuild_ext):
ext.define_macros += [ ('PYTANGO_HAS_UNIQUE_PTR', '1') ]
dftbuild_ext.build_extension(self, ext)
-
+
if sphinx:
class build_doc(BuildDoc):
@@ -323,18 +330,17 @@ class install(dftinstall):
sub_commands = list(dftinstall.sub_commands)
sub_commands.append(('install_html', has_html))
-
-def main():
+
+def setup_args():
macros = []
-
+
directories = {
'include_dirs': [],
'library_dirs': [],
- 'libraries': ['tango', 'log4tango', 'zmq',
- 'omniDynamic4', 'COS4', 'omniORB4', 'omnithread'],
+ 'libraries': ['tango'],
}
sys_libs = []
-
+
add_lib('omni', directories, sys_libs, lib_name='omniORB4')
add_lib('zmq', directories, sys_libs, lib_name='libzmq')
add_lib('tango', directories, sys_libs, inc_suffix='tango')
@@ -363,11 +369,11 @@ def main():
directories['include_dirs'].append(inc_dir)
directories['library_dirs'].extend(lib_dirs)
-
+
directories['libraries'].append(boost_library_name)
# special numpy configuration
-
+
numpy_c_include = get_c_numpy()
if numpy_c_include is not None:
directories['include_dirs'].append(numpy_c_include)
@@ -379,7 +385,7 @@ def main():
if 'posix' in os.name:
directories = pkg_config(*sys_libs, **directories)
-
+
Release = get_release_info()
author = Release.authors['Coutinho']
@@ -391,7 +397,7 @@ def main():
'PyTango.ipython',
'PyTango.ipython.ipython_00_10',
'PyTango.ipython.ipython_00_11',
- 'PyTango.ipython.ipython_10_00',
+ 'PyTango.ipython.ipython_10_00',
]
py_modules = []
@@ -412,6 +418,8 @@ def main():
scripts = get_script_files()
data_files = []
+ if os.name == 'nt':
+ data_files.append(('scripts', ['doc/_static/itango.ico']))
classifiers = [
'Development Status :: 5 - Production/Stable',
@@ -429,7 +437,7 @@ def main():
'Topic :: Scientific/Engineering',
'Topic :: Software Development :: Libraries',
]
-
+
# Note for PyTango developers:
# Compilation time can be greatly reduced by compiling the file
# src/precompiled_header.hpp as src/precompiled_header.hpp.gch
@@ -446,7 +454,7 @@ def main():
if please_debug:
extra_compile_args += ['-g', '-O0']
- extra_link_args += ['-g' , '-O0']
+ extra_link_args += ['-g' , '-O0']
src_dir = abspath('src', 'boost', 'cpp')
client_dir = src_dir
@@ -485,7 +493,7 @@ def main():
if sphinx:
cmdclass['build_doc'] = build_doc
- dist = setup(
+ opts = dict(
name='PyTango',
version=Release.version,
description=Release.description,
@@ -510,7 +518,10 @@ def main():
ext_modules=[_pytango],
cmdclass=cmdclass)
- return dist
+ return opts
+
+def main():
+ return setup(**setup_args())
if __name__ == "__main__":
main()
diff --git a/src/boost/cpp/attr_conf_event_data.cpp b/src/boost/cpp/attr_conf_event_data.cpp
index cee7201..15c5521 100644
--- a/src/boost/cpp/attr_conf_event_data.cpp
+++ b/src/boost/cpp/attr_conf_event_data.cpp
@@ -12,26 +12,55 @@
#include "precompiled_header.hpp"
#include <tango.h>
+#include "exception.h"
+
using namespace boost::python;
+extern boost::python::object PyTango_DevFailed;
+
+namespace PyAttrConfEventData
+{
+ static boost::shared_ptr<Tango::AttrConfEventData> makeAttrConfEventData()
+ {
+ Tango::AttrConfEventData *result = new Tango::AttrConfEventData;
+ return boost::shared_ptr<Tango::AttrConfEventData>(result);
+ }
+
+ static void set_errors(Tango::AttrConfEventData &event_data,
+ boost::python::object &dev_failed)
+ {
+ Tango::DevFailed df;
+ boost::python::object errors = dev_failed.attr("args");
+ sequencePyDevError_2_DevErrorList(errors.ptr(), event_data.errors);
+ }
+};
+
void export_attr_conf_event_data()
{
class_<Tango::AttrConfEventData>("AttrConfEventData",
init<const Tango::AttrConfEventData &>())
- // The original Tango::EventData structure has a 'device' field.
+
+ .def("__init__", boost::python::make_constructor(PyAttrConfEventData::makeAttrConfEventData))
+
+ // The original Tango::AttrConfEventData structure has a 'device' field.
// However, if we returned this directly we would get a different
// python device each time. So we are doing our weird things to make
// sure the device returned is the same where the read action was
- // performed. So we don't return Tango::EventData::device directly.
+ // performed. So we don't return Tango::AttrConfEventData::device directly.
// See callback.cpp
.setattr("device",object())
- .def_readonly("attr_name", &Tango::AttrConfEventData::attr_name)
- .def_readonly("event", &Tango::AttrConfEventData::event)
+ .def_readwrite("attr_name", &Tango::AttrConfEventData::attr_name)
+ .def_readwrite("event", &Tango::AttrConfEventData::event)
+
.setattr("attr_conf",object())
- .def_readonly("err", &Tango::AttrConfEventData::err)
- .def_readonly("reception_date", &Tango::AttrConfEventData::reception_date)
- .add_property("errors", make_getter(&Tango::AttrConfEventData::errors,
- return_value_policy<copy_non_const_reference>()))
+
+ .def_readwrite("err", &Tango::AttrConfEventData::err)
+ .def_readwrite("reception_date", &Tango::AttrConfEventData::reception_date)
+ .add_property("errors",
+ make_getter(&Tango::AttrConfEventData::errors,
+ return_value_policy<copy_non_const_reference>()),
+ &PyAttrConfEventData::set_errors)
+
.def("get_date", &Tango::AttrConfEventData::get_date,
return_internal_reference<>())
;
diff --git a/src/boost/cpp/attribute_proxy.cpp b/src/boost/cpp/attribute_proxy.cpp
index 8beaf9c..3f0f7ed 100644
--- a/src/boost/cpp/attribute_proxy.cpp
+++ b/src/boost/cpp/attribute_proxy.cpp
@@ -32,6 +32,16 @@ namespace PyAttributeProxy
return bopy::make_tuple(ret);
}
};
+
+ static boost::shared_ptr<Tango::AttributeProxy> makeAttributeProxy1(const std::string& name)
+ {
+ return boost::shared_ptr<Tango::AttributeProxy>(new Tango::AttributeProxy(name.c_str()));
+ }
+
+ static boost::shared_ptr<Tango::AttributeProxy> makeAttributeProxy2(const Tango::DeviceProxy *dev, const std::string& name)
+ {
+ return boost::shared_ptr<Tango::AttributeProxy>(new Tango::AttributeProxy(dev, name.c_str()));
+ }
}
void export_attribute_proxy()
@@ -46,14 +56,13 @@ void export_attribute_proxy()
void (Tango::AttributeProxy::*delete_property_)(std::string &) =
&Tango::AttributeProxy::delete_property;
- bopy::class_<Tango::AttributeProxy> AttributeProxy(
- "__AttributeProxy",
- bopy::init<const char *>())
+ bopy::class_<Tango::AttributeProxy> AttributeProxy("__AttributeProxy",
+ bopy::init<const Tango::AttributeProxy &>())
;
AttributeProxy
- .def(bopy::init<const Tango::DeviceProxy *, const char *>())
- .def(bopy::init<const Tango::AttributeProxy &>())
+ .def("__init__", boost::python::make_constructor(PyAttributeProxy::makeAttributeProxy1))
+ .def("__init__", boost::python::make_constructor(PyAttributeProxy::makeAttributeProxy2))
//
// Pickle
diff --git a/src/boost/cpp/base_types.cpp b/src/boost/cpp/base_types.cpp
index c1586c4..dcc9faa 100644
--- a/src/boost/cpp/base_types.cpp
+++ b/src/boost/cpp/base_types.cpp
@@ -159,11 +159,76 @@ struct convert_PySequence_to_CORBA_Sequence
};
+#if PY_VERSION_HEX < 0x03000000
+bool is_str(PyObject* obj)
+{
+ return PyString_Check(obj) || PyUnicode_Check(obj);
+}
+
+struct StdString_from_python_str_unicode
+{
+ StdString_from_python_str_unicode()
+ {
+ boost::python::converter::registry::push_back(
+ &convertible,
+ &construct,
+ boost::python::type_id<std::string>());
+ }
+
+ // Determine if obj_ptr can be converted in a std::string
+ static void* convertible(PyObject* obj)
+ {
+ if (!is_str(obj))
+ {
+ return 0;
+ }
+ return obj;
+ }
+
+ // Convert obj_ptr into a std::string
+ static void construct(PyObject* obj,
+ boost::python::converter::rvalue_from_python_stage1_data* data)
+ {
+ bool decref = false;
+
+ if (PyUnicode_Check(obj))
+ {
+ decref = true;
+ obj = PyUnicode_AsLatin1String(obj);
+ }
+
+ const char* value = PyBytes_AsString(obj);
+
+ // Grab pointer to memory into which to construct the new std::string
+ void* storage = (
+ (boost::python::converter::rvalue_from_python_storage<std::string>*)
+ data)->storage.bytes;
+
+ // in-place construct the new std::string using the character data
+ // extraced from the python object
+ new (storage) std::string(value);
+
+ // Stash the memory chunk pointer for later use by boost.python
+ data->convertible = storage;
+
+ if (decref)
+ Py_DECREF(obj);
+ }
+};
+
+#endif // PY_VERSION_HEX < 0x03000000
+
int raise_asynch_exception(long thread_id, boost::python::object exp_klass)
{
return PyThreadState_SetAsyncExc(thread_id, exp_klass.ptr());
}
+void leave()
+{
+ AutoPythonAllowThreads guard;
+ Tango::leavefunc();
+}
+
void export_base_types()
{
enum_<PyTango::ExtractAs>("ExtractAs")
@@ -317,7 +382,11 @@ void export_base_types()
convert_numpy_to_integer<Tango::DEV_ULONG>();
convert_numpy_to_integer<Tango::DEV_LONG64>();
convert_numpy_to_integer<Tango::DEV_ULONG64>();
-
+
+#if PY_VERSION_HEX < 0x03000000
+ StdString_from_python_str_unicode();
+#endif
+
// from tango_const.h
export_poll_device();
@@ -347,4 +416,6 @@ void export_base_types()
def("raise_asynch_exception", &raise_asynch_exception);
def("_get_tango_lib_release", &Tango::_convert_tango_lib_release);
+
+ def("_leavefunc", &leave);
}
diff --git a/src/boost/cpp/callback.cpp b/src/boost/cpp/callback.cpp
index 59ae207..ce083d4 100644
--- a/src/boost/cpp/callback.cpp
+++ b/src/boost/cpp/callback.cpp
@@ -254,6 +254,11 @@ namespace {
}
+boost::python::object PyCallBackPushEvent::get_override(const char* name)
+{
+ return boost::python::wrapper<Tango::CallBack>::get_override(name);
+}
+
void PyCallBackPushEvent::fill_py_event(Tango::EventData* ev, object & py_ev, object py_device, PyTango::ExtractAs extract_as)
{
diff --git a/src/boost/cpp/callback.h b/src/boost/cpp/callback.h
index 7c2e21d..a149bb4 100644
--- a/src/boost/cpp/callback.h
+++ b/src/boost/cpp/callback.h
@@ -65,7 +65,8 @@ public:
class PyCallBackPushEvent : public Tango::CallBack , public boost::python::wrapper<Tango::CallBack>
{
public:
- PyCallBackPushEvent() : m_weak_device(0), m_extract_as(PyTango::ExtractAsNumpy) {}
+ PyCallBackPushEvent() : m_weak_device(0), m_extract_as(PyTango::ExtractAsNumpy)
+ {}
virtual ~PyCallBackPushEvent();
//! The object that will call this callback (DeviceProxy), so we can
@@ -78,8 +79,7 @@ public:
void set_extract_as(PyTango::ExtractAs extract_as)
{ this->m_extract_as = extract_as; }
- boost::python::object get_override(const char* name)
- { return boost::python::wrapper<Tango::CallBack>::get_override(name); }
+ boost::python::object get_override(const char* name);
// virtual void cmd_ended(Tango::CmdDoneEvent * ev);
// virtual void attr_read(Tango::AttrReadEvent *ev);
diff --git a/src/boost/cpp/data_ready_event_data.cpp b/src/boost/cpp/data_ready_event_data.cpp
index 073e395..6000598 100644
--- a/src/boost/cpp/data_ready_event_data.cpp
+++ b/src/boost/cpp/data_ready_event_data.cpp
@@ -12,14 +12,32 @@
#include "precompiled_header.hpp"
#include <tango.h>
+#include "exception.h"
+
using namespace boost::python;
+extern boost::python::object PyTango_DevFailed;
+
struct PyDataReadyEventData
{
static inline Tango::DeviceProxy* get_device(Tango::DataReadyEventData &self)
{
return self.device;
}
+
+ static boost::shared_ptr<Tango::DataReadyEventData> makeDataReadyEventData()
+ {
+ Tango::DataReadyEventData *result = new Tango::DataReadyEventData;
+ return boost::shared_ptr<Tango::DataReadyEventData>(result);
+ }
+
+ static void set_errors(Tango::DataReadyEventData &event_data,
+ boost::python::object &dev_failed)
+ {
+ Tango::DevFailed df;
+ boost::python::object errors = dev_failed.attr("args");
+ sequencePyDevError_2_DevErrorList(errors.ptr(), event_data.errors);
+ }
};
void export_data_ready_event_data()
@@ -27,6 +45,8 @@ void export_data_ready_event_data()
class_<Tango::DataReadyEventData>("DataReadyEventData",
init<const Tango::DataReadyEventData &>())
+ .def("__init__", boost::python::make_constructor(PyDataReadyEventData::makeDataReadyEventData))
+
// The original Tango::EventData structure has a 'device' field.
// However, if we returned this directly we would get a different
// python device each time. So we are doing our weird things to make
@@ -34,14 +54,17 @@ void export_data_ready_event_data()
// performed. So we don't return Tango::EventData::device directly.
// See callback.cpp
.setattr("device",object())
- .def_readonly("attr_name", &Tango::DataReadyEventData::attr_name)
- .def_readonly("event", &Tango::DataReadyEventData::event)
- .def_readonly("attr_data_type", &Tango::DataReadyEventData::attr_data_type)
- .def_readonly("ctr", &Tango::DataReadyEventData::ctr)
- .def_readonly("err", &Tango::DataReadyEventData::err)
- .def_readonly("reception_date", &Tango::DataReadyEventData::reception_date)
- .add_property("errors", make_getter(&Tango::DataReadyEventData::errors,
- return_value_policy<copy_non_const_reference>()))
+ .def_readwrite("attr_name", &Tango::DataReadyEventData::attr_name)
+ .def_readwrite("event", &Tango::DataReadyEventData::event)
+ .def_readwrite("attr_data_type", &Tango::DataReadyEventData::attr_data_type)
+ .def_readwrite("ctr", &Tango::DataReadyEventData::ctr)
+ .def_readwrite("err", &Tango::DataReadyEventData::err)
+ .def_readwrite("reception_date", &Tango::DataReadyEventData::reception_date)
+ .add_property("errors",
+ make_getter(&Tango::DataReadyEventData::errors,
+ return_value_policy<copy_non_const_reference>()),
+ &PyDataReadyEventData::set_errors)
+
.def("get_date", &Tango::DataReadyEventData::get_date,
return_internal_reference<>())
;
diff --git a/src/boost/cpp/device_attribute.cpp b/src/boost/cpp/device_attribute.cpp
index ad11024..01d6390 100644
--- a/src/boost/cpp/device_attribute.cpp
+++ b/src/boost/cpp/device_attribute.cpp
@@ -680,9 +680,9 @@ namespace PyDeviceAttribute
TANGO_CALL_ON_ATTRIBUTE_DATA_TYPE_ID( data_type, _fill_numpy_attribute, self, isImage, py_value );
else
TANGO_CALL_ON_ATTRIBUTE_DATA_TYPE_ID( data_type, _fill_list_attribute, self, isImage, py_value );
- break;
}
# endif
+ break;
default:
raise_(PyExc_TypeError, "unsupported data_format.");
}
diff --git a/src/boost/cpp/device_attribute.h b/src/boost/cpp/device_attribute.h
index 23828ee..2bf3952 100644
--- a/src/boost/cpp/device_attribute.h
+++ b/src/boost/cpp/device_attribute.h
@@ -122,6 +122,12 @@ namespace PyDeviceAttribute {
template<typename TDeviceAttribute>
boost::python::object convert_to_python(const unique_pointer<std::vector<TDeviceAttribute> >& dev_attr_vec, Tango::DeviceProxy & dev_proxy, PyTango::ExtractAs extract_as)
{
+ if (dev_attr_vec->empty())
+ {
+ boost::python::list ls;
+ return ls;
+ }
+
update_data_format(dev_proxy, &(*dev_attr_vec)[0], dev_attr_vec->size());
// Convert the c++ vector of DeviceAttribute into a pythonic list
@@ -169,10 +175,6 @@ namespace PyDeviceAttribute {
template<>
inline void _fill_scalar_attribute<Tango::DEV_ENCODED>(Tango::DeviceAttribute & dev_attr, const boost::python::object & py_value)
{
- static const long tangoTypeConst = Tango::DEV_ENCODED;
- typedef TANGO_const2type(tangoTypeConst) TangoScalarType;
- typedef TANGO_const2arraytype(tangoTypeConst) TangoArrayType;
-
/// @todo test it!!
/// @todo Now I am accepting 2 strings: encoded_format, encoded_data. This
diff --git a/src/boost/cpp/device_proxy.cpp b/src/boost/cpp/device_proxy.cpp
index f99838c..08c5f03 100644
--- a/src/boost/cpp/device_proxy.cpp
+++ b/src/boost/cpp/device_proxy.cpp
@@ -372,6 +372,16 @@ namespace PyDeviceProxy
return get_events__aux<Tango::DataReadyEventData, Tango::DataReadyEventDataList>
(py_self, event_id);
}
+
+ static boost::shared_ptr<Tango::DeviceProxy> makeDeviceProxy1(const std::string& name)
+ {
+ return boost::shared_ptr<Tango::DeviceProxy>(new Tango::DeviceProxy(name.c_str()));
+ }
+
+ static boost::shared_ptr<Tango::DeviceProxy> makeDeviceProxy2(const std::string& name, bool b)
+ {
+ return boost::shared_ptr<Tango::DeviceProxy>(new Tango::DeviceProxy(name.c_str(), b));
+ }
};
void export_device_proxy()
@@ -394,6 +404,8 @@ void export_device_proxy()
.def(bopy::init<const char *>())
.def(bopy::init<const char *, bool>())
.def(bopy::init<const Tango::DeviceProxy &>())
+ .def("__init__", boost::python::make_constructor(PyDeviceProxy::makeDeviceProxy1))
+ .def("__init__", boost::python::make_constructor(PyDeviceProxy::makeDeviceProxy2))
//
// Pickle
diff --git a/src/boost/cpp/event_data.cpp b/src/boost/cpp/event_data.cpp
index a9f7f49..f55405f 100644
--- a/src/boost/cpp/event_data.cpp
+++ b/src/boost/cpp/event_data.cpp
@@ -12,13 +12,44 @@
#include "precompiled_header.hpp"
#include <tango.h>
+#include "exception.h"
+
using namespace boost::python;
+extern boost::python::object PyTango_DevFailed;
+
+namespace PyEventData
+{
+ static boost::shared_ptr<Tango::EventData> makeEventData()
+ {
+ Tango::EventData *result = new Tango::EventData;
+ result->attr_value = new Tango::DeviceAttribute();
+ return boost::shared_ptr<Tango::EventData>(result);
+ }
+
+ static void set_errors(Tango::EventData &event_data, boost::python::object &error)
+ {
+ PyObject* error_ptr = error.ptr();
+ if (PyObject_IsInstance(error_ptr, PyTango_DevFailed.ptr()))
+ {
+ Tango::DevFailed df;
+ boost::python::object error_list = error.attr("args");
+ sequencePyDevError_2_DevErrorList(error_list.ptr(), event_data.errors);
+ }
+ else
+ {
+ sequencePyDevError_2_DevErrorList(error_ptr, event_data.errors);
+ }
+ }
+};
+
void export_event_data()
{
class_<Tango::EventData>("EventData",
init<const Tango::EventData &>())
+ .def("__init__", boost::python::make_constructor(PyEventData::makeEventData))
+
// The original Tango::EventData structure has a 'device' field.
// However, if we returned this directly we would get a different
// python device each time. So we are doing our weird things to make
@@ -27,8 +58,8 @@ void export_event_data()
// See callback.cpp
.setattr("device", object())
- .def_readonly("attr_name", &Tango::EventData::attr_name)
- .def_readonly("event", &Tango::EventData::event)
+ .def_readwrite("attr_name", &Tango::EventData::attr_name)
+ .def_readwrite("event", &Tango::EventData::event)
// The original Tango::EventData structure has "get_attr_value" but
// we can't refer it directly here because we have to extract value
@@ -36,10 +67,12 @@ void export_event_data()
// See callback.cpp
.setattr("attr_value", object())
- .def_readonly("err", &Tango::EventData::err)
- .def_readonly("reception_date", &Tango::EventData::reception_date)
- .add_property("errors", make_getter(&Tango::EventData::errors,
- return_value_policy<copy_non_const_reference>()))
+ .def_readwrite("err", &Tango::EventData::err)
+ .def_readwrite("reception_date", &Tango::EventData::reception_date)
+ .add_property("errors",
+ make_getter(&Tango::EventData::errors,
+ return_value_policy<copy_non_const_reference>()),
+ &PyEventData::set_errors)
.def("get_date", &Tango::EventData::get_date,
return_internal_reference<>())
diff --git a/src/boost/cpp/fast_from_py.h b/src/boost/cpp/fast_from_py.h
index 4329aae..4abce71 100644
--- a/src/boost/cpp/fast_from_py.h
+++ b/src/boost/cpp/fast_from_py.h
@@ -281,8 +281,7 @@ inline typename TANGO_const2type(tangoTypeConst)*
fast_python_to_tango_buffer_sequence(PyObject* py_val, long* pdim_x, long *pdim_y, const std::string &fname, bool isImage, long& res_dim_x, long& res_dim_y)
{
typedef typename TANGO_const2type(tangoTypeConst) TangoScalarType;
- typedef typename TANGO_const2arraytype(tangoTypeConst) TangoArrayType;
-
+
long dim_x;
long dim_y = 0;
Py_ssize_t len = PySequence_Size(py_val);
diff --git a/src/boost/cpp/fast_from_py_numpy.hpp b/src/boost/cpp/fast_from_py_numpy.hpp
index 6fd6cb8..7f24054 100644
--- a/src/boost/cpp/fast_from_py_numpy.hpp
+++ b/src/boost/cpp/fast_from_py_numpy.hpp
@@ -33,7 +33,6 @@ inline typename TANGO_const2type(tangoScalarTypeConst)*
fast_python_to_tango_buffer_numpy(PyObject* py_val, long* pdim_x, long* pdim_y, const std::string &fname, bool isImage, long& res_dim_x, long& res_dim_y)
{
typedef typename TANGO_const2type(tangoScalarTypeConst) TangoScalarType;
- typedef typename TANGO_const2arraytype(tangoScalarTypeConst) TangoArrayType;
static const int typenum = TANGO_const2numpy(tangoScalarTypeConst);
if (!PyArray_Check(py_val)) {
diff --git a/src/boost/cpp/server/attribute.cpp b/src/boost/cpp/server/attribute.cpp
index 0e88137..94c321a 100644
--- a/src/boost/cpp/server/attribute.cpp
+++ b/src/boost/cpp/server/attribute.cpp
@@ -87,36 +87,6 @@ namespace PyAttribute
}
/**
- * ATTENTION: this template specialization is done to close a memory leak
- * that exists up to tango 7.1.1 for string read_write attributes
- *
- * Tango Attribute set_value wrapper for scalar string attributes
- *
- * @param att attribute reference
- * @param value new attribute value
- */
- /*
- template<>
- inline void __set_value_scalar<Tango::DEV_STRING>(Tango::Attribute &att,
- bopy::object &value)
- {
- Tango::DevString *v = new Tango::DevString;
-
- if (TANGO_VERSION_HEX < 0x07020000 && att.get_writable() != Tango::READ)
- { // MEMORY LEAK: use the python string directly instead of creating a
- // string
- v[0] = PyString_AsString(value.ptr());
- att.set_value(v, 1, 0);
- }
- else
- { // No memory leak here. Do the standard thing
- from_py<Tango::DEV_STRING>::convert(value, *v);
- att.set_value(v, 1, 0, true);
- }
- }
- */
-
- /**
* Tango Attribute set_value wrapper for DevEncoded attributes
*
* @param att attribute reference
@@ -143,6 +113,35 @@ namespace PyAttribute
att.set_value(&val_str_real, (Tango::DevUChar*)val_real, (long)len(data));
}
+ /**
+ * Tango Attribute set_value wrapper for DevEncoded attributes
+ *
+ * @param att attribute reference
+ * @param data_str new attribute data string
+ * @param data new attribute data
+ */
+ inline void __set_value(Tango::Attribute &att,
+ bopy::str &data_str,
+ bopy::object &data)
+ {
+ extract<Tango::DevString> val_str(data_str.ptr());
+ if (!val_str.check())
+ {
+ throw_wrong_python_data_type(att.get_name(), "set_value()");
+ }
+
+ PyObject* data_ptr = data.ptr();
+ Py_buffer view;
+
+ if (PyObject_GetBuffer(data_ptr, &view, PyBUF_FULL_RO) < 0)
+ {
+ throw_wrong_python_data_type(att.get_name(), "set_value()");
+ }
+
+ Tango::DevString val_str_real = val_str;
+ att.set_value(&val_str_real, (Tango::DevUChar*)view.buf, (long)view.len);
+ PyBuffer_Release(&view);
+ }
/**
* Tango Attribute set_value_date_quality wrapper for scalar attributes
@@ -175,38 +174,37 @@ namespace PyAttribute
att.set_value_date_quality(cpp_val.release(), tv, quality, 1, 0, true);
}
-
/**
- * ATTENTION: this template specialization is done to close a memory leak
- * that exists up to tango 7.1.1 for string read_write attributes
+ * Tango Attribute set_value_date_quality wrapper for DevEncoded attributes
*
- * Tango Attribute set_value_date_quality wrapper for scalar string attributes
- *
* @param att attribute reference
- * @param value new attribute value
+ * @param data_str new attribute data string
+ * @param data new attribute data
* @param t timestamp
* @param quality attribute quality
*/
- /*
- template<>
- inline void __set_value_date_quality_scalar<Tango::DEV_STRING>(Tango::Attribute &att,
- bopy::object &value,
- double t, Tango::AttrQuality quality)
+ inline void __set_value_date_quality(Tango::Attribute &att,
+ bopy::str &data_str,
+ bopy::str &data,
+ double t, Tango::AttrQuality quality)
{
- PYTG_NEW_TIME_FROM_DOUBLE(t, tv);
-
- Tango::DevString *v = new Tango::DevString;
- if (att.get_writable() == Tango::READ)
- { // No memory leak here. Do the standard thing
- from_py<Tango::DEV_STRING>::convert(value, *v);
+ extract<Tango::DevString> val_str(data_str.ptr());
+ if (!val_str.check())
+ {
+ throw_wrong_python_data_type(att.get_name(), "set_value1()");
}
- else
- { // MEMORY LEAK: use the python string directly instead of creating a string
- v[0] = PyString_AsString(value.ptr());
+ extract<Tango::DevString> val(data.ptr());
+ if (!val.check())
+ {
+ throw_wrong_python_data_type(att.get_name(), "set_value2()");
}
- att.set_value_date_quality(v, tv, quality, 1, 0, true);
+
+ PYTG_NEW_TIME_FROM_DOUBLE(t, tv);
+ Tango::DevString val_str_real = val_str;
+ Tango::DevString val_real = val;
+ att.set_value_date_quality(&val_str_real, (Tango::DevUChar*)val_real,
+ (long)len(data), tv, quality);
}
- */
/**
* Tango Attribute set_value_date_quality wrapper for DevEncoded attributes
@@ -219,7 +217,7 @@ namespace PyAttribute
*/
inline void __set_value_date_quality(Tango::Attribute &att,
bopy::str &data_str,
- bopy::str &data,
+ bopy::object &data,
double t, Tango::AttrQuality quality)
{
extract<Tango::DevString> val_str(data_str.ptr());
@@ -227,17 +225,21 @@ namespace PyAttribute
{
throw_wrong_python_data_type(att.get_name(), "set_value1()");
}
- extract<Tango::DevString> val(data.ptr());
- if (!val.check())
- {
- throw_wrong_python_data_type(att.get_name(), "set_value2()");
- }
+
+ PyObject* data_ptr = data.ptr();
+ Py_buffer view;
+
+ if (PyObject_GetBuffer(data_ptr, &view, PyBUF_FULL_RO) < 0)
+ {
+ throw_wrong_python_data_type(att.get_name(), "set_value()");
+ }
PYTG_NEW_TIME_FROM_DOUBLE(t, tv);
- Tango::DevString val_str_real = val_str;
- Tango::DevString val_real = val;
- att.set_value_date_quality(&val_str_real, (Tango::DevUChar*)val_real,
- (long)len(data), tv, quality);
+ Tango::DevString val_str_real = val_str;
+ att.set_value(&val_str_real, (Tango::DevUChar*)view.buf, (long)view.len);
+ att.set_value_date_quality(&val_str_real, (Tango::DevUChar*)view.buf,
+ (long)view.len, tv, quality);
+ PyBuffer_Release(&view);
}
template<long tangoTypeConst>
@@ -252,7 +254,6 @@ namespace PyAttribute
bool isImage)
{
typedef typename TANGO_const2type(tangoTypeConst) TangoScalarType;
- typedef typename TANGO_const2arraytype(tangoTypeConst) TangoArrayType;
if (!PySequence_Check(value.ptr()))
{
@@ -288,8 +289,6 @@ namespace PyAttribute
}
}
-
-
inline void __set_value(const std::string & fname, Tango::Attribute &att, bopy::object &value, long* x, long *y, double t = 0.0, Tango::AttrQuality* quality = 0)
{
long type = att.get_data_type();
@@ -337,6 +336,14 @@ namespace PyAttribute
__set_value(att, data_str, data);
}
+ inline void __set_value(const std::string & fname, Tango::Attribute &att, bopy::str &data_str, bopy::object &data, double t = 0.0, Tango::AttrQuality* quality = 0)
+ {
+ if (quality)
+ __set_value_date_quality(att, data_str, data, t, *quality);
+ else
+ __set_value(att, data_str, data);
+ }
+
inline void set_value(Tango::Attribute &att, bopy::object &value)
{ __set_value("set_value", att, value, 0, 0); }
@@ -346,6 +353,9 @@ namespace PyAttribute
inline void set_value(Tango::Attribute &att, bopy::str &data_str, bopy::str &data)
{ __set_value("set_value", att, data_str, data); }
+ inline void set_value(Tango::Attribute &att, bopy::str &data_str, bopy::object &data)
+ { __set_value("set_value", att, data_str, data); }
+
inline void set_value(Tango::Attribute &att, bopy::object &value, long x)
{ __set_value("set_value", att, value, &x, 0); }
@@ -365,6 +375,13 @@ namespace PyAttribute
{ __set_value("set_value_date_quality", att, data_str, data, t, &quality); }
inline void set_value_date_quality(Tango::Attribute &att,
+ bopy::str &data_str,
+ bopy::object &data,
+ double t,
+ Tango::AttrQuality quality)
+ { __set_value("set_value_date_quality", att, data_str, data, t, &quality); }
+
+ inline void set_value_date_quality(Tango::Attribute &att,
bopy::object &value,
double t, Tango::AttrQuality quality,
long x)
@@ -893,6 +910,9 @@ void export_attribute()
(void (*) (Tango::Attribute &, bopy::object &))
&PyAttribute::set_value)
.def("set_value",
+ (void (*) (Tango::Attribute &, bopy::str &, bopy::object &))
+ &PyAttribute::set_value)
+ .def("set_value",
(void (*) (Tango::Attribute &, bopy::str &, bopy::str &))
&PyAttribute::set_value)
.def("set_value",
@@ -911,6 +931,9 @@ void export_attribute()
(void (*) (Tango::Attribute &, bopy::str &, bopy::str &, double t, Tango::AttrQuality quality))
&PyAttribute::set_value_date_quality)
.def("set_value_date_quality",
+ (void (*) (Tango::Attribute &, bopy::str &, bopy::object &, double t, Tango::AttrQuality quality))
+ &PyAttribute::set_value_date_quality)
+ .def("set_value_date_quality",
(void (*) (Tango::Attribute &, bopy::object &, double t, Tango::AttrQuality quality, long))
&PyAttribute::set_value_date_quality)
.def("set_value_date_quality",
diff --git a/src/boost/cpp/server/attribute.h b/src/boost/cpp/server/attribute.h
index 7c9ef38..0535ff3 100644
--- a/src/boost/cpp/server/attribute.h
+++ b/src/boost/cpp/server/attribute.h
@@ -22,6 +22,9 @@ namespace PyAttribute
void set_value(Tango::Attribute &, boost::python::str &,
boost::python::str &);
+ void set_value(Tango::Attribute &, boost::python::str &,
+ boost::python::object &);
+
void set_value(Tango::Attribute &, boost::python::object &, long);
void set_value(Tango::Attribute &, boost::python::object &, long, long);
@@ -33,6 +36,10 @@ namespace PyAttribute
boost::python::str &, double,
Tango::AttrQuality);
+ void set_value_date_quality(Tango::Attribute &, boost::python::str &,
+ boost::python::object &, double,
+ Tango::AttrQuality);
+
void set_value_date_quality(Tango::Attribute &, boost::python::object &,
double, Tango::AttrQuality , long);
diff --git a/src/boost/cpp/server/device_impl.cpp b/src/boost/cpp/server/device_impl.cpp
index a5472e7..f6e76dc 100644
--- a/src/boost/cpp/server/device_impl.cpp
+++ b/src/boost/cpp/server/device_impl.cpp
@@ -235,6 +235,15 @@ namespace PyDeviceImpl
attr.fire_change_event();
}
+ // Special variantion for encoded data type
+ inline void push_change_event(Tango::DeviceImpl &self, str &name, str &str_data,
+ object &data)
+ {
+ SAFE_PUSH(self, attr, name)
+ PyAttribute::set_value(attr, str_data, data);
+ attr.fire_change_event();
+ }
+
inline void push_change_event(Tango::DeviceImpl &self, str &name, object &data,
long x)
{
@@ -266,6 +275,15 @@ namespace PyDeviceImpl
attr.fire_change_event();
}
+ // Special variantion for encoded data type
+ inline void push_change_event(Tango::DeviceImpl &self, str &name, str &str_data,
+ object &data, double t, Tango::AttrQuality quality)
+ {
+ SAFE_PUSH(self, attr, name)
+ PyAttribute::set_value_date_quality(attr, str_data, data, t, quality);
+ attr.fire_change_event();
+ }
+
inline void push_change_event(Tango::DeviceImpl &self, str &name, object &data,
double t, Tango::AttrQuality quality, long x)
{
@@ -299,7 +317,7 @@ namespace PyDeviceImpl
SAFE_PUSH_ARCHIVE_EVENT(self, name, data);
}
- // Special variantion for encoded data type
+ // Special variation for encoded data type
inline void push_archive_event(Tango::DeviceImpl &self, str &name, str &str_data,
str &data)
{
@@ -308,6 +326,15 @@ namespace PyDeviceImpl
attr.fire_archive_event();
}
+ // Special variation for encoded data type
+ inline void push_archive_event(Tango::DeviceImpl &self, str &name, str &str_data,
+ object &data)
+ {
+ SAFE_PUSH(self, attr, name)
+ PyAttribute::set_value(attr, str_data, data);
+ attr.fire_archive_event();
+ }
+
inline void push_archive_event(Tango::DeviceImpl &self, str &name, object &data,
long x)
{
@@ -339,6 +366,15 @@ namespace PyDeviceImpl
attr.fire_archive_event();
}
+ // Special variantion for encoded data type
+ inline void push_archive_event(Tango::DeviceImpl &self, str &name, str &str_data,
+ object &data, double t, Tango::AttrQuality quality)
+ {
+ SAFE_PUSH(self, attr, name)
+ PyAttribute::set_value_date_quality(attr, str_data, data, t, quality);
+ attr.fire_archive_event();
+ }
+
inline void push_archive_event(Tango::DeviceImpl &self, str &name, object &data,
double t, Tango::AttrQuality quality, long x)
{
@@ -377,6 +413,16 @@ namespace PyDeviceImpl
attr.fire_event(filt_names_, filt_vals_);
}
+ // Special variantion for encoded data type
+ inline void push_event(Tango::DeviceImpl &self, str &name,
+ object &filt_names, object &filt_vals,
+ str &str_data, object &data)
+ {
+ AUX_SAFE_PUSH_EVENT(self, name, filt_names, filt_vals)
+ PyAttribute::set_value(attr, str_data, data);
+ attr.fire_event(filt_names_, filt_vals_);
+ }
+
inline void push_event(Tango::DeviceImpl &self, str &name,
object &filt_names, object &filt_vals, object &data,
long x)
@@ -413,6 +459,17 @@ namespace PyDeviceImpl
attr.fire_event(filt_names_, filt_vals_);
}
+ // Special variantion for encoded data type
+ inline void push_event(Tango::DeviceImpl &self, str &name,
+ object &filt_names, object &filt_vals,
+ str &str_data, object &data,
+ double t, Tango::AttrQuality quality)
+ {
+ AUX_SAFE_PUSH_EVENT(self, name, filt_names, filt_vals)
+ PyAttribute::set_value_date_quality(attr, str_data, data, t, quality);
+ attr.fire_event(filt_names_, filt_vals_);
+ }
+
inline void push_event(Tango::DeviceImpl &self, str &name,
object &filt_names, object &filt_vals, object &data,
double t, Tango::AttrQuality quality, long x)
@@ -1255,6 +1312,10 @@ void export_device_impl()
&PyDeviceImpl::push_change_event)
.def("push_change_event",
+ (void (*) (Tango::DeviceImpl &, str &, str &, object &))
+ &PyDeviceImpl::push_change_event)
+
+ .def("push_change_event",
(void (*) (Tango::DeviceImpl &, str &, object &, long))
&PyDeviceImpl::push_change_event)
@@ -1271,6 +1332,10 @@ void export_device_impl()
&PyDeviceImpl::push_change_event)
.def("push_change_event",
+ (void (*) (Tango::DeviceImpl &, str &, str &, object &, double, Tango::AttrQuality))
+ &PyDeviceImpl::push_change_event)
+
+ .def("push_change_event",
(void (*) (Tango::DeviceImpl &, str &, object &, double, Tango::AttrQuality, long))
&PyDeviceImpl::push_change_event)
@@ -1292,6 +1357,10 @@ void export_device_impl()
&PyDeviceImpl::push_archive_event)
.def("push_archive_event",
+ (void (*) (Tango::DeviceImpl &, str &, str &, object &))
+ &PyDeviceImpl::push_archive_event)
+
+ .def("push_archive_event",
(void (*) (Tango::DeviceImpl &, str &, object &, long))
&PyDeviceImpl::push_archive_event)
@@ -1308,6 +1377,10 @@ void export_device_impl()
&PyDeviceImpl::push_archive_event)
.def("push_archive_event",
+ (void (*) (Tango::DeviceImpl &, str &, str &, object &, double, Tango::AttrQuality))
+ &PyDeviceImpl::push_archive_event)
+
+ .def("push_archive_event",
(void (*) (Tango::DeviceImpl &, str &, object &, double, Tango::AttrQuality, long))
&PyDeviceImpl::push_archive_event)
@@ -1328,6 +1401,10 @@ void export_device_impl()
&PyDeviceImpl::push_event)
.def("push_event",
+ (void (*) (Tango::DeviceImpl &, str &, object &, object &, str &, object &))
+ &PyDeviceImpl::push_event)
+
+ .def("push_event",
(void (*) (Tango::DeviceImpl &, str &, object &, object &, object &, long))
&PyDeviceImpl::push_event)
@@ -1344,6 +1421,10 @@ void export_device_impl()
&PyDeviceImpl::push_event)
.def("push_event",
+ (void (*) (Tango::DeviceImpl &, str &, object &, object &, str &, object &, double, Tango::AttrQuality))
+ &PyDeviceImpl::push_event)
+
+ .def("push_event",
(void (*) (Tango::DeviceImpl &, str &, object &, object &, object &, double, Tango::AttrQuality, long))
&PyDeviceImpl::push_event)
diff --git a/src/boost/cpp/server/device_impl.h b/src/boost/cpp/server/device_impl.h
index b998d80..eca1630 100644
--- a/src/boost/cpp/server/device_impl.h
+++ b/src/boost/cpp/server/device_impl.h
@@ -15,7 +15,7 @@
#include <boost/python.hpp>
#include <tango.h>
-#include <server/device_class.h>
+#include "server/device_class.h"
/**
* A wrapper around the Tango::DeviceImpl class
diff --git a/src/boost/cpp/server/dserver.cpp b/src/boost/cpp/server/dserver.cpp
index 904ba61..2394ff6 100644
--- a/src/boost/cpp/server/dserver.cpp
+++ b/src/boost/cpp/server/dserver.cpp
@@ -127,12 +127,6 @@ namespace PyDServer
delete ret;
return py_ret;
}
-
- void duplicate_d_var(Tango::DServer &self)
- {
- Tango::Device_var d = self._this();
- self.set_d_var(Tango::Device::_duplicate(d));
- }
}
BOOST_PYTHON_FUNCTION_OVERLOADS(add_obj_polling_overload, PyDServer::add_obj_polling, 2, 4)
@@ -192,7 +186,6 @@ void export_dserver()
.def("get_poll_th_pool_size", &Tango::DServer::get_poll_th_pool_size)
.def("get_opt_pool_usage", &Tango::DServer::get_opt_pool_usage)
.def("get_poll_th_conf", &Tango::DServer::get_poll_th_conf)
- .def("duplicate_d_var", &PyDServer::duplicate_d_var)
;
}
diff --git a/src/boost/cpp/server/tango_util.cpp b/src/boost/cpp/server/tango_util.cpp
index d6594af..bfd2a47 100644
--- a/src/boost/cpp/server/tango_util.cpp
+++ b/src/boost/cpp/server/tango_util.cpp
@@ -184,9 +184,11 @@ namespace PyUtil
boost::python::str get_dserver_ior(Tango::Util& self, Tango::DServer* dserver)
{
- const char *ior = self.get_orb()->object_to_string(dserver->_this());
- boost::python::str ret = ior;
- delete [] ior;
+ Tango::Device_var d = dserver->_this();
+ dserver->set_d_var(Tango::Device::_duplicate(d));
+ const char *dserver_ior = self.get_orb()->object_to_string(d);
+ boost::python::str ret = dserver_ior;
+ delete [] dserver_ior;
return ret;
}
@@ -200,8 +202,21 @@ namespace PyUtil
void orb_run(Tango::Util& self)
{
+ AutoPythonAllowThreads guard;
self.get_orb()->run();
}
+
+ boost::python::str get_pid_str(Tango::Util& self)
+ {
+ boost::python::str ret = self.get_pid_str().c_str();
+ return ret;
+ }
+
+ boost::python::str get_version_str(Tango::Util& self)
+ {
+ boost::python::str ret = self.get_version_str().c_str();
+ return ret;
+ }
}
void init_python()
@@ -242,12 +257,10 @@ void export_util()
return_value_policy<copy_non_const_reference>())
.def("get_host_name", &Tango::Util::get_host_name,
return_value_policy<copy_non_const_reference>())
- .def("get_pid_str", &Tango::Util::get_pid_str,
- return_value_policy<copy_non_const_reference>())
+ .def("get_pid_str", &PyUtil::get_pid_str)
.def("get_pid", &Tango::Util::get_pid)
.def("get_tango_lib_release", &Tango::Util::get_tango_lib_release)
- .def("get_version_str", &Tango::Util::get_version_str,
- return_value_policy<copy_non_const_reference>())
+ .def("get_version_str", &PyUtil::get_version_str)
.def("get_server_version", &Tango::Util::get_server_version,
return_value_policy<copy_non_const_reference>())
.def("set_server_version", &Tango::Util::set_server_version)
diff --git a/src/boost/cpp/to_py_numpy.hpp b/src/boost/cpp/to_py_numpy.hpp
index 2574d76..f5a49c2 100644
--- a/src/boost/cpp/to_py_numpy.hpp
+++ b/src/boost/cpp/to_py_numpy.hpp
@@ -17,8 +17,6 @@
template <long tangoArrayTypeConst>
inline boost::python::object to_py_numpy(const typename TANGO_const2type(tangoArrayTypeConst)* tg_array, boost::python::object parent)
{
- typedef typename TANGO_const2type(tangoArrayTypeConst) TangoArrayType;
-
static const int typenum = TANGO_const2scalarnumpy(tangoArrayTypeConst);
if (tg_array == 0) {
diff --git a/src/boost/python/__init__.py b/src/boost/python/__init__.py
index 38a9163..b1bc15e 100644
--- a/src/boost/python/__init__.py
+++ b/src/boost/python/__init__.py
@@ -78,36 +78,47 @@ import os
import sys
def __prepare_nt():
- PATH = os.environ['PATH']
+ import struct
+ PATH = os.environ.get('PATH')
+ if PATH is None:
+ os.environ["PATH"] = PATH = ""
tango_root = os.environ.get("TANGO_ROOT")
if tango_root is None:
tango_root = os.path.join(os.environ["ProgramFiles"], "tango")
+ tango_root = tango_root.lower()
+
if sys.hexversion < 0x03030000:
vc = "vc9_dll"
else:
vc = "vc10_dll"
- tango_dll_path = os.path.join(tango_root,"win32","lib",vc)
- if tango_dll_path.lower() not in PATH.lower():
- os.environ['PATH'] += ";" + tango_dll_path
-
-try:
- from ._PyTango import DeviceProxy
-except ImportError as ie:
- if ie.args[0].count("_PyTango"):
- print(80*"-")
- print(ie)
- print(80*"-")
- print("Probably your current directory is the PyTango's source installation directory.")
- print("You must leave this directory first before using PyTango, otherwise the")
- print("source distribution will conflict with the installed PyTango")
- print(80*"-")
- raise
-
- # in windows try to find the location for tango
- if os.name == 'nt':
- __prepare_nt()
- from ._PyTango import DeviceProxy
+ is64 = 8 * struct.calcsize("P") == 64
+ if is64:
+ arch = "win64"
+ else:
+ arch = "win32"
+ tango_dll_path = os.path.join(tango_root, arch, "lib", vc)
+ tango_dll_path = tango_dll_path.lower()
+ if os.path.exists(tango_dll_path) and \
+ tango_dll_path not in PATH.lower():
+ os.environ['PATH'] += ";" + tango_dll_path
+ else:
+ # Tango C++ could not be found on the system...
+ # ... use PyTango's private Tango C++ library
+ tango_dll_path = os.path.dirname(os.path.abspath(__file__))
+ tango_dll_path = os.path.join(tango_dll_path, "_tango_dll_")
+ if os.path.exists(tango_dll_path):
+ os.environ['PATH'] += ";" + tango_dll_path
+if os.name == 'nt':
+ try:
+ from . import _PyTango
+ except ImportError as ie:
+ # in windows try to find the location for tango
+ __prepare_nt()
+ from . import _PyTango
+else:
+ from . import _PyTango
+
from ._PyTango import (AccessControlType, ApiUtil, ArchiveEventInfo,
AsynCall, AsynReplyNotArrived, AttReqType, Attr, AttrConfEventData,
AttrDataFormat, AttrList, AttrProperty, AttrQuality, AttrReadEvent,
@@ -128,7 +139,7 @@ from ._PyTango import (AccessControlType, ApiUtil, ArchiveEventInfo,
DevVarULong64Array, DevVarULongArray, DevVarUShortArray, DevVoid,
DeviceAttribute, DeviceAttributeConfig, DeviceAttributeHistory,
DeviceData, DeviceDataList, DeviceDataHistory, DeviceDataHistoryList,
- DeviceImpl, DeviceInfo, DeviceUnlocked, Device_2Impl,
+ DeviceImpl, DeviceInfo, DeviceProxy, DeviceUnlocked, Device_2Impl,
Device_3Impl, Device_4Impl, DispLevel, EncodedAttribute, ErrSeverity,
EventData, EventSystemFailed, EventType,
Except, ExtractAs, GreenMode, FMT_UNKNOWN, GroupAttrReply, GroupAttrReplyList,
@@ -172,7 +183,8 @@ from .globals import get_class, get_classes, get_cpp_class, get_cpp_classes, \
get_constructed_class, get_constructed_classes, class_factory, \
delete_class_list, class_list, cpp_class_list, constructed_class
from .utils import is_scalar_type, is_array_type, is_numerical_type, \
- is_int_type, is_float_type, obj_2_str, seqStr_2_obj
+ is_int_type, is_float_type, is_bool_type, is_str_type, \
+ obj_2_str, str_2_obj, seqStr_2_obj
from .green import set_green_mode, get_green_mode
from .device_proxy import get_device_proxy
from .tango_numpy import NumpyType, numpy_type, numpy_spectrum, numpy_image
@@ -180,3 +192,10 @@ from .tango_numpy import NumpyType, numpy_type, numpy_spectrum, numpy_image
from .pytango_init import init as __init
__init()
+
+def __leave():
+ _PyTango._leavefunc()
+
+import atexit
+atexit.register(__leave)
+
diff --git a/src/boost/python/attr_data.py b/src/boost/python/attr_data.py
index c75c6d6..5812e04 100644
--- a/src/boost/python/attr_data.py
+++ b/src/boost/python/attr_data.py
@@ -274,8 +274,13 @@ class AttrData(object):
throw_ex("Wrong polling period in attribute information for "
"attribute %s in class %s\nAttribute information for "
"polling period is not an integer" % (attr_name, name))
-
- memorized = extra_info.get("memorized", "false").lower()
+
+ try:
+ memorized = extra_info.get("memorized", "false").lower()
+ except:
+ throw_ex("Wrong memorized value. for attribute %s in class %s."
+ "Allowed valued are the strings \"true\", \"false\" and "
+ "\"true_without_hard_applied\" (case incensitive)")
if memorized == "true":
self.memorized = True
self.hw_memorized = True
diff --git a/src/boost/python/connection.py b/src/boost/python/connection.py
index a576e3f..5850b1c 100644
--- a/src/boost/python/connection.py
+++ b/src/boost/python/connection.py
@@ -130,7 +130,9 @@ def __Connection__command_inout_raw(self, cmd_name, cmd_param = None):
def __Connection__command_inout_asynch(self, cmd_name, *args):
"""
- command_inout_asynch(self, cmd_name, cmd_param=None, forget=False) -> id
+ command_inout_asynch(self, cmd_name) -> id
+ command_inout_asynch(self, cmd_name, cmd_param) -> id
+ command_inout_asynch(self, cmd_name, cmd_param, forget) -> id
Execute asynchronously (polling model) a command on a device
@@ -139,11 +141,11 @@ def __Connection__command_inout_asynch(self, cmd_name, *args):
- cmd_param : (any) It should be a value of the type expected by the
command or a DeviceData object with this value inserted.
It can be ommited if the command should not get any argument.
- If the command sould get no argument and you want
+ If the command should get no argument and you want
to set the 'forget' param, use None for cmd_param.
- forget : (bool) If this flag is set to true, this means that the client
does not care at all about the server answer and will even
- not try to get it. A false default value is provided. Please,
+ not try to get it. Default value is False. Please,
note that device re-connection will not take place (in case
it is needed) if the fire and forget mode is used. Therefore,
an application using only fire and forget requests is not able
diff --git a/src/boost/python/databaseds/DataBaseds b/src/boost/python/databaseds/DataBaseds
index 2f8cf3c..cb35c0f 100755
--- a/src/boost/python/databaseds/DataBaseds
+++ b/src/boost/python/databaseds/DataBaseds
@@ -1,9 +1,6 @@
#!/usr/bin/env python
-import database
-
-def main():
- database.main()
+from database import main
if __name__ == '__main__':
main()
diff --git a/src/boost/python/databaseds/create_db.sql b/src/boost/python/databaseds/create_db.sql
index 34f6703..3ffdd7a 100644
--- a/src/boost/python/databaseds/create_db.sql
+++ b/src/boost/python/databaseds/create_db.sql
@@ -20,6 +20,11 @@ INSERT INTO object_history_id VALUES (0);
INSERT INTO device VALUES ('sys/database/2',NULL,'sys','database','2','nada','nada','nada','DataBaseds/2','nada','DataBase','nada','nada','nada','nada');
INSERT INTO device VALUES ('dserver/DataBaseds/2',NULL,'dserver','DataBaseds','2','nada','nada','nada','DataBaseds/2','nada','DServer','nada','nada','nada','nada');
+INSERT INTO server VALUES ('databaseds/2','',0,0);
+
+INSERT INTO device VALUES ('sys/database/pydb-test',NULL,'sys','database','pydb-test','nada','nada','nada','DataBaseds/pydb-test','nada','DataBase','nada','nada','nada','nada');
+INSERT INTO device VALUES ('dserver/DataBaseds/pydb-test',NULL,'dserver','DataBaseds','pydb-test','nada','nada','nada','DataBaseds/pydb-test','nada','DServer','nada','nada','nada','nada');
+INSERT INTO server VALUES ('databaseds/pydb-test','',0,0);
#
# Create entry for test device server in device table
@@ -27,6 +32,7 @@ INSERT INTO device VALUES ('dserver/DataBaseds/2',NULL,'dserver','DataBaseds','2
INSERT INTO device VALUES ('sys/tg_test/1',NULL,'sys','tg_test','1','nada','nada','nada','TangoTest/test','nada','TangoTest','nada','nada','nada','nada');
INSERT INTO device VALUES ('dserver/TangoTest/test',NULL,'dserver','TangoTest','test','nada','nada','nada','TangoTest/test','nada','DServer','nada','nada','nada','nada');
+INSERT INTO server VALUES ('tangotest/test','',0,0);
#
# Create entry for Tango Control Access in device table
diff --git a/src/boost/python/databaseds/database.py b/src/boost/python/databaseds/database.py
index 6092884..d9ee34c 100644
--- a/src/boost/python/databaseds/database.py
+++ b/src/boost/python/databaseds/database.py
@@ -33,10 +33,25 @@ __docformat__ = 'restructuredtext'
import PyTango
import sys
# Add additional import
+
+import time
+
#----- PROTECTED REGION ID(DataBase.additionnal_import) ENABLED START -----#
import logging
+try:
+ import argparse
+except ImportError:
+ argparse = None
+ from optparse import OptionParser
+
+from PyTango.globals import get_class, get_class_by_class, \
+ get_constructed_class_by_class
+
+#Argument Options
+global options
+
class DbInter(PyTango.Interceptors):
def create_thread(self):
@@ -61,11 +76,6 @@ th_exc = PyTango.Except.throw_exception
from db_errors import *
-DB_FRONTED = {
- "sqlite3" : "db2",
- "pyodbc" : "pyodbc"
-}
-
def check_device_name(dev_name):
if '*' in dev_name:
return False, None, None
@@ -99,6 +109,18 @@ def replace_wildcard(text):
text = text.replace("*", "%")
return text
+class TimeStructure:
+ def __init__(self):
+ self.average = 0
+ self.minimum = 0
+ self.maximum = 0
+ self.maximum = 0
+ self.total_elapsed = 0
+ self.calls = 0
+ self.index = ''
+
+
+cmd_list = ["DbImportDevice", "DbExportDevice", "DbGetHostServerList", "DbGetHostList","DbGetServerList", "DbGetDevicePropertyList", "DbGetClassPropertyList", "DbGetDeviceMemberList", "DbGetDeviceFamilyList", "DbGetDeviceDomainList", "DbGetDeviceProperty", "DbPutDeviceProperty", "DbDeleteDeviceProperty", "DbInfo", "DbGetDeviceClassList", "DbGetDeviceAttributeProperty", "DbPutDeviceAttributeProperty", "DbGetDeviceAttributeProperty2", "DbPutDeviceAttributeProperty2", "DbUnExportServer", " [...]
#----- PROTECTED REGION END -----# // DataBase.additionnal_import
@@ -111,6 +133,24 @@ def replace_wildcard(text):
class DataBase (PyTango.Device_4Impl):
#--------- Add you global variables here --------------------------
+
+ def update_timing_stats(self, time_before, time_after, cmd_name):
+ tmp_time = self.timing_maps[cmd_name]
+ time_elapsed = (time_after - time_before) * 1000.
+ tmp_time.total_elapsed = tmp_time.total_elapsed + time_elapsed
+ if time_elapsed > tmp_time.maximum:
+ tmp_time.maximum = time_elapsed
+ if time_elapsed < tmp_time.minimum or tmp_time.minimum == 0:
+ tmp_time.minimum = time_elapsed
+ tmp_time.calls = tmp_time.calls + 1
+ tmp_time.average = tmp_time.total_elapsed/tmp_time.calls
+
+ def init_timing_stats(self):
+ self.timing_maps = {}
+ for cmd in cmd_list:
+ self.timing_maps[cmd] = TimeStructure()
+ self.timing_maps[cmd].index = cmd
+
#----- PROTECTED REGION ID(DataBase.global_variables) ENABLED START -----#
def get_device_properties(self, device_klass):
@@ -147,10 +187,13 @@ class DataBase (PyTango.Device_4Impl):
self.attr_Timing_calls_read = [0.0]
self.attr_Timing_index_read = ['']
self.attr_Timing_info_read = ['']
+ self.init_timing_stats()
#----- PROTECTED REGION ID(DataBase.init_device) ENABLED START -----#
+ m = __import__('db_access.%s' % (options.db_access),None,None,
+ 'db_access.%s' % (options.db_access))
+ self.db = m.get_db()
- self.db = db_access.Tango_sqlite3()
-
+ self.set_state(PyTango.DevState.ON)
#----- PROTECTED REGION END -----# // DataBase.init_device
@@ -186,6 +229,10 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".read_Timing_average()")
#----- PROTECTED REGION ID(DataBase.Timing_average_read) ENABLED START -----#
+ self.attr_Timing_average_read[:] = []
+ for tmp_name in cmd_list:
+ self.attr_Timing_average_read.append(self.timing_maps[tmp_name].average)
+
#----- PROTECTED REGION END -----# // DataBase.Timing_average_read
attr.set_value(self.attr_Timing_average_read)
@@ -196,6 +243,10 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".read_Timing_minimum()")
#----- PROTECTED REGION ID(DataBase.Timing_minimum_read) ENABLED START -----#
+ self.attr_Timing_minimum_read[:] = []
+ for tmp_name in cmd_list:
+ self.attr_Timing_minimum_read.append(self.timing_maps[tmp_name].minimum)
+
#----- PROTECTED REGION END -----# // DataBase.Timing_minimum_read
attr.set_value(self.attr_Timing_minimum_read)
@@ -206,6 +257,10 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".read_Timing_maximum()")
#----- PROTECTED REGION ID(DataBase.Timing_maximum_read) ENABLED START -----#
+ self.attr_Timing_maximum_read[:] = []
+ for tmp_name in cmd_list:
+ self.attr_Timing_maximum_read.append(self.timing_maps[tmp_name].maximum)
+
#----- PROTECTED REGION END -----# // DataBase.Timing_maximum_read
attr.set_value(self.attr_Timing_maximum_read)
@@ -216,6 +271,10 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".read_Timing_calls()")
#----- PROTECTED REGION ID(DataBase.Timing_calls_read) ENABLED START -----#
+ self.attr_Timing_calls_read[:] = []
+ for tmp_name in cmd_list:
+ self.attr_Timing_calls_read.append(self.timing_maps[tmp_name].calls)
+
#----- PROTECTED REGION END -----# // DataBase.Timing_calls_read
attr.set_value(self.attr_Timing_calls_read)
@@ -226,6 +285,9 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".read_Timing_index()")
#----- PROTECTED REGION ID(DataBase.Timing_index_read) ENABLED START -----#
+ for tmp_name in cmd_list:
+ self.attr_Timing_index_read.append(self.timing_maps[tmp_name].index)
+
#----- PROTECTED REGION END -----# // DataBase.Timing_index_read
attr.set_value(self.attr_Timing_index_read)
@@ -235,7 +297,17 @@ class DataBase (PyTango.Device_4Impl):
def read_Timing_info(self, attr):
self.debug_stream("In " + self.get_name() + ".read_Timing_info()")
#----- PROTECTED REGION ID(DataBase.Timing_info_read) ENABLED START -----#
-
+ self.attr_Timing_info_read[:] = []
+ util = PyTango.Util.instance()
+ self.attr_Timing_info_read.append("TANGO Database Timing info on host " + util.get_host_name())
+ self.attr_Timing_info_read.append(" ")
+ self.attr_Timing_info_read.append("command average minimum maximum calls")
+ self.attr_Timing_info_read.append(" ")
+
+
+ for tmp_name in cmd_list:
+ tmp_info = "%41s\t%6.3f\t%6.3f\t%6.3f\t%.0f"%(tmp_name, self.timing_maps[tmp_name].average, self.timing_maps[tmp_name].minimum, self.timing_maps[tmp_name].maximum, self.timing_maps[tmp_name].calls)
+ self.attr_Timing_info_read.append(tmp_info)
#----- PROTECTED REGION END -----# // DataBase.Timing_info_read
attr.set_value(self.attr_Timing_info_read)
@@ -318,8 +390,6 @@ class DataBase (PyTango.Device_4Impl):
"DataBase::AddServer()")
server_name = argin[0]
- db = self.db
- cursor = db.get_cursor()
for i in range((len(argin) - 1) / 2):
d_name, klass_name = argin[i * 2 + 1], argin[i * 2 + 2]
ret, dev_name, dfm = check_device_name(d_name)
@@ -327,10 +397,9 @@ class DataBase (PyTango.Device_4Impl):
th_exc(DB_IncorrectDeviceName,
"device name (" + d_name + ") syntax error (should be [tango:][//instance/]domain/family/member)",
"DataBase::AddServer()")
- db.add_device(server_name, (dev_name, dfm) , klass_name, cursor=cursor)
+ self.db.add_device(server_name, (dev_name, dfm) , klass_name)
+
- cursor.connection.commit()
- cursor.close()
#----- PROTECTED REGION END -----# // DataBase.DbAddServer
@@ -401,12 +470,8 @@ class DataBase (PyTango.Device_4Impl):
klass_name, attr_name = argin[:2]
- db = self.db
- cursor = db.get_cursor()
for prop_name in argin[2:]:
- db.delete_class_attribute_property(klass_name, attr_name, prop_name, cursor=cursor)
- cursor.connection.commit()
- cursor.close()
+ self.db.delete_class_attribute_property(klass_name, attr_name, prop_name)
#----- PROTECTED REGION END -----# // DataBase.DbDeleteClassAttributeProperty
@@ -427,12 +492,8 @@ class DataBase (PyTango.Device_4Impl):
klass_name = argin[0]
- db = self.db
- cursor = db.get_cursor()
for prop_name in argin[1:]:
- db.delete_class_property(prop_name, cursor=cursor)
- cursor.connection.commit()
- cursor.close()
+ self.db.delete_class_property(prop_name)
#----- PROTECTED REGION END -----# // DataBase.DbDeleteClassProperty
@@ -500,7 +561,7 @@ class DataBase (PyTango.Device_4Impl):
ret, dev_name, dfm = check_device_name(argin)
if not ret:
- self.warn_stream("DataBase::db_delete_device_attribute(): device name " + dev_name + " incorrect ")
+ self.warn_stream("DataBase::db_delete_device_attribute(): device name " + argin + " incorrect ")
th_exc(DB_IncorrectDeviceName,
"failed to delete device attribute, device name incorrect",
"DataBase::DeleteDeviceAttribute()")
@@ -535,17 +596,14 @@ class DataBase (PyTango.Device_4Impl):
ret, dev_name, dfm = check_device_name(argin)
if not ret:
- self.warn_stream("DataBase::db_delete_device_attribute_property(): device name " + dev_name + " incorrect ")
+ self.warn_stream("DataBase::db_delete_device_attribute_property(): device name " + argin + " incorrect ")
th_exc(DB_IncorrectDeviceName,
"failed to delete device attribute property, device name incorrect",
"DataBase::DeleteDeviceAttributeProperty()")
- db = self.db
- cursor = db.get_cursor()
+
for prop_name in argin[2:]:
- self.db.delete_device_attribute_property(dev_name, attr_name, prop_name, cursor=cursor)
- cursor.connection.commit()
- cursor.close()
+ self.db.delete_device_attribute_property(dev_name, attr_name, prop_name)
@@ -566,13 +624,14 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbDeleteDeviceProperty()")
#----- PROTECTED REGION ID(DataBase.DbDeleteDeviceProperty) ENABLED START -----#
+ time_before = time.time()
+
dev_name = argin[0]
- db = self.db
- cursor = db.get_cursor()
for prop_name in argin[1:]:
self.db.delete_device_property(dev_name, prop_name)
- cursor.connection.commit()
- cursor.close()
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbDeleteDeviceProperty")
#----- PROTECTED REGION END -----# // DataBase.DbDeleteDeviceProperty
@@ -591,13 +650,9 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbDeleteProperty()")
#----- PROTECTED REGION ID(DataBase.DbDeleteProperty) ENABLED START -----#
- obj_name = argin[0];
- db = self.db
- cursor = db.get_cursor()
+ obj_name = argin[0]
for prop_name in argin[1:]:
self.db.delete_property(obj_name, prop_name)
- cursor.connection.commit()
- cursor.close()
#----- PROTECTED REGION END -----# // DataBase.DbDeleteProperty
@@ -657,7 +712,7 @@ class DataBase (PyTango.Device_4Impl):
:rtype: PyTango.DevVoid """
self.debug_stream("In " + self.get_name() + ".DbExportDevice()")
#----- PROTECTED REGION ID(DataBase.DbExportDevice) ENABLED START -----#
-
+ time_before = time.time()
if len(argin) < 5:
self.warn_stream("DataBase::DbExportDevice(): insufficient export info for device ")
th_exc(DB_IncorrectArguments,
@@ -665,9 +720,12 @@ class DataBase (PyTango.Device_4Impl):
"DataBase::ExportDevice()")
dev_name, IOR, host, pid, version = argin[:5]
+ dev_name = dev_name.lower()
if pid.lower() == 'null':
pid = "-1"
self.db.export_device(dev_name, IOR, host, pid, version)
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbExportDevice")
#----- PROTECTED REGION END -----# // DataBase.DbExportDevice
@@ -688,6 +746,7 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbExportEvent()")
#----- PROTECTED REGION ID(DataBase.DbExportEvent) ENABLED START -----#
+ time_before = time.time()
if len(argin) < 5:
self.warn_stream("DataBase::db_export_event(): insufficient export info for event ")
th_exc(DB_IncorrectArguments,
@@ -697,6 +756,8 @@ class DataBase (PyTango.Device_4Impl):
event, IOR, host, pid, version = argin[:5]
event = replace_wildcard(event.lower())
self.db.export_event(event, IOR, host, pid, version)
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbExportEvent")
#----- PROTECTED REGION END -----# // DataBase.DbExportEvent
@@ -785,10 +846,7 @@ class DataBase (PyTango.Device_4Impl):
#----- PROTECTED REGION ID(DataBase.DbGetClassAttributeList) ENABLED START -----#
class_name = argin[0]
- if len(argin) > 1:
- wildcard = replace_wildcard(argin[1])
- else:
- wildcard = "%"
+ wildcard = replace_wildcard(argin[1])
argout = self.db.get_class_attribute_list(class_name, wildcard)
@@ -817,7 +875,7 @@ class DataBase (PyTango.Device_4Impl):
#----- PROTECTED REGION ID(DataBase.DbGetClassAttributeProperty) ENABLED START -----#
class_name = argin[0]
- argout = self.db.get_class_attribute_property(clas_name, argout[1:])
+ argout = self.db.get_class_attribute_property(class_name, argin[1:])
#----- PROTECTED REGION END -----# // DataBase.DbGetClassAttributeProperty
return argout
@@ -850,7 +908,7 @@ class DataBase (PyTango.Device_4Impl):
#----- PROTECTED REGION ID(DataBase.DbGetClassAttributeProperty2) ENABLED START -----#
class_name = argin[0]
- argout = self.db.get_class_attribute_property2(clas_name, argout[1:])
+ argout = self.db.get_class_attribute_property2(class_name, argin[1:])
#----- PROTECTED REGION END -----# // DataBase.DbGetClassAttributeProperty2
return argout
@@ -875,7 +933,10 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbGetClassAttributePropertyHist()")
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetClassAttributePropertyHist) ENABLED START -----#
-
+ class_name = argin[0]
+ attribute = replace_wildcard(argin[1])
+ prop_name = replace_wildcard(argin[2])
+ argout = self.db.get_class_attribute_property_hist(class_name, attribute, prop_name)
#----- PROTECTED REGION END -----# // DataBase.DbGetClassAttributePropertyHist
return argout
@@ -894,7 +955,7 @@ class DataBase (PyTango.Device_4Impl):
#----- PROTECTED REGION ID(DataBase.DbGetClassForDevice) ENABLED START -----#
argout = self.db.get_class_for_device(argin)
-
+ print argout
#----- PROTECTED REGION END -----# // DataBase.DbGetClassForDevice
return argout
@@ -962,6 +1023,8 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetClassProperty) ENABLED START -----#
+ class_name = argin[0]
+ argout = self.db.get_class_property(class_name,argint[1:])
#----- PROTECTED REGION END -----# // DataBase.DbGetClassProperty
return argout
@@ -984,6 +1047,10 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetClassPropertyHist) ENABLED START -----#
+ class_name = argin[0]
+ prop_name = argin[1]
+ argout = self.db.get_class_property_hist(class_name, prop_name)
+
#----- PROTECTED REGION END -----# // DataBase.DbGetClassPropertyHist
return argout
@@ -1000,6 +1067,16 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbGetClassPropertyList()")
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetClassPropertyList) ENABLED START -----#
+ time_before = time.time()
+ if not argin:
+ argin = "%"
+ else:
+ argin = replace_wildcard(argin)
+
+ argout = self.db.get_class_property_list(argin)
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbDeleteDeviceProperty")
+
#----- PROTECTED REGION END -----# // DataBase.DbGetClassPropertyList
return argout
@@ -1018,6 +1095,15 @@ class DataBase (PyTango.Device_4Impl):
argout = ''
#----- PROTECTED REGION ID(DataBase.DbGetDeviceAlias) ENABLED START -----#
+ ret, dev_name, dfm = check_device_name(argin)
+ if not ret:
+ th_exc(DB_IncorrectDeviceName,
+ "device name (" + argin + ") syntax error (should be [tango:][//instance/]domain/family/member)",
+ "DataBase::DbGetDeviceAlias()")
+
+ argout = self.db.get_device_alias(dev_name)
+
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceAlias
return argout
@@ -1034,6 +1120,12 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbGetDeviceAliasList()")
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceAliasList) ENABLED START -----#
+ if not argin:
+ argin = "%"
+ else:
+ argin = replace_wildcard(argin)
+
+ argout = self.db.get_device_alias_list(argin)
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceAliasList
return argout
@@ -1054,6 +1146,15 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceAttributeList) ENABLED START -----#
+ dev_name = argin[0]
+ wildcard = argin[1]
+ if not wildcard:
+ wildcard = "%"
+ else:
+ wildcard = replace_wildcard(wildcard)
+
+ argout = self.db.get_device_attribute_list(dev_name, wildcard)
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceAttributeList
return argout
@@ -1077,6 +1178,11 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbGetDeviceAttributeProperty()")
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceAttributeProperty) ENABLED START -----#
+ time_before = time.time()
+ dev_name = argin[0]
+ argout = self.db.get_device_attribute_property(class_name, argin[1:])
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbGetDeviceAttributeProperty")
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceAttributeProperty
return argout
@@ -1109,6 +1215,13 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceAttributeProperty2) ENABLED START -----#
+ time_before = time.time()
+
+ dev_name = argin[0]
+ argout = self.db.get_device_attribute_property2(class_name, argin[1:])
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbGetDeviceAttributeProperty2")
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceAttributeProperty2
return argout
@@ -1133,6 +1246,11 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceAttributePropertyHist) ENABLED START -----#
+ dev_name = argin[0]
+ attribute = replace_wildcard(argin[1])
+ prop_name = replace_wildcard(argin[2])
+ argout = self.db.get_device_attribute_property_hist(dev_name, attribute, prop_name)
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceAttributePropertyHist
return argout
@@ -1153,6 +1271,12 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceClassList) ENABLED START -----#
+ time_before = time.time()
+
+ argout = self.db.get_device_class_list(argin)
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbGetDeviceClassList")
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceClassList
return argout
@@ -1170,6 +1294,12 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceDomainList) ENABLED START -----#
+ time_before = time.time()
+ argin = replace_wildcard(argin)
+ argout = self.db.get_device_domain_list(argin)
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbGetDeviceDomainList")
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceDomainList
return argout
@@ -1187,6 +1317,14 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceExportedList) ENABLED START -----#
+ time_before = time.time()
+
+ argin = replace_wildcard(argin)
+ argout = self.db.get_device_exported_list(argin)
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbGetDeviceExportedList")
+
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceExportedList
return argout
@@ -1205,6 +1343,13 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceFamilyList) ENABLED START -----#
+ time_before = time.time()
+
+ argin = replace_wildcard(argin)
+ argout = self.db.get_device_family_list(argin)
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbGetDeviceFamilyList")
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceFamilyList
return argout
@@ -1230,8 +1375,16 @@ class DataBase (PyTango.Device_4Impl):
:rtype: PyTango.DevVarLongStringArray """
self.debug_stream("In " + self.get_name() + ".DbGetDeviceInfo()")
argout = [0], ['']
+
#----- PROTECTED REGION ID(DataBase.DbGetDeviceInfo) ENABLED START -----#
+ ret, dev_name, dfm = check_device_name(argin)
+ if not ret:
+ th_exc(DB_IncorrectDeviceName,
+ "device name (" + argin + ") syntax error (should be [tango:][//instance/]domain/family/member)",
+ "DataBase::DbGetDeviceAlias()")
+ argout = self.db.get_device_info(dev_name)
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceInfo
return argout
@@ -1249,7 +1402,10 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbGetDeviceList()")
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceList) ENABLED START -----#
-
+ server_name = replace_wildcard(argin[0])
+ class_name = replace_wildcard(argin[1])
+ argout = self.db.get_device_list(server_name, class_name)
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceList
return argout
@@ -1266,7 +1422,10 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbGetDeviceWideList()")
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceWideList) ENABLED START -----#
-
+
+ argin = replace_wildcard(argin)
+ argout = self.db.get_device_wide_list(argin)
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceWideList
return argout
@@ -1285,6 +1444,13 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceMemberList) ENABLED START -----#
+ time_before = time.time()
+
+ argin = replace_wildcard(argin)
+ argout = self.db.get_device_member_list(argin)
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbGetDeviceMemberList")
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceMemberList
return argout
@@ -1313,6 +1479,13 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceProperty) ENABLED START -----#
+ time_before = time.time()
+
+ device_name = argin[0]
+ argout = self.db.get_device_property(device_name, argin[1:])
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbGetDeviceProperty")
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceProperty
return argout
@@ -1323,7 +1496,7 @@ class DataBase (PyTango.Device_4Impl):
""" Retrieve device property history
:param argin: Str[0] = Device name
- Str[2] = Property name
+ Str[1] = Property name
:type: PyTango.DevVarStringArray
:return: Str[0] = Property name
Str[1] = date
@@ -1335,6 +1508,11 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDevicePropertyHist) ENABLED START -----#
+ device_name = argin[0]
+ prop_name = argin[1]
+ argout = self.db.get_device_property_hist(device_name, prop_name)
+
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDevicePropertyHist
return argout
@@ -1354,6 +1532,17 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDevicePropertyList) ENABLED START -----#
+ time_before = time.time()
+
+ device_name = argin[0]
+ prop_filter = argin[1]
+
+ prop_filter = replace_wildcard(prop_filter)
+
+ argout = self.db.get_device_property_list(device_name, prop_filter)
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbGetDevicePropertyList")
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDevicePropertyList
return argout
@@ -1370,6 +1559,10 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbGetDeviceServerClassList()")
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDeviceServerClassList) ENABLED START -----#
+
+ argin = replace_wildcard(argin)
+
+ argout = self.db.get_server_class_list(argin)
#----- PROTECTED REGION END -----# // DataBase.DbGetDeviceServerClassList
return argout
@@ -1387,6 +1580,10 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbGetExportdDeviceListForClass()")
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetExportdDeviceListForClass) ENABLED START -----#
+
+ argin = replace_wildcard(argin)
+
+ argout = self.db.get_exported_device_list_for_class(argin)
#----- PROTECTED REGION END -----# // DataBase.DbGetExportdDeviceListForClass
return argout
@@ -1405,6 +1602,14 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetHostList) ENABLED START -----#
+ time_before = time.time()
+
+ argin = replace_wildcard(argin)
+ argout = self.db.get_host_list(argin)
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbGetHostList")
+
#----- PROTECTED REGION END -----# // DataBase.DbGetHostList
return argout
@@ -1423,6 +1628,15 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetHostServerList) ENABLED START -----#
+ time_before = time.time()
+
+ argin = replace_wildcard(argin)
+ argout = self.db.get_host_server_list(argin)
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbGetHostServerList")
+
+
#----- PROTECTED REGION END -----# // DataBase.DbGetHostServerList
return argout
@@ -1439,6 +1653,10 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbGetHostServersInfo()")
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetHostServersInfo) ENABLED START -----#
+
+ argin = replace_wildcard(argin)
+
+ argout = self.db.get_host_servers_info(argin)
#----- PROTECTED REGION END -----# // DataBase.DbGetHostServersInfo
return argout
@@ -1456,7 +1674,9 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbGetInstanceNameList()")
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetInstanceNameList) ENABLED START -----#
-
+
+ argout = self.db.get_instance_name_list(argin)
+
#----- PROTECTED REGION END -----# // DataBase.DbGetInstanceNameList
return argout
@@ -1474,7 +1694,10 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbGetObjectList()")
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetObjectList) ENABLED START -----#
-
+
+ argin = replace_wildcard(argin)
+ argout = self.db.get_object_list(argin)
+
#----- PROTECTED REGION END -----# // DataBase.DbGetObjectList
return argout
@@ -1503,6 +1726,9 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetProperty) ENABLED START -----#
+ object_name = argin[0]
+ argout = self.db.get_property(object_name, argin[1:])
+
#----- PROTECTED REGION END -----# // DataBase.DbGetProperty
return argout
@@ -1525,6 +1751,10 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetPropertyHist) ENABLED START -----#
+ object_name = argin[0]
+ prop_name = argin[1]
+ argout = self.db.get_property_hist(object_name, prop_name)
+
#----- PROTECTED REGION END -----# // DataBase.DbGetPropertyHist
return argout
@@ -1544,6 +1774,11 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetPropertyList) ENABLED START -----#
+ object_name = argin[0]
+ wildcard = replace_wildcard(argin[1])
+
+ argout = self.db.get_property_list(object_name, wildcard)
+
#----- PROTECTED REGION END -----# // DataBase.DbGetPropertyList
return argout
@@ -1560,6 +1795,8 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbGetServerInfo()")
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetServerInfo) ENABLED START -----#
+
+ argout = self.db.get_server_info(argin)
#----- PROTECTED REGION END -----# // DataBase.DbGetServerInfo
return argout
@@ -1579,6 +1816,14 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetServerList) ENABLED START -----#
+ time_before = time.time()
+
+ argin = replace_wildcard(argin)
+ argout = self.db.get_server_list(argin)
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbGetServerList")
+
#----- PROTECTED REGION END -----# // DataBase.DbGetServerList
return argout
@@ -1595,7 +1840,10 @@ class DataBase (PyTango.Device_4Impl):
:rtype: PyTango.DevVarStringArray """
self.debug_stream("In " + self.get_name() + ".DbGetServerNameList()")
argout = ['']
- #----- PROTECTED REGION ID(DataBase.DbGetServerNameList) ENABLED START -----#
+ #----- PROTECTED REGION ID(DataBase.DbGetServerNameList) ENABLED START ----
+
+ argin = replace_wildcard(argin)
+ argout = self.db.get_server_name_list(argin)
#----- PROTECTED REGION END -----# // DataBase.DbGetServerNameList
return argout
@@ -1622,6 +1870,13 @@ class DataBase (PyTango.Device_4Impl):
argout = [0], ['']
#----- PROTECTED REGION ID(DataBase.DbImportDevice) ENABLED START -----#
+ time_before = time.time()
+
+ argout = self.db.import_device(argin.lower())
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbImportDevice")
+
#----- PROTECTED REGION END -----# // DataBase.DbImportDevice
return argout
@@ -1639,6 +1894,16 @@ class DataBase (PyTango.Device_4Impl):
argout = [0], ['']
#----- PROTECTED REGION ID(DataBase.DbImportEvent) ENABLED START -----#
+ time_before = time.time()
+
+ argin = replace_wildcard(argin.lower())
+
+ argout = self.db.import_event(argin)
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbImportEvent")
+
+
#----- PROTECTED REGION END -----# // DataBase.DbImportEvent
return argout
@@ -1666,6 +1931,13 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbInfo) ENABLED START -----#
+ time_before = time.time()
+
+ argout = self.db.info()
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbInfo")
+
#----- PROTECTED REGION END -----# // DataBase.DbInfo
return argout
@@ -1683,6 +1955,16 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbPutAttributeAlias()")
#----- PROTECTED REGION ID(DataBase.DbPutAttributeAlias) ENABLED START -----#
+ if len(argin) < 2:
+ self.warn_stream("DataBase::DbPutAttributeAlias(): insufficient number of arguments ")
+ th_exc(DB_IncorrectArguments,
+ "insufficient number of arguments to put attribute alias",
+ "DataBase::DbPutAttributeAlias()")
+
+ attribute_name = argin[0]
+ attribute_alias = argin[1]
+ self.db.put_attribute_alias(attribute_name, attribute_alias)
+
#----- PROTECTED REGION END -----# // DataBase.DbPutAttributeAlias
#------------------------------------------------------------------
@@ -1703,7 +1985,16 @@ class DataBase (PyTango.Device_4Impl):
:rtype: PyTango.DevVoid """
self.debug_stream("In " + self.get_name() + ".DbPutClassAttributeProperty()")
#----- PROTECTED REGION ID(DataBase.DbPutClassAttributeProperty) ENABLED START -----#
-
+
+ class_name = argin[0]
+ nb_attributes = int(argin[1])
+
+
+ self.info_stream("DataBase::DbPutClassAttributeProperty(): put " + str(nb_attributes) + " attributes for class " + class_name)
+
+ self.db.put_class_attribute_property(class_name, nb_attributes, argin[2:])
+
+
#----- PROTECTED REGION END -----# // DataBase.DbPutClassAttributeProperty
#------------------------------------------------------------------
@@ -1727,7 +2018,13 @@ class DataBase (PyTango.Device_4Impl):
:rtype: PyTango.DevVoid """
self.debug_stream("In " + self.get_name() + ".DbPutClassAttributeProperty2()")
#----- PROTECTED REGION ID(DataBase.DbPutClassAttributeProperty2) ENABLED START -----#
-
+ class_name = argin[0]
+ nb_attributes = int(argin[1])
+
+ self.info_stream("DataBase::DbPutClassAttributeProperty2(): put " + str(nb_attributes) + " attributes for class " + class_name)
+
+ self.db.put_class_attribute_property2(class_name, nb_attributes, argin[2:])
+
#----- PROTECTED REGION END -----# // DataBase.DbPutClassAttributeProperty2
#------------------------------------------------------------------
@@ -1749,6 +2046,19 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbPutClassProperty()")
#----- PROTECTED REGION ID(DataBase.DbPutClassProperty) ENABLED START -----#
+ time_before = time.time()
+
+ class_name = argin[0]
+ nb_properties = int(argin[1])
+
+ self.info_stream("DataBase::DbPutClassProperty(): put " + str(nb_properties) + " attributes for class " + class_name)
+
+ self.db.put_class_property(class_name, properties, argin[2:])
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbPutClassProperty")
+
+
#----- PROTECTED REGION END -----# // DataBase.DbPutClassProperty
#------------------------------------------------------------------
@@ -1765,6 +2075,18 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbPutDeviceAlias()")
#----- PROTECTED REGION ID(DataBase.DbPutDeviceAlias) ENABLED START -----#
+ if len(argin) < 2:
+ self.warn_stream("DataBase::DbPutDeviceAlias(): insufficient number of arguments ")
+ th_exc(DB_IncorrectArguments,
+ "insufficient number of arguments to put device alias",
+ "DataBase::DbPutDeviceAlias()")
+
+ device_name = argin[0]
+ device_alias = argin[1]
+ self.db.put_device_alias(device_name, device_alias)
+
+
+
#----- PROTECTED REGION END -----# // DataBase.DbPutDeviceAlias
#------------------------------------------------------------------
@@ -1786,6 +2108,18 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbPutDeviceAttributeProperty()")
#----- PROTECTED REGION ID(DataBase.DbPutDeviceAttributeProperty) ENABLED START -----#
+ time_before = time.time()
+
+ device_name = argin[0]
+ nb_attributes = int(argin[1])
+
+ self.info_stream("DataBase::DbPutDeviceAttributeProperty(): put " + str(nb_attributes) + " attributes for device " + device_name)
+
+ self.db.put_device_attribute_property(device_name, nb_attributes, argin[2:])
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbPutDeviceAttributeProperty")
+
#----- PROTECTED REGION END -----# // DataBase.DbPutDeviceAttributeProperty
#------------------------------------------------------------------
@@ -1811,6 +2145,19 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbPutDeviceAttributeProperty2()")
#----- PROTECTED REGION ID(DataBase.DbPutDeviceAttributeProperty2) ENABLED START -----#
+ time_before = time.time()
+
+ device_name = argin[0]
+ nb_attributes = int(argin[1])
+
+ self.info_stream("DataBase::DbPutDeviceAttributeProperty2(): put " + str(nb_attributes) + " attributes for device " + device_name)
+
+ self.db.put_device_attribute_property2(device_name, nb_attributes, argin[2:])
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbPutDeviceAttributeProperty2")
+
+
#----- PROTECTED REGION END -----# // DataBase.DbPutDeviceAttributeProperty2
#------------------------------------------------------------------
@@ -1832,6 +2179,18 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbPutDeviceProperty()")
#----- PROTECTED REGION ID(DataBase.DbPutDeviceProperty) ENABLED START -----#
+ time_before = time.time()
+
+ device_name = argin[0]
+ nb_properties = int(argin[1])
+
+ self.info_stream("DataBase::DbPutDeviceProperty(): put " + str(nb_properties) + " attributes for device " + device_name)
+
+ self.db.put_device_property(device_name, properties, argin[2:])
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbPutDeviceProperty")
+
#----- PROTECTED REGION END -----# // DataBase.DbPutDeviceProperty
#------------------------------------------------------------------
@@ -1853,6 +2212,13 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbPutProperty()")
#----- PROTECTED REGION ID(DataBase.DbPutProperty) ENABLED START -----#
+ object_name = argin[0]
+ nb_properties = int(argin[1])
+
+ self.info_stream("DataBase::DbPutProperty(): put " + str(nb_properties) + " attributes for object " + object_name)
+
+ self.db.put_property(object_name, properties, argin[2:])
+
#----- PROTECTED REGION END -----# // DataBase.DbPutProperty
#------------------------------------------------------------------
@@ -1868,6 +2234,25 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbPutServerInfo()")
#----- PROTECTED REGION ID(DataBase.DbPutServerInfo) ENABLED START -----#
+ if len(argin) < 4:
+ self.warn_stream("DataBase::DbPutServerInfo(): insufficient number of arguments ")
+ th_exc(DB_IncorrectArguments,
+ "insufficient server info",
+ "DataBase::DbPutServerInfo()")
+
+ tmp_server = argin[0].lower()
+ tmp_host = argin[1]
+ tmp_mode = argin[2]
+ tmp_level = argin[3]
+ tmp_extra = []
+ if len(argin) > 4:
+ tmp_extra = argin[4:]
+
+ tmp_len = len(argin) - 1
+ self.info_stream("DataBase::DbPutServerInfo(): put " + tmp_len + " export info for device " + tmp_server)
+
+ self.db.put_server_info(tmp_server, tmp_host, tmp_mode, tmp_level, tmp_extra)
+
#----- PROTECTED REGION END -----# // DataBase.DbPutServerInfo
#------------------------------------------------------------------
@@ -1883,6 +2268,10 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbUnExportDevice()")
#----- PROTECTED REGION ID(DataBase.DbUnExportDevice) ENABLED START -----#
+ dev_name = argin[0].lower()
+
+ self.db.unexport_device(dev_name)
+
#----- PROTECTED REGION END -----# // DataBase.DbUnExportDevice
#------------------------------------------------------------------
@@ -1898,6 +2287,10 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbUnExportEvent()")
#----- PROTECTED REGION ID(DataBase.DbUnExportEvent) ENABLED START -----#
+ event_name = argin[0].lower()
+
+ self.db.unexport_event(event_name)
+
#----- PROTECTED REGION END -----# // DataBase.DbUnExportEvent
#------------------------------------------------------------------
@@ -1914,6 +2307,15 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbUnExportServer()")
#----- PROTECTED REGION ID(DataBase.DbUnExportServer) ENABLED START -----#
+ time_before = time.time()
+
+ server_name = argin[0].lower()
+
+ self.db.unexport_server(server_name)
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbUnExportServer")
+
#----- PROTECTED REGION END -----# // DataBase.DbUnExportServer
#------------------------------------------------------------------
@@ -1928,6 +2330,12 @@ class DataBase (PyTango.Device_4Impl):
:rtype: PyTango.DevVoid """
self.debug_stream("In " + self.get_name() + ".ResetTimingValues()")
#----- PROTECTED REGION ID(DataBase.ResetTimingValues) ENABLED START -----#
+ for tmp_timing in timing_maps.itervalues():
+ tmp_timing.average = 0.
+ tmp_timing.minimum = 0.
+ tmp_timing.maximum = 0.
+ tmp_timing.total_elapsed = 0.
+ tmp_timing.calls = 0.
#----- PROTECTED REGION END -----# // DataBase.ResetTimingValues
@@ -1947,6 +2355,13 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetDataForServerCache) ENABLED START -----#
+ time_before = time.time()
+
+ ## TODO
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbDbGetDataForServerCache")
+
#----- PROTECTED REGION END -----# // DataBase.DbGetDataForServerCache
return argout
@@ -1964,6 +2379,23 @@ class DataBase (PyTango.Device_4Impl):
self.debug_stream("In " + self.get_name() + ".DbDeleteAllDeviceAttributeProperty()")
#----- PROTECTED REGION ID(DataBase.DbDeleteAllDeviceAttributeProperty) ENABLED START -----#
+ if len(argin) < 2:
+ self.warn_stream("DataBase::DbDeleteAllDeviceAttributeProperty(): insufficient number of arguments ")
+ th_exc(DB_IncorrectArguments,
+ "insufficient number of arguments to delete all device attribute(s) property",
+ "DataBase::DbDeleteAllDeviceAttributeProperty()")
+
+ dev_name = argin[0]
+
+ ret, d_name, dfm = check_device_name(dev_name)
+
+ if not ret:
+ th_exc(DB_IncorrectDeviceName,
+ "device name (" + argin + ") syntax error (should be [tango:][//instance/]domain/family/member)",
+ "DataBase::DbDeleteAllDeviceAttributeProperty()")
+
+ self.db.delete_all_device_attribute_property(dev_name, argin[1:])
+
#----- PROTECTED REGION END -----# // DataBase.DbDeleteAllDeviceAttributeProperty
#------------------------------------------------------------------
@@ -1984,6 +2416,30 @@ class DataBase (PyTango.Device_4Impl):
argout = [0], ['']
#----- PROTECTED REGION ID(DataBase.DbMySqlSelect) ENABLED START -----#
+ time_before = time.time()
+
+ tmp_argin = argin.lower()
+
+ # Check if SELECT key is alread inside command
+
+ cmd = argin
+ tmp_argin = argin.lower()
+ pos = tmp_argin.find('select')
+ if pos == -1:
+ cmd = "SELECT " + cmd
+
+ pos = tmp_argin.find(';')
+ if pos != -1 and len(tmp_argin) > (pos + 1):
+ th_exc(DB_IncorrectArguments,
+ "SQL command not valid: " + argin,
+ "DataBase::ExportDevice()")
+ logging.info("DataBase::db_my_sql_select(): \n %s" % cmd)
+
+ argout = self.db.my_sql_select(cmd)
+
+ time_after = time.time()
+ self.update_timing_stats(time_before, time_after, "DbMySqlSelect")
+
#----- PROTECTED REGION END -----# // DataBase.DbMySqlSelect
return argout
@@ -2001,6 +2457,8 @@ class DataBase (PyTango.Device_4Impl):
argout = ['']
#----- PROTECTED REGION ID(DataBase.DbGetCSDbServerList) ENABLED START -----#
+ argout = self.db.get_csdb_server_list()
+
#----- PROTECTED REGION END -----# // DataBase.DbGetCSDbServerList
return argout
@@ -2019,6 +2477,9 @@ class DataBase (PyTango.Device_4Impl):
argout = ''
#----- PROTECTED REGION ID(DataBase.DbGetAttributeAlias2) ENABLED START -----#
+ attr_name = argin[0]
+ argout = self.db.get_attribute_alias2(attr_name)
+
#----- PROTECTED REGION END -----# // DataBase.DbGetAttributeAlias2
return argout
@@ -2037,9 +2498,47 @@ class DataBase (PyTango.Device_4Impl):
argout = ''
#----- PROTECTED REGION ID(DataBase.DbGetAliasAttribute) ENABLED START -----#
+ alias_name = argin[0]
+ argout = self.db.get_alias_attribute(alias_name)
+
#----- PROTECTED REGION END -----# // DataBase.DbGetAliasAttribute
return argout
+#------------------------------------------------------------------
+# DbRenameServer command:
+#------------------------------------------------------------------
+ def DbRenameServer(self, argin):
+ """ Rename a device server process
+
+ :param argin: str[0] = old device server name (exec/instance)
+ str[1] = new device server name (exec/instance)
+ :type: PyTango.DevVarStringArray
+ :return:
+ :rtype: PyTango.DevVoid """
+ self.debug_stream("In " + self.get_name() + ".DbRenameServer()")
+ #----- PROTECTED REGION ID(DataBase.DbRenameServer) ENABLED START -----#
+
+ if len(argin) < 2:
+ self.warn_stream("DataBase::DbRenameServer(): insufficient number of arguments ")
+ th_exc(DB_IncorrectArguments,
+ "insufficient number of arguments (two required: old name and new name",
+ "DataBase::DbRenameServer")
+
+
+ old_name = argin[0]
+ new_name = argin[1]
+
+ if ('/' not in argin[0]) or ('/' not in argin[1]):
+ self.warn_stream("DataBase::DbRenameServer(): wrong syntax in command args ")
+ th_exc(DB_IncorrectArguments,
+ "Wrong syntax in command args (ds_exec_name/inst_name)",
+ "DataBase::DbRenameServer")
+
+ self.db.rename_server(old_name, new_name)
+
+
+
+ #----- PROTECTED REGION END -----# // DataBase.DbRenameServer
#==================================================================
#
@@ -2306,6 +2805,9 @@ class DataBaseClass(PyTango.DeviceClass):
'DbGetAliasAttribute':
[[PyTango.DevString, "The attribute alias"],
[PyTango.DevString, "The attribute name (dev_name/att_name)"]],
+ 'DbRenameServer':
+ [[PyTango.DevVoid, "none"],
+ [PyTango.DevVarStringArray, "s[0] = old device server name (exec/instance)\ns[1] = new device server name (exec/instance)"]],
}
@@ -2348,55 +2850,123 @@ class DataBaseClass(PyTango.DeviceClass):
def __init__(self, name):
PyTango.DeviceClass.__init__(self, name)
self.set_type(name);
- print "In DataBase Class constructor"
def device_factory(self, names):
names = [get_db_name()]
return PyTango.DeviceClass.device_factory(self, names)
+ def device_factory(self, device_list):
+ """for internal usage only"""
+
+ dev_name = get_db_name()
+
+ klass = self.__class__
+ klass_name = klass.__name__
+ info, klass = get_class_by_class(klass), get_constructed_class_by_class(klass)
+
+ if info is None:
+ raise RuntimeError("Device class '%s' is not registered" % klass_name)
+
+ if klass is None:
+ raise RuntimeError("Device class '%s' as not been constructed" % klass_name)
+
+ deviceClassClass, deviceImplClass, deviceImplName = info
+ deviceImplClass._device_class_instance = klass
+
+ device = deviceImplClass(klass, dev_name)
+ self._add_device(device)
+ tmp_dev_list = [device]
+
+ self.dyn_attr(tmp_dev_list)
+
+ self.export_device(device, "database")
+ self.py_dev_list += tmp_dev_list
+
#==================================================================
#
# DataBase class main method
#
#==================================================================
def main():
+ #Parameters management
+ global options
+ if argparse:
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--db_access",dest="db_access",default="sqlite3",
+ help="database type")
+ parser.add_argument("-e", "--embedded",dest="embedded",default=False,
+ action="store_true")
+ parser.add_argument('argv',nargs=argparse.REMAINDER)
+ options = parser.parse_args()
+ options.argv = ["DataBaseds"] + options.argv
+ else:
+ parser = OptionParser()
+ parser.add_option("--db_access",dest="db_access",default="sqlite3",
+ help="database type")
+ parser.add_option("-e","--embedded",dest="embedded",default=False,
+ action="store_true")
+ (options,args) = parser.parse_args()
+ options.argv = ["DataBaseds"] + args
+
log_fmt = '%(threadName)-14s %(levelname)-8s %(asctime)s %(name)s: %(message)s'
logging.basicConfig(format=log_fmt, stream=sys.stdout, level=logging.INFO)
try:
- db_name = "sys/database/" + sys.argv[1]
+ db_name = "sys/database/" + options.argv[1]
set_db_name(db_name)
- PyTango.Util.set_use_db(False)
- py = PyTango.Util(sys.argv)
- py.add_class(DataBaseClass, DataBase, 'DataBase')
-
- U = PyTango.Util.instance()
- dbi = DbInter()
- U.set_interceptors(dbi)
- U.set_serial_model(PyTango.SerialModel.NO_SYNC)
- U.server_init()
- dserver = U.get_dserver_device()
- dbase = U.get_device_by_name(db_name)
-
- dserver.duplicate_d_var()
-
- pars = [dserver.get_name(), U.get_dserver_ior(dserver),
- U.get_host_name(), U.get_pid_str(), U.get_version_str()]
- dbase.DbExportDevice(pars)
-
- pars[0] = dbase.get_name()
- pars[1] = U.get_device_ior(dbase)
- dbase.DbExportDevice(pars)
-
- print "Ready to accept request"
- U.orb_run()
-
-
- except PyTango.DevFailed as df:
- print '-------> Received a DevFailed exception:', df
- import traceback;traceback.print_exc()
+ if options.embedded:
+ __run_embedded(db_name, options.argv)
+ else:
+ __run(db_name, options.argv)
except Exception as e:
- print '-------> An unforeseen exception occured....', e
- import traceback;traceback.print_exc()
-
+ import traceback
+ traceback.print_exc()
+
+def __run(db_name,argv):
+ """
+ Runs the Database DS as a standalone database. Run it with::
+
+ ./DataBaseds pydb-test -ORBendPoint giop:tcp::11000
+ """
+ PyTango.Util.set_use_db(False)
+ py_util = PyTango.Util(argv)
+ py_util.add_class(DataBaseClass, DataBase, 'DataBase')
+
+ util = PyTango.Util.instance()
+ dbi = DbInter()
+ util.set_interceptors(dbi)
+ #util.set_serial_model(PyTango.SerialModel.NO_SYNC)
+ util.server_init()
+
+ dserver = util.get_dserver_device()
+ dserver_name = dserver.get_name()
+ dserver_ior = util.get_dserver_ior(dserver)
+
+ dbase = util.get_device_by_name(db_name)
+ dbase_name = dbase.get_name()
+ dbase_ior = util.get_device_ior(dbase)
+
+ host = util.get_host_name()
+ pid = util.get_pid_str()
+ version = util.get_version_str()
+
+ dbase.DbExportDevice([dserver_name, dserver_ior, host, pid, version])
+ dbase.DbExportDevice([dbase_name, dbase_ior, host, pid, version])
+
+ print("Ready to accept request")
+ util.orb_run()
+# util.server_run()
+
+def __run_embedded(db_name,argv):
+ """Runs the Database device server embeded in another TANGO Database
+ (just like any other TANGO device server)"""
+
+ py_util = PyTango.Util(argv)
+ py_util.add_class(DataBaseClass, DataBase, 'DataBase')
+
+ util = PyTango.Util.instance()
+ util.server_init()
+ print("Ready to accept request")
+ util.server_run()
+
if __name__ == '__main__':
main()
diff --git a/src/boost/python/databaseds/db_access.py b/src/boost/python/databaseds/db_access.py
deleted file mode 100644
index 6faa291..0000000
--- a/src/boost/python/databaseds/db_access.py
+++ /dev/null
@@ -1,517 +0,0 @@
-from __future__ import print_function
-
-import os
-import logging
-import functools
-
-import PyTango
-
-th_exc = PyTango.Except.throw_exception
-
-from db_errors import *
-
-def get_create_db_statements():
- statements = []
- with open("create_db_tables.sql") as f:
- lines = f.readlines()
- # strip comments
- lines = (line for line in lines if not line.startswith('#'))
- lines = (line for line in lines if not line.lower().strip().startswith('key'))
- lines = (line for line in lines if not line.lower().strip().startswith('key'))
- lines = "".join(lines)
- lines = lines.replace("ENGINE=MyISAM", "")
- statements += lines.split(";")
-
- with open("create_db.sql") as f:
- lines = f.readlines()
- # strip comments
- lines = (line for line in lines if not line.lower().startswith('#'))
- lines = (line for line in lines if not line.lower().startswith('create database'))
- lines = (line for line in lines if not line.lower().startswith('use'))
- lines = (line for line in lines if not line.lower().startswith('source'))
- lines = "".join(lines)
- statements += lines.split(";")
-
- return statements
-
-def replace_wildcard(text):
- # escape '%' with '\'
- text = text.replace("%", "\\%")
- # escape '_' with '\'
- text = text.replace("_", "\\_")
- # escape '"' with '\'
- text = text.replace('"', '\\"')
- # escape ''' with '\'
- text = text.replace("'", "\\'")
- # replace '*' with '%'
- text = text.replace("*", "%")
- return text
-
-def use_cursor(f):
- @functools.wraps(f)
- def wrap(*args, **kwargs):
- self = args[0]
- has_cursor = 'cursor' in kwargs
- cursor = kwargs.pop('cursor', None)
- if not has_cursor:
- cursor = self.get_cursor()
- self.cursor = cursor
- try:
- ret = f(*args, **kwargs)
- if not has_cursor:
- cursor.connection.commit()
- return ret
- finally:
- if not has_cursor:
- cursor.close()
- del self.cursor
- return wrap
-
-class Tango_dbapi2(object):
-
- DB_API_NAME = 'sqlite3'
-
- def __init__(self, db_name="tango_database.db", history_depth=10, fire_to_starter=True):
- self._db_api = None
- self._db_conn = None
- self.db_name = db_name
- self.history_depth = history_depth;
- self.fire_to_starter = fire_to_starter
- self._logger = logging.getLogger(self.__class__.__name__)
- self._debug = self._logger.info
- self._info = self._logger.info
- self._warn = self._logger.warn
- self._error = self._logger.error
- self._critical = self._logger.critical
- self.initialize()
-
- def close_db(self):
- if self._db_conn is not None:
- self._db_conn.commit()
- self._db_conn.close()
- self._db_api = None
- self._db_conn = None
-
- def get_db_api(self):
- if self._db_api is None:
- self._db_api = __import__(self.DB_API_NAME)
- return self._db_api
-
- @property
- def db_api(self):
- return self.get_db_api()
-
- @property
- def db_conn(self):
- if self._db_conn is None:
- self._db_conn = self.db_api.connect(self.db_name)
- return self._db_conn
-
- def get_cursor(self):
- return self.db_conn.cursor()
-
- def initialize(self):
- self._info("Initializing database...")
- if not os.path.isfile(self.db_name):
- self.create_db()
-
- @use_cursor
- def create_db(self):
- self._info("Creating database...")
- statements = get_create_db_statements()
- cursor = self.cursor
- for statement in statements:
- cursor.execute(statement)
-
- @use_cursor
- def get_id(self, name):
- cursor = self.cursor
- name += '"_history_id'
- _id = cursor.execute('SELECT id FROM ?', (name,)).fetchone()[0] + 1
- cursor.execute('UPDATE ? SET id=?', (name, _id))
- return _id
-
- @use_cursor
- def purge_att_property(self, table, field, obj, attr, name):
- cursor = self.cursor
- cursor.execute(\
- 'SELECT DISTINCT id FROM ? WHERE ? = ? AND name = ? AND ' \
- 'attribute = ? ORDER BY date', (table, field, obj, name, attr))
- rows = cursor.fetchall()
- to_del = len(rows) - self.history_depth
- if to_del > 0:
- for row in rows[:to_del]:
- cursor.execute('DELETE FROM ? WHERE id=?', (table, row[0]))
-
- @use_cursor
- def purge_property(self, table, field, obj, name):
- cursor = self.cursor
- cursor.execute(\
- 'SELECT DISTINCT id FROM ? WHERE ? = ? AND name = ? ' \
- 'ORDER BY date', (table, field, obj, name))
- rows = cursor.fetchall()
- to_del = len(rows) - self.history_depth
- if to_del > 0:
- for row in rows[:to_del]:
- cursor.execute('DELETE FROM ? WHERE id=?', (table, row[0]))
-
- @use_cursor
- def get_device_host(self, name):
- cursor = self.cursor
- name = replace_wildcard(name)
- cursor.execute('SELECT host FROM device WHERE name LIKE ?', (name,))
- row = cursor.fetchone()
- if row is None:
- raise Exception("No host for device '" + name + "'")
- else:
- return row[0]
-
- # TANGO API
-
- def get_stored_procedure_release(self):
- return 'release 1.8'
-
- @use_cursor
- def add_device(self, server_name, dev_info, klass_name, alias=None):
- self._info("delete_attribute_alias(server_name=%s, dev_info=%s, klass_name=%s, alias=%s)",
- server_name, dev_info, klass_name, alias)
- dev_name, (domain, family, member) = dev_info
- cursor = self.cursor
-
- # first delete the tuple (device,name) from the device table
- cursor.execute('DELETE FROM device WHERE name LIKE ?', (dev_name,))
-
- # then insert the new value for this tuple
- cursor.execute(\
- 'INSERT INTO device (name, alias, domain, family, member, exported, ' \
- 'ior, host, server, pid, class, version, started, stopped) ' \
- 'VALUES (?, ?, ?, ?, ?, 0, "nada", "nada", ?, 0, ?, "0", NULL, NULL)',
- (dev_name, alias, domain, family, member, server_name, klass_name))
-
- # Check if a DServer device entry for the process already exists
- cursor.execute('SELECT name FROM device WHERE server LIKE ? AND class LIKE "DServer"', (server_name,))
- if cursor.fetchone() is None:
- dev_name = "dserver/" + server_name
- domain, family, member = dev_name.split("/", 2)
- cursor.execute(\
- 'INSERT INTO device (name, domain, family, member, exported, ior, ' \
- 'host, server, pid, class, version, started, stopped) ' \
- 'VALUES (?, ?, ?, ?, 0, "nada", "nada", ?, 0, "DServer", "0", NULL, NULL)',
- (dev_name, domain, family, member, server_name))
-
- @use_cursor
- def delete_attribute_alias(self, alias):
- self._info("delete_attribute_alias(alias=%s)", alias)
- self.cursor.execute('DELETE FROM attribute_alias WHERE alias=?', (alias,))
-
- @use_cursor
- def delete_class_attribute(self, klass_name, attr_name):
- self.cursor.execute(\
- 'DELETE FROM property_attribute_class WHERE class LIKE ? AND ' \
- 'attribute LIKE ?', (klass_name, attr_name))
-
- @use_cursor
- def delete_class_attribute_property(self, klass_name, attr_name, prop_name):
- cursor = self.cursor
-
- # Is there something to delete ?
- cursor.execute(\
- 'SELECT count(*) FROM property_attribute_class WHERE class = ? ' \
- 'AND attribute = ? AND name = ?', (klass_name, attr_name, prop_name))
- if cursor.fetchone()[0] > 0:
- # then delete property from the property_attribute_class table
- cursor.execute(\
- 'DELETE FROM property_attribute_class WHERE class = ? AND ' \
- 'attribute = ? and name = ?', (klass_name, attr_name, prop_name))
- # mark this property as deleted
- hist_id = self.get_id('class_attibute', cursor=cursor)
- cursor.execute(\
- 'INSERT INTO property_attribute_class_hist (class, attribute, ' \
- 'name, id, count, value) VALUES ' \
- '(?, ?, ?, ?, "0", "DELETED")',
- (klass_name, attr_name, prop_name, hist_id))
- self.purge_att_property("property_attribute_class_hist", "class",
- klass_name, attr_name, prop_name, cursor=cursor)
-
- @use_cursor
- def delete_class_property(self, klass_name, prop_name):
- cursor = self.cursor
-
- prop_name = replace_wildcard(prop_name)
- # Is there something to delete ?
- cursor.execute(\
- 'SELECT DISTINCT name FROM property_class WHERE class=? AND ' \
- 'name LIKE ?', (klass_name, prop_name))
- for row in cursor.fetchall():
- # delete the tuple (device,name,count) from the property table
- name = row[0]
- cursor.execute(\
- 'DELETE FROM property_class WHERE class=? AND name=?',
- (klass_name, name))
- # Mark this property as deleted
- hist_id = self.get_id("class", cursor=cursor)
- cursor.execute(\
- 'INSERT INTO property_class_hist (class, name, id, count, value) ' \
- 'VALUES (?, ?, ?, "0", "DELETED")',
- (klass_name, name, hist_id))
- self.purge_property("property_class_hist", "class", klass_name,
- name, cursor=cursor)
-
- @use_cursor
- def delete_device(self, dev_name):
- self._info("delete_device(dev_name=%s)", dev_name)
- cursor = self.cursor
- dev_name = replace_wildcard(dev_name)
-
- # delete the device from the device table
- cursor.execute('DELETE FROM device WHERE name LIKE ?', (dev_name,))
-
- # delete device from the property_device table
- cursor.execute('DELETE FROM property_device WHERE device LIKE ?', (dev_name,))
-
- # delete device from the property_attribute_device table
- cursor.execute('DELETE FROM property_attribute_device WHERE device LIKE ?', (dev_name,))
-
- @use_cursor
- def delete_device_alias(self, dev_alias):
- self._info("delete_device_alias(dev_alias=%s)", dev_alias)
- self.cursor.execute('UPDATE device SET alias=NULL WHERE alias=?', (dev_alias,))
-
- @use_cursor
- def delete_device_attribute(self, dev_name, attr_name):
- dev_name = replace_wildcard(dev_name)
- self.cursor.execute(\
- 'DELETE FROM property_attribute_device WHERE device LIKE ? AND ' \
- 'attribute LIKE ?', (dev_name, attr_name))
-
- @use_cursor
- def delete_device_attribute_property(self, dev_name, attr_name, prop_name):
- cursor = self.cursor
- # Is there something to delete ?
- cursor.execute(\
- 'SELECT count(*) FROM property_attribute_device WHERE device = ?' \
- 'AND attribute = ? AND name = ?', (dev_name, attr_name, prop_name))
- if cursor.fetchone()[0] > 0:
- # delete property from the property_attribute_device table
- cursor.execute(\
- 'DELETE FROM property_attribute_device WHERE device = ? AND '
- 'attribute = ? AND name = ?', (dev_name, attr_name, prop_name))
- # Mark this property as deleted
- hist_id = self.get_id("device_attribute", cursor=cursor)
- cursor.execute(\
- 'INSERT INTO property_attribute_device_hist ' \
- '(device, attribute, name, id, count, value) VALUES ' \
- '(?, ?, ?, ?, "0", "DELETED")', (dev_name, attr_name, prop_name, hist_id))
- self.purge_att_property("property_attribute_device_hist", "device",
- dev_name, attr_name, prop_name, cursor=cursor)
-
- @use_cursor
- def delete_device_property(self, dev_name, prop_name):
- cursor = self.cursor
- prop_name = replace_wildcard(prop_name)
-
- # Is there something to delete ?
- cursor.execute(\
- 'SELECT DISTINCT name FROM property_device WHERE device=? AND ' \
- 'name LIKE ?', (dev_name, prop_name))
- for row in cursor.fetchall():
- # delete the tuple (device,name,count) from the property table
- cursor.execute(\
- 'DELETE FROM property_device WHERE device=? AND name LIKE ?',
- (dev_name, prop_name))
- # Mark this property as deleted
- hist_id = self.get_id("device", cursor=cursor)
- cursor.execute(\
- 'INSERT INTO property_device_hist (device, id, name, count, value) ' \
- 'VALUES (?, ?, ?, "0", "DELETED")', (dev_name, hist_id, row[0]))
- self.purge_property("property_device_hist", "device", dev_name, row[0])
-
- @use_cursor
- def delete_property(self, obj_name, prop_name):
- cursor = self.cursor
- prop_name = replace_wildcard(prop_name)
-
- # Is there something to delete ?
- cursor.execute(\
- 'SELECT DISTINCT name FROM property WHERE object=? AND ' \
- 'name LIKE ?', (obj_name, prop_name))
- for row in cursor.fetchall():
- # delete the tuple (object,name,count) from the property table
- cursor.execute(\
- 'DELETE FROM property_device WHERE device=? AND name LIKE ?',
- (obj_name, prop_name))
- # Mark this property as deleted
- hist_id = self.get_id("object", cursor=cursor)
- cursor.execute(\
- 'INSERT INTO property_hist (object, name, id, count, value) ' \
- 'VALUES (?, ?, ?, "0", "DELETED")', (obj_name, row[0], hist_id))
- self.purge_property("property_hist", "object", obj_name, row[0])
-
- @use_cursor
- def delete_server(self, server_instance):
- cursor = self.cursor
- server_instance = replace_wildcard(server_instance)
-
- previous_host = None
- # get host where running
- if self.fire_to_starter:
- adm_dev_name = "dserver/" + server_instance
- previous_host = self.get_device_host(adm_dev_name)
-
- # then delete the device from the device table
- cursor.execute('DELETE FROM device WHERE server LIKE ?', (server_instance,))
-
- # Update host's starter to update controlled servers list
- if self.fire_to_starter and previous_host:
- # TODO send to starter
- pass
-
- @use_cursor
- def delete_server_info(self, server_instance):
- self.cursor.execute('DELETE FROM server WHERE name=?', (server_instance,))
-
- @use_cursor
- def export_device(self, dev_name, IOR, host, pid, version):
- self._info("export_device(dev_name=%s, IOR=%s, host=%s, pid=%s, version=%s)",
- dev_name, IOR, host, pid, version)
- cursor = self.cursor
- do_fire = False
- previous_host = None
- if self.fire_to_starter:
- # TODO send to starter
- pass
- cursor.execute('SELECT server FROM device WHERE name LIKE ?', (dev_name,))
- row = cursor.fetchone()
- if row is None:
- th_exc(DB_DeviceNotDefined,
- "device " + dev_name + " not defined in the database !",
- "DataBase::ExportDevice()")
- server = row[0]
-
- # update the new value for this tuple
- cursor.execute(\
- 'UPDATE device SET exported=1, ior=?, host=?, pid=?, version=?, ' \
- 'started=datetime("now") WHERE name LIKE ?',
- (IOR, host, pid, version, dev_name))
-
- # update host name in server table
- cursor.execute('UPDATE server SET host=? WHERE name LIKE ?', (host, server))
-
- if do_fire:
- # TODO send to starter
- pass
-
- @use_cursor
- def export_event(self, event, IOR, host, pid, version):
- cursor = self.cursor
- cursor.execute(\
- 'INSERT event (name,exported,ior,host,server,pid,version,started) ' \
- 'VALUES (?, 1, ?, ?, ?, ?, ?, datetime("now")',
- (event, IOR, host, event, pid, version))
-
- @use_cursor
- def get_alias_device(self, dev_alias):
- cursor = self.cursor
- cursor.execute('SELECT name FROM device WHERE alias LIKE ?',
- (dev_alias,))
- row = cursor.fetchone()
- if row is None:
- th_exc(DB_DeviceNotDefined,
- "No device found for alias '" + dev_alias + "'",
- "DataBase::GetAliasDevice()")
- return row[0]
-
- @use_cursor
- def get_attribute_alias(self, attr_alias):
- cursor = self.cursor
- cursor.execute('SELECT name from attribute_alias WHERE alias LIKE ?',
- (attr_alias,))
- row = cursor.fetchone()
- if row is None:
- th_exc(DB_SQLError,
- "No attribute found for alias '" + attr_alias + "'",
- "DataBase::GetAttributeAlias()")
- return row[0]
-
- @use_cursor
- def get_attribute_alias_list(self, attr_alias):
- cursor = self.cursor
- cursor.execute('SELECT DISTINCT alias FROM attribute_alias WHERE alias LIKE ? ORDER BY attribute',
- (attr_alias,))
- return [ row[0] for row in cursor.fetchall() ]
-
- @use_cursor
- def get_class_attribute_list(self, class_name, wildcard):
- cursor = self.cursor
- cursor.execute('SELECT DISTINCT attribute FROM property_attribute_class WHERE class=? and attribute like ?',
- (class_name, wildcard))
- return [ row[0] for row in cursor.fetchall() ]
-
- @use_cursor
- def get_class_attribute_property(self, clas_name, attributes):
- cursor = self.cursor
- stmt = 'SELECT name,value FROM property_attribute_class WHERE class=? AND attribute LIKE ?'
- result = [class_name, str(len(attributes))]
- for attribute in attributes:
- cursor.execute(stmt, (class_name, attribute))
- rows = cursor.fetchall()
- result.append(attribute)
- result.append(str(len(rows)))
- for row in rows:
- result.append(row[0])
- result.append(row[1])
- return result
-
- @use_cursor
- def get_class_attribute_property2(self, clas_name, attributes):
- cursor = self.cursor
- stmt = 'SELECT name,value FROM property_attribute_class WHERE class=? AND attribute LIKE ? ORDER BY name,count'
- result = [class_name, str(len(attributes))]
- # TODO: NOT DONE YET!
- for attribute in attributes:
- cursor.execute(stmt, (class_name, attribute))
- rows = cursor.fetchall()
- result.append(attribute)
- result.append(str(len(rows)))
- for row in rows:
- result.append(row[0])
- result.append(row[1])
- return result
-
- @use_cursor
- def get_class_for_device(self, dev_name):
- cursor = self.cursor
- cursor.execute('SELECT DISTINCT class FROM device WHERE name=?', (dev_name,))
- row = cursor.fetchone()
- if row is None:
- th_exc(DB_IncorrectArguments, "Class not found for " + dev_name,
- "Database.GetClassForDevice")
- return row
-
- @use_cursor
- def get_class_inheritance_for_device(self, dev_name):
- cursor = self.cursor
- class_name = self.get_class_for_device(dev_name, cursor=cursor)
- props = self.get_class_property(class_name, "InheritedFrom", cursor=cursor)
- return [class_name] + props[4:]
-
- @use_cursor
- def get_class_list(self, server):
- cursor = self.cursor
- cursor.execute('SELECT DISTINCT class FROM device WHERE class LIKE ? ORDER BY class', (server,))
- return [ row[0] for row in cursor.fetchall() ]
-
-
-
-class Tango_sqlite3(Tango_dbapi2):
-
- DB_API_NAME = 'sqlite3'
-
-
-def main():
- db = Tango_sqlite3()
- db.add_device("MyServer/my1", ("a/b/c", ("a", "b", "c")), "MyClass")
- db.close_db()
-
-if __name__ == "__main__":
- main()
diff --git a/src/boost/python/databaseds/db_access/__init__.py b/src/boost/python/databaseds/db_access/__init__.py
new file mode 100644
index 0000000..0a06f03
--- /dev/null
+++ b/src/boost/python/databaseds/db_access/__init__.py
@@ -0,0 +1,14 @@
+__all__ = []
+def _init_module() :
+ import os
+ for root,dirs,files in os.walk(__path__[0]) :
+ for file_name in files :
+ if file_name.startswith('__') : continue
+ base,ext = os.path.splitext(file_name)
+ if ext == '.py' :
+ subdir = root[len(__path__[0]) + 1:]
+ if subdir:
+ base = '%s.%s' % (subdir,base)
+ __all__.append(base)
+_init_module()
+
diff --git a/src/boost/python/databaseds/db_access/sqlite3.py b/src/boost/python/databaseds/db_access/sqlite3.py
new file mode 100644
index 0000000..f637355
--- /dev/null
+++ b/src/boost/python/databaseds/db_access/sqlite3.py
@@ -0,0 +1,1575 @@
+from __future__ import print_function
+
+import os
+import logging
+import functools
+import threading
+import Queue
+
+import PyTango
+
+th_exc = PyTango.Except.throw_exception
+
+from db_errors import *
+
+from concurrent.futures import ThreadPoolExecutor
+
+Executor = ThreadPoolExecutor(1)
+
+def get_create_db_statements():
+ statements = []
+ with open("create_db_tables.sql") as f:
+ lines = f.readlines()
+ # strip comments
+ lines = (line for line in lines if not line.startswith('#'))
+ lines = (line for line in lines if not line.lower().strip().startswith('key'))
+ lines = (line for line in lines if not line.lower().strip().startswith('key'))
+ lines = "".join(lines)
+ lines = lines.replace("ENGINE=MyISAM", "")
+ statements += lines.split(";")
+
+ with open("create_db.sql") as f:
+ lines = f.readlines()
+ # strip comments
+ lines = (line for line in lines if not line.lower().startswith('#'))
+ lines = (line for line in lines if not line.lower().startswith('create database'))
+ lines = (line for line in lines if not line.lower().startswith('use'))
+ lines = (line for line in lines if not line.lower().startswith('source'))
+ lines = "".join(lines)
+ statements += lines.split(";")
+
+ return statements
+
+def replace_wildcard(text):
+ # escape '%' with '\'
+ text = text.replace("%", "\\%")
+ # escape '_' with '\'
+ text = text.replace("_", "\\_")
+ # escape '"' with '\'
+ text = text.replace('"', '\\"')
+ # escape ''' with '\'
+ text = text.replace("'", "\\'")
+ # replace '*' with '%'
+ text = text.replace("*", "%")
+ return text
+
+def use_cursor(f):
+ @functools.wraps(f)
+ def wrap(*args, **kwargs):
+ self = args[0]
+ has_cursor = 'cursor' in kwargs
+ cursor = kwargs.pop('cursor', None)
+ if not has_cursor:
+ cursor = Executor.submit(self.get_cursor).result()
+ self.cursor = cursor
+ try:
+ ret = Executor.submit(f, *args, **kwargs).result()
+ if not has_cursor:
+ Executor.submit(cursor.connection.commit).result()
+ return ret
+ finally:
+ if not has_cursor:
+ Executor.submit(cursor.close).result()
+ del self.cursor
+ return wrap
+
+
+class Tango_dbapi2(object):
+
+ DB_API_NAME = 'sqlite3'
+
+ def __init__(self, db_name="tango_database.db", history_depth=10, fire_to_starter=True):
+ self._db_api = None
+ self._db_conn = None
+ self.db_name = db_name
+ self.history_depth = history_depth
+ self.fire_to_starter = fire_to_starter
+ self._logger = logging.getLogger(self.__class__.__name__)
+ self._debug = self._logger.debug
+ self._info = self._logger.info
+ self._warn = self._logger.warn
+ self._error = self._logger.error
+ self._critical = self._logger.critical
+ self.initialize()
+
+ def close_db(self):
+ if self._db_conn is not None:
+ self._db_conn.commit()
+ self._db_conn.close()
+ self._db_api = None
+ self._db_conn = None
+
+ def get_db_api(self):
+ if self._db_api is None:
+ self._db_api = __import__(self.DB_API_NAME)
+ return self._db_api
+
+ @property
+ def db_api(self):
+ return self.get_db_api()
+
+ @property
+ def db_conn(self):
+ if self._db_conn is None:
+ self._db_conn = self.db_api.connect(self.db_name)
+ return self._db_conn
+
+ def get_cursor(self):
+ return self.db_conn.cursor()
+
+ def initialize(self):
+ self._info("Initializing database...")
+ if not os.path.isfile(self.db_name):
+ self.create_db()
+ else:
+ # trigger connection
+ self.db_conn
+
+ @use_cursor
+ def create_db(self):
+ self._info("Creating database...")
+ statements = get_create_db_statements()
+ cursor = self.cursor
+ for statement in statements:
+ cursor.execute(statement)
+
+ @use_cursor
+ def get_id(self, name):
+ cursor = self.cursor
+ name += '"_history_id'
+ _id = cursor.execute('SELECT id FROM ?', (name,)).fetchone()[0] + 1
+ cursor.execute('UPDATE ? SET id=?', (name, _id))
+ return _id
+
+ @use_cursor
+ def purge_att_property(self, table, field, obj, attr, name):
+ cursor = self.cursor
+ cursor.execute(\
+ 'SELECT DISTINCT id FROM ? WHERE ? = ? AND name = ? AND ' \
+ 'attribute = ? ORDER BY date', (table, field, obj, name, attr))
+ rows = cursor.fetchall()
+ to_del = len(rows) - self.history_depth
+ if to_del > 0:
+ for row in rows[:to_del]:
+ cursor.execute('DELETE FROM ? WHERE id=?', (table, row[0]))
+
+ @use_cursor
+ def purge_property(self, table, field, obj, name):
+ cursor = self.cursor
+ cursor.execute(\
+ 'SELECT DISTINCT id FROM ? WHERE ? = ? AND name = ? ' \
+ 'ORDER BY date', (table, field, obj, name))
+ rows = cursor.fetchall()
+ to_del = len(rows) - self.history_depth
+ if to_del > 0:
+ for row in rows[:to_del]:
+ cursor.execute('DELETE FROM ? WHERE id=?', (table, row[0]))
+
+ @use_cursor
+ def get_device_host(self, name):
+ cursor = self.cursor
+ name = replace_wildcard(name)
+ cursor.execute('SELECT host FROM device WHERE name LIKE ?', (name,))
+ row = cursor.fetchone()
+ if row is None:
+ raise Exception("No host for device '" + name + "'")
+ else:
+ return row[0]
+
+
+ def send_starter_cmd(self, starter_dev_names):
+ for name in starter_dev_names:
+ pos = name.find('.')
+ if pos != -1:
+ name = name[0:pos]
+ dev = PyTango.DeviceProxy(name)
+ dev.UpdateServersInfo()
+
+
+
+ # TANGO API
+
+ def get_stored_procedure_release(self):
+ return 'release 1.8'
+
+ @use_cursor
+ def add_device(self, server_name, dev_info, klass_name, alias=None):
+ self._info("delete_attribute_alias(server_name=%s, dev_info=%s, klass_name=%s, alias=%s)",
+ server_name, dev_info, klass_name, alias)
+ dev_name, (domain, family, member) = dev_info
+ cursor = self.cursor
+
+ # first delete the tuple (device,name) from the device table
+ cursor.execute('DELETE FROM device WHERE name LIKE ?', (dev_name,))
+
+ # then insert the new value for this tuple
+ cursor.execute(\
+ 'INSERT INTO device (name, alias, domain, family, member, exported, ' \
+ 'ior, host, server, pid, class, version, started, stopped) ' \
+ 'VALUES (?, ?, ?, ?, ?, 0, "nada", "nada", ?, 0, ?, "0", NULL, NULL)',
+ (dev_name, alias, domain, family, member, server_name, klass_name))
+
+ # Check if a DServer device entry for the process already exists
+ cursor.execute('SELECT name FROM device WHERE server LIKE ? AND class LIKE "DServer"', (server_name,))
+ if cursor.fetchone() is None:
+ dev_name = "dserver/" + server_name
+ domain, family, member = dev_name.split("/", 2)
+ cursor.execute(\
+ 'INSERT INTO device (name, domain, family, member, exported, ior, ' \
+ 'host, server, pid, class, version, started, stopped) ' \
+ 'VALUES (?, ?, ?, ?, 0, "nada", "nada", ?, 0, "DServer", "0", NULL, NULL)',
+ (dev_name, domain, family, member, server_name))
+
+ @use_cursor
+ def delete_attribute_alias(self, alias):
+ self._info("delete_attribute_alias(alias=%s)", alias)
+ self.cursor.execute('DELETE FROM attribute_alias WHERE alias=?', (alias,))
+
+ @use_cursor
+ def delete_class_attribute(self, klass_name, attr_name):
+ self.cursor.execute(\
+ 'DELETE FROM property_attribute_class WHERE class LIKE ? AND ' \
+ 'attribute LIKE ?', (klass_name, attr_name))
+
+ @use_cursor
+ def delete_class_attribute_property(self, klass_name, attr_name, prop_name):
+ cursor = self.cursor
+
+ # Is there something to delete ?
+ cursor.execute(\
+ 'SELECT count(*) FROM property_attribute_class WHERE class = ? ' \
+ 'AND attribute = ? AND name = ?', (klass_name, attr_name, prop_name))
+ if cursor.fetchone()[0] > 0:
+ # then delete property from the property_attribute_class table
+ cursor.execute(\
+ 'DELETE FROM property_attribute_class WHERE class = ? AND ' \
+ 'attribute = ? and name = ?', (klass_name, attr_name, prop_name))
+ # mark this property as deleted
+ hist_id = self.get_id('class_attribute', cursor=cursor)
+ cursor.execute(\
+ 'INSERT INTO property_attribute_class_hist (class, attribute, ' \
+ 'name, id, count, value) VALUES ' \
+ '(?, ?, ?, ?, "0", "DELETED")',
+ (klass_name, attr_name, prop_name, hist_id))
+ self.purge_att_property("property_attribute_class_hist", "class",
+ klass_name, attr_name, prop_name, cursor=cursor)
+
+ @use_cursor
+ def delete_class_property(self, klass_name, prop_name):
+ cursor = self.cursor
+
+ prop_name = replace_wildcard(prop_name)
+ # Is there something to delete ?
+ cursor.execute(\
+ 'SELECT DISTINCT name FROM property_class WHERE class=? AND ' \
+ 'name LIKE ?', (klass_name, prop_name))
+ for row in cursor.fetchall():
+ # delete the tuple (device,name,count) from the property table
+ name = row[0]
+ cursor.execute(\
+ 'DELETE FROM property_class WHERE class=? AND name=?',
+ (klass_name, name))
+ # Mark this property as deleted
+ hist_id = self.get_id("class", cursor=cursor)
+ cursor.execute(\
+ 'INSERT INTO property_class_hist (class, name, id, count, value) ' \
+ 'VALUES (?, ?, ?, "0", "DELETED")',
+ (klass_name, name, hist_id))
+ self.purge_property("property_class_hist", "class", klass_name,
+ name, cursor=cursor)
+
+ @use_cursor
+ def delete_device(self, dev_name):
+ self._info("delete_device(dev_name=%s)", dev_name)
+ cursor = self.cursor
+ dev_name = replace_wildcard(dev_name)
+
+ # delete the device from the device table
+ cursor.execute('DELETE FROM device WHERE name LIKE ?', (dev_name,))
+
+ # delete device from the property_device table
+ cursor.execute('DELETE FROM property_device WHERE device LIKE ?', (dev_name,))
+
+ # delete device from the property_attribute_device table
+ cursor.execute('DELETE FROM property_attribute_device WHERE device LIKE ?', (dev_name,))
+
+ @use_cursor
+ def delete_device_alias(self, dev_alias):
+ self._info("delete_device_alias(dev_alias=%s)", dev_alias)
+ self.cursor.execute('UPDATE device SET alias=NULL WHERE alias=?', (dev_alias,))
+
+ @use_cursor
+ def delete_device_attribute(self, dev_name, attr_name):
+ dev_name = replace_wildcard(dev_name)
+ self.cursor.execute(\
+ 'DELETE FROM property_attribute_device WHERE device LIKE ? AND ' \
+ 'attribute LIKE ?', (dev_name, attr_name))
+
+ @use_cursor
+ def delete_device_attribute_property(self, dev_name, attr_name, prop_name):
+ cursor = self.cursor
+ # Is there something to delete ?
+ cursor.execute(\
+ 'SELECT count(*) FROM property_attribute_device WHERE device = ?' \
+ 'AND attribute = ? AND name = ?', (dev_name, attr_name, prop_name))
+ if cursor.fetchone()[0] > 0:
+ # delete property from the property_attribute_device table
+ cursor.execute(\
+ 'DELETE FROM property_attribute_device WHERE device = ? AND '
+ 'attribute = ? AND name = ?', (dev_name, attr_name, prop_name))
+ # Mark this property as deleted
+ hist_id = self.get_id("device_attribute", cursor=cursor)
+ cursor.execute(\
+ 'INSERT INTO property_attribute_device_hist ' \
+ '(device, attribute, name, id, count, value) VALUES ' \
+ '(?, ?, ?, ?, "0", "DELETED")', (dev_name, attr_name, prop_name, hist_id))
+ self.purge_att_property("property_attribute_device_hist", "device",
+ dev_name, attr_name, prop_name, cursor=cursor)
+
+ @use_cursor
+ def delete_device_property(self, dev_name, prop_name):
+ cursor = self.cursor
+ prop_name = replace_wildcard(prop_name)
+
+ # Is there something to delete ?
+ cursor.execute(\
+ 'SELECT DISTINCT name FROM property_device WHERE device=? AND ' \
+ 'name LIKE ?', (dev_name, prop_name))
+ for row in cursor.fetchall():
+ # delete the tuple (device,name,count) from the property table
+ cursor.execute(\
+ 'DELETE FROM property_device WHERE device=? AND name LIKE ?',
+ (dev_name, prop_name))
+ # Mark this property as deleted
+ hist_id = self.get_id("device", cursor=cursor)
+ cursor.execute(\
+ 'INSERT INTO property_device_hist (device, id, name, count, value) ' \
+ 'VALUES (?, ?, ?, "0", "DELETED")', (dev_name, hist_id, row[0]))
+ self.purge_property("property_device_hist", "device", dev_name, row[0])
+
+ @use_cursor
+ def delete_property(self, obj_name, prop_name):
+ cursor = self.cursor
+ prop_name = replace_wildcard(prop_name)
+
+ # Is there something to delete ?
+ cursor.execute(\
+ 'SELECT DISTINCT name FROM property WHERE object=? AND ' \
+ 'name LIKE ?', (obj_name, prop_name))
+ for row in cursor.fetchall():
+ # delete the tuple (object,name,count) from the property table
+ cursor.execute(\
+ 'DELETE FROM property_device WHERE device=? AND name LIKE ?',
+ (obj_name, prop_name))
+ # Mark this property as deleted
+ hist_id = self.get_id("object", cursor=cursor)
+ cursor.execute(\
+ 'INSERT INTO property_hist (object, name, id, count, value) ' \
+ 'VALUES (?, ?, ?, "0", "DELETED")', (obj_name, row[0], hist_id))
+ self.purge_property("property_hist", "object", obj_name, row[0])
+
+ @use_cursor
+ def delete_server(self, server_instance):
+ cursor = self.cursor
+ server_instance = replace_wildcard(server_instance)
+
+ previous_host = None
+ # get host where running
+ if self.fire_to_starter:
+ adm_dev_name = "dserver/" + server_instance
+ previous_host = self.get_device_host(adm_dev_name)
+
+ # then delete the device from the device table
+ cursor.execute('DELETE FROM device WHERE server LIKE ?', (server_instance,))
+
+ # Update host's starter to update controlled servers list
+ if self.fire_to_starter and previous_host:
+ self.send_starter_cmd(previous_host)
+ pass
+
+ @use_cursor
+ def delete_server_info(self, server_instance):
+ self.cursor.execute('DELETE FROM server WHERE name=?', (server_instance,))
+
+ @use_cursor
+ def export_device(self, dev_name, IOR, host, pid, version):
+ self._info("export_device(dev_name=%s, host=%s, pid=%s, version=%s)",
+ dev_name, host, pid, version)
+ self._info("export_device(IOR=%s)", IOR)
+ cursor = self.cursor
+ do_fire = False
+ previous_host = None
+
+ if self.fire_to_starter:
+ if dev_name[0:8] == "dserver/":
+ # Get database server name
+ tango_util = PyTango.Util.instance()
+ db_serv = tango_util.get_ds_name()
+ adm_dev_name = "dserver/" + db_serv.lower()
+ if dev_name != adm_dev_name and dev_name[0:16] != "dserver/starter/":
+ do_fire = True
+ previous_host = self.get_device_host(dev_name)
+
+ cursor.execute('SELECT server FROM device WHERE name LIKE ?', (dev_name,))
+ row = cursor.fetchone()
+ if row is None:
+ th_exc(DB_DeviceNotDefined,
+ "device " + dev_name + " not defined in the database !",
+ "DataBase::ExportDevice()")
+ server = row[0]
+
+ # update the new value for this tuple
+ cursor.execute(\
+ 'UPDATE device SET exported=1, ior=?, host=?, pid=?, version=?, ' \
+ 'started=datetime("now") WHERE name LIKE ?',
+ (IOR, host, pid, version, dev_name))
+
+ # update host name in server table
+ cursor.execute('UPDATE server SET host=? WHERE name LIKE ?', (host, server))
+
+ if do_fire:
+ hosts = []
+ hosts.append(host)
+ if previous_host != "" and previous_host != "nada" and previous_host != host:
+ hosts.append(previous_host)
+ self.send_starter_cmd(hosts)
+
+ @use_cursor
+ def export_event(self, event, IOR, host, pid, version):
+ cursor = self.cursor
+ cursor.execute(\
+ 'INSERT event (name,exported,ior,host,server,pid,version,started) ' \
+ 'VALUES (?, 1, ?, ?, ?, ?, ?, datetime("now")',
+ (event, IOR, host, event, pid, version))
+
+ @use_cursor
+ def get_alias_device(self, dev_alias):
+ cursor = self.cursor
+ cursor.execute('SELECT name FROM device WHERE alias LIKE ?',
+ (dev_alias,))
+ row = cursor.fetchone()
+ if row is None:
+ th_exc(DB_DeviceNotDefined,
+ "No device found for alias '" + dev_alias + "'",
+ "DataBase::GetAliasDevice()")
+ return row[0]
+
+ @use_cursor
+ def get_attribute_alias(self, attr_alias):
+ cursor = self.cursor
+ cursor.execute('SELECT name from attribute_alias WHERE alias LIKE ?',
+ (attr_alias,))
+ row = cursor.fetchone()
+ if row is None:
+ th_exc(DB_SQLError,
+ "No attribute found for alias '" + attr_alias + "'",
+ "DataBase::GetAttributeAlias()")
+ return row[0]
+
+ @use_cursor
+ def get_attribute_alias_list(self, attr_alias):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT alias FROM attribute_alias WHERE alias LIKE ? ORDER BY attribute',
+ (attr_alias,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_class_attribute_list(self, class_name, wildcard):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT attribute FROM property_attribute_class WHERE class=? and attribute like ?',
+ (class_name, wildcard))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_class_attribute_property(self, class_name, attributes):
+ cursor = self.cursor
+ stmt = 'SELECT name,value FROM property_attribute_class WHERE class=? AND attribute LIKE ?'
+ result = [class_name, str(len(attributes))]
+ for attribute in attributes:
+ cursor.execute(stmt, (class_name, attribute))
+ rows = cursor.fetchall()
+ result.append(attribute)
+ result.append(str(len(rows)))
+ for row in rows:
+ result.append(row[0])
+ result.append(row[1])
+ return result
+
+ @use_cursor
+ def get_class_attribute_property2(self, class_name, attributes):
+ cursor = self.cursor
+ stmt = 'SELECT name,value FROM property_attribute_class WHERE class=? AND attribute LIKE ? ORDER BY name,count'
+ result = [class_name, str(len(attributes))]
+ for attribute in attributes:
+ cursor.execute(stmt, (class_name, attribute))
+ rows = cursor.fetchall()
+ result.append(attribute)
+ j = 0
+ new_prop = True
+ nb_props = 0
+ prop_size = 0
+ prop_names = []
+ prop_sizes = []
+ prop_values = []
+ for row in rows:
+ prop_values.append(row[1])
+ if j == 0:
+ old_name = row[0]
+ else:
+ name = row[0]
+ if name != old_name:
+ new_prop = True
+ old_name = name
+ else:
+ new_prop = False
+ j = j + 1
+ if new_prop == True:
+ nb_props = nb_props + 1
+ prop_names.append(row[0])
+ if prop_size != 0:
+ prop_sizes.append(prop_size)
+ prop_size = 1
+ else:
+ prop_size = prop_size + 1
+
+ result.append(str(nb_props))
+ j = 0
+ k = 0
+ for name in prop_names:
+ result.append(name)
+ result.append(prop_sizes[j])
+ for i in range(0, prop_sizes[j]):
+ result.append(prop_values[k])
+ k = k + 1
+ j = j + 1
+ return result
+
+ @use_cursor
+ def get_class_attribute_property_hist(self, class_name, attribute, prop_name):
+ cursor = self.cursor
+ stmt = 'SELECT DISTINCT id FROM property_attribute_class_hist WHERE class=? AND attribute LIKE ? AND name LIKE ? ORDER by date ASC'
+
+ result = []
+
+ cursor.execute(stmt, (class_name, attribute, prop_name))
+
+ for row in cursor.fetchall():
+ idr = row[0]
+
+ stmt = 'SELECT DATE_FORMAT(date,\'%Y-%m-%d %H:%i:%s\'),value,attribute,name,count FROM property_attribute_class_hist WHERE id =? AND class =?'
+
+ cursor.execute(stmt, (idr, class_name))
+
+ rows = cursor.fetchall()
+
+ result.append(rows[2])
+ result.append(rows[3])
+ result.append(rows[0])
+ result.append(str(rows[4]))
+ for value in rows[1]:
+ result.append(value)
+
+ return result
+
+ @use_cursor
+ def get_class_for_device(self, dev_name):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT class FROM device WHERE name=?', (dev_name,))
+ row = cursor.fetchone()
+ if row is None:
+ th_exc(DB_IncorrectArguments, "Class not found for " + dev_name,
+ "Database.GetClassForDevice")
+ return row
+
+ @use_cursor
+ def get_class_inheritance_for_device(self, dev_name):
+ cursor = self.cursor
+ class_name = self.get_class_for_device(dev_name, cursor=cursor)
+ props = self.get_class_property(class_name, "InheritedFrom", cursor=cursor)
+ return [class_name] + props[4:]
+
+ @use_cursor
+ def get_class_list(self, server):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT class FROM device WHERE class LIKE ? ORDER BY class', (server,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+
+ @use_cursor
+ def get_class_property(self, class_name, properties):
+ cursor = self.cursor
+ stmt = 'SELECT count,value FROM property_class WHERE class=? AND name LIKE ? ORDER BY count'
+ result.append(class_name)
+ result.append(len(properties))
+ for prop_name in properties:
+ cursor.execute(stmt, (class_name, prop_name))
+ rows = cursor.fetchall()
+ result.append(prop_name)
+ result.append(str(len(rows)))
+ for row in rows:
+ result.append(row[1])
+ return result
+
+ @use_cursor
+ def get_class_property_hist(self, class_name, prop_name):
+ cursor = self.cursor
+ stmt = 'SELECT DISTINCT id FROM property_class_hist WHERE class=? AND AND name LIKE ? ORDER by date ASC'
+
+ result = []
+
+ cursor.execute(stmt, (class_name, prop_name))
+
+ for row in cursor.fetchall():
+ idr = row[0]
+
+ stmt = 'SELECT DATE_FORMAT(date,\'%Y-%m-%d %H:%i:%s\'),value,name,count FROM property_class_hist WHERE id =? AND class =?'
+
+ cursor.execute(stmt, (idr, class_name))
+
+ rows = cursor.fetchall()
+
+ result.append(rows[2])
+ result.append(rows[0])
+ result.append(str(rows[3]))
+ for value in rows[1]:
+ result.append(value)
+
+ return result
+
+ @use_cursor
+ def get_class_property_list(self, class_name):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT name FROM property_class WHERE class LIKE ? order by NAME',
+ (class_name,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_device_alias(self, dev_name):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT alias FROM device WHERE name LIKE ?',
+ (dev_name,))
+ row = cursor.fetchone()
+ if row is None:
+ th_exc(DB_DeviceNotDefined,
+ "No alias found for device '" + dev_name + "'",
+ "DataBase::GetDeviceAlias()")
+ return row[0]
+
+ @use_cursor
+ def get_device_alias_list(self, alias):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT alias FROM device WHERE alias LIKE ? ORDER BY alias',
+ (alias,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_device_attribute_list(self, dev_name, attribute):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT attribute FROM property_attribute_device WHERE device=? AND attribute LIKE ? ORDER BY attribute',
+ (dev_name, attribute,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+
+ @use_cursor
+ def get_device_attribute_property(self, dev_name, attributes):
+ cursor = self.cursor
+ stmt = 'SELECT name,value FROM property_attribute_device WHERE device=? AND attribute LIKE ?'
+ result = [dev_name, str(len(attributes))]
+ for attribute in attributes:
+ cursor.execute(stmt, (dev_name, attribute))
+ rows = cursor.fetchall()
+ result.append(attribute)
+ result.append(str(len(rows)))
+ for row in rows:
+ result.append(row[0])
+ result.append(row[1])
+ return result
+
+ @use_cursor
+ def get_device_attribute_property2(self, dev_name, attributes):
+ cursor = self.cursor
+ stmt = 'SELECT name,value FROM property_attribute_device WHERE device=? AND attribute LIKE ? ORDER BY name,count'
+ result = [dev_name, str(len(attributes))]
+ for attribute in attributes:
+ cursor.execute(stmt, (dev_name, attribute))
+ rows = cursor.fetchall()
+ result.append(attribute)
+ j = 0
+ new_prop = True
+ nb_props = 0
+ prop_size = 0
+ prop_names = []
+ prop_sizes = []
+ prop_values = []
+ for row in rows:
+ prop_values.append(row[1])
+ if j == 0:
+ old_name = row[0]
+ else:
+ name = row[0]
+ if name != old_name:
+ new_prop = True
+ old_name = name
+ else:
+ new_prop = False
+ j = j + 1
+ if new_prop == True:
+ nb_props = nb_props + 1
+ prop_names.append(row[0])
+ if prop_size != 0:
+ prop_sizes.append(prop_size)
+ prop_size = 1
+ else:
+ prop_size = prop_size + 1
+
+ result.append(str(nb_props))
+ j = 0
+ k = 0
+ for name in prop_names:
+ result.append(name)
+ result.append(prop_sizes[j])
+ for i in range(0, prop_sizes[j]):
+ result.append(prop_values[k])
+ k = k + 1
+ j = j + 1
+ return result
+
+ @use_cursor
+ def get_device_attribute_property_hist(self, dev_name, attribute, prop_name):
+ cursor = self.cursor
+ stmt = 'SELECT DISTINCT id FROM property_attribute_device_hist WHERE device=? AND attribute LIKE ? AND name LIKE ? ORDER by date ASC'
+
+ result = []
+
+ cursor.execute(stmt, (dev_name, attribute, prop_name))
+
+ for row in cursor.fetchall():
+ idr = row[0]
+
+ stmt = 'SELECT DATE_FORMAT(date,\'%Y-%m-%d %H:%i:%s\'),value,attribute,name,count FROM property_attribute_device_hist WHERE id =? AND device =? ORDER BY count ASC'
+
+ cursor.execute(stmt, (idr, class_name))
+
+ rows = cursor.fetchall()
+
+ result.append(rows[2])
+ result.append(rows[3])
+ result.append(rows[0])
+ result.append(str(rows[4]))
+ for value in rows[1]:
+ result.append(value)
+
+ return result
+
+
+ @use_cursor
+ def get_device_class_list(self, server_name):
+ cursor = self.cursor
+ result = []
+ cursor.execute('SELECT name,class FROM device WHERE server =? ORDER BY name',
+ (server_name,))
+ for row in cursor.fetchall():
+ result.append(row[0])
+ result.append(row[1])
+
+ return result
+
+ @use_cursor
+ def get_device_domain_list(self, wildcard):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT domain FROM device WHERE name LIKE ? OR alias LIKE ? ORDER BY domain',
+ (wildcard,wildcard))
+ return [ row[0] for row in cursor.fetchall() ]
+
+
+ @use_cursor
+ def get_device_exported_list(self, wildcard):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT name FROM device WHERE (name LIKE ? OR alias LIKE ?) AND exported=1 ORDER BY name',
+ (wildcard,wildcard))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_device_family_list(self, wildcard):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT family FROM device WHERE name LIKE ? OR alias LIKE ? ORDER BY family',
+ (wildcard,wildcard))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_device_info(self, dev_name):
+ cursor = self.cursor
+ cursor.execute('SELECT exported,ior,version,pid,server,host,started,stopped,class FROM device WHERE name =? or alias =?',
+ (dev_name,dev_name))
+ result_long = []
+ result_str = []
+ for row in cursor.fetchall():
+ if ((row[4] == None) or (row[5] == None)):
+ th_exc(DB_SQLError,
+ "Wrong info in database for device '" + dev_name + "'",
+ "DataBase::GetDeviceInfo()")
+ result_str.append(dev_name)
+ if raw[1] != None:
+ result_str.append(str(raw[1]))
+ else:
+ result_str.append("")
+ result_str.append(str(raw[2]))
+ result_str.append(str(raw[4]))
+ result_str.append(str(raw[5]))
+
+ for i in range(0,2):
+ cursor.execute('SELECT DATE_FORMAT(?,\'%D-%M-%Y at %H:%i:%s\')', raw[6 + i])
+ tmp_date = cursor.fetchone()
+ if tmp_date == None:
+ result_str.append("?")
+ else:
+ result_str.append(str(tmp_date))
+
+ for i in range(0,2):
+ if raw[i] != None:
+ result_long.append(raw[i])
+
+ result = (result_long, result_str)
+ return result
+
+ @use_cursor
+ def get_device_list(self,server_name, class_name ):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT name FROM device WHERE server LIKE ? AND class LIKE ? ORDER BY name',
+ (server_name, class_name))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_device_wide_list(self, wildcard):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT name FROM device WHERE name LIKE ? ORDER BY name',
+ (wildcard,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_device_member_list(self, wildcard):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT member FROM device WHERE name LIKE ? ORDER BY member',
+ (wildcard,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+
+ @use_cursor
+ def get_device_property(self, dev_name, properties):
+ cursor = self.cursor
+ stmt = 'SELECT count,value,name FROM property_device WHERE device = ? AND name LIKE ? ORDER BY count'
+ result = []
+ result.append(dev_name)
+ result.append(str(len(properties)))
+ for prop in properties:
+ result.append(prop)
+ tmp_name = replace_wildcard(prop)
+ cursor.execute(stmt, (dev_name, tmp_name))
+ rows = cursor.fetchall()
+ result.append(attribute)
+ result.append(str(len(rows)))
+ for row in rows:
+ result.append(row[1])
+ return result
+
+ @use_cursor
+ def get_device_property_hist(self, device_name, prop_name):
+ cursor = self.cursor
+ stmt = 'SELECT DISTINCT id FROM property_device_hist WHERE device=? AND name LIKE ? ORDER by date ASC'
+
+ result = []
+
+ tmp_name = replace_wildcard(prop_name);
+
+ cursor.execute(stmt, (class_name, device_name, tmp_name))
+
+ stmt = 'SELECT DATE_FORMAT(date,\'%Y-%m-%d %H:%i:%s\'),value,name,count FROM property_device_hist WHERE id =? AND device =? ORDER BY count ASC'
+
+ for row in cursor.fetchall():
+ idr = row[0]
+ cursor.execute(stmt, (idr, device_name))
+ rows = cursor.fetchall()
+ result.append(rows[2])
+ result.append(rows[0])
+ result.append(str(rows[3]))
+ for value in rows[1]:
+ result.append(value)
+
+ return result
+
+ @use_cursor
+ def get_device_server_class_list(self, server_name):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT class FROM device WHERE server LIKE ? ORDER BY class',
+ (sever_name,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_exported_device_list_for_class(self, class_name):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT name FROM device WHERE class LIKE ? AND exported=1 ORDER BY name',
+ (class_name,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_host_list(self, host_name):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT host FROM device WHERE host LIKE ? ORDER BY host',
+ (host_name,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_host_server_list(self, host_name):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT server FROM device WHERE host LIKE ? ORDER BY server',
+ (host_name,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+
+ def get_host_servers_info(self, host_name):
+ servers = self.get_host_server_list(host_name)
+ result = []
+ for server in servers:
+ result.append(server)
+ info = self.get_server_info(server)
+ result.append(info[2])
+ result.append(info[3])
+ return result
+
+
+ def get_instance_name_list(self, server_name):
+ server_name = server_name + "\*"
+ server_list = self.get_server_list(server_name)
+ result = []
+ for server in server_list:
+ names = server.split("/")
+ result.append(names[1])
+ return result
+
+ @use_cursor
+ def get_object_list(self, name):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT object FROM property WHERE object LIKE ? ORDER BY object',
+ (name,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_property(self, object_name, properties):
+ cursor = self.cursor
+ result = []
+ result.append(object_name)
+ result.append(str(len(properties)))
+ stmt = 'SELECT count,value,name FROM property WHERE object LIKE ? AND name LIKE ? ORDER BY count'
+ for prop_name in properties:
+ result.append(prop_name)
+ prop_name = replace_wildcard(prop_name)
+ cursor.execute(stmt, (object_name,prop_name))
+ rows = cursor.fetchall()
+ n_rows = len(rows)
+ result.append(n_rows)
+ if n_rows:
+ for row in rows:
+ result.append(row[1])
+ else:
+ result.append(" ")
+ return result
+
+
+ @use_cursor
+ def get_property_hist(self, object_name, prop_name):
+ cursor = self.cursor
+ result = []
+
+ stmt = 'SELECT DISTINCT id FROM property_hist WHERE object=? AND name LIKE ? ORDER by date'
+ prop_name = replace_wildcard(prop_name)
+ cursor.execute(stmt, (object_name, prop_name))
+
+ stmt = 'SELECT DATE_FORMAT(date,\'%Y-%m-%d %H:%i:%s\'),value,name,count FROM property_hist WHERE id =? AND object =?'
+ for row in cursor.fetchall():
+ idr = row[0]
+
+ cursor.execute(stmt, (idr, object_name))
+ rows = cursor.fetchall()
+ count = len(rows)
+ if rows[3] == 0:
+ count = 0
+ result.append(rows[2])
+ result.append(rows[0])
+ result.append(str(count))
+ for tmp_row in rows:
+ result.append(tmp_row[1])
+
+ return result
+
+ @use_cursor
+ def get_property_list(self, object_name, wildcard):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT name FROM property WHERE object LIKE ? AND name LIKE ? ORDER BY name',
+ (object_name,wildcard))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_server_info(self, server_name):
+ cursor = self.cursor
+ cursor.execute('SELECT host,mode,level FROM server WHERE name =?',
+ (server_name,))
+ result = []
+ result.append(server_name)
+ row = cursor.fetchone()
+ if row is None:
+ result.append(" ")
+ result.append(" ")
+ result.append(" ")
+ else:
+ result.append(row[0])
+ result.append(row[1])
+ result.append(row[2])
+
+ return result
+
+ @use_cursor
+ def get_server_list(self, wildcard):
+ cursor = self.cursor
+ cursor.execute('SELECT DISTINCT server FROM device WHERE server LIKE ? ORDER BY server',
+ (wildcard,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ def get_server_list(self, wildcard):
+ result = []
+ server_list = self.get_server_list(wildcard)
+ for server in server_list:
+ found = 0
+ server_name = server.split("/")[0]
+ for res in result:
+ if server_name.lower() == res.lower():
+ found = 1
+ if not found:
+ result.append(server_name)
+ return result
+
+ @use_cursor
+ def import_device(self, dev_name):
+ cursor = self.cursor
+ result_long = []
+ result_str = []
+ # Search first by server name and if nothing found by alias
+ # Using OR takes much more time
+ cursor.execute('"SELECT exported,ior,version,pid,server,host,class FROM device WHERE name =?',
+ (dev_name,))
+ rows = cursor.fetchall()
+ if len(rows) == 0:
+ cursor.execute('"SELECT exported,ior,version,pid,server,host,class FROM device WHERE alias =?',
+ (dev_name,))
+ rows = cursor.fetchall()
+ if len(rows) == 0:
+ th_exc(DB_DeviceNotDefined,
+ "device " + dev_name + " not defined in the database !",
+ "DataBase::ImportDevice()")
+ for row in rows:
+ result_str.append(dev_name)
+ result_str.append(row[2])
+ result_str.append(row[4])
+ result_str.append(row[5])
+ result_str.append(row[6])
+ if row[1] != None:
+ result_str.append(row[1])
+ else:
+ result_str.append("")
+ result_long.append(row[0])
+ result_long.append(row[3])
+ result = (result_long, result_str)
+ return result
+
+ @use_cursor
+ def import_event(self, event_name):
+ cursor = self.cursor
+ result_long = []
+ result_str = []
+ cursor.execute('"SELECT exported,ior,version,pid,host FROM event WHERE name =?',
+ (event_name,))
+ rows = cursor.fetchall()
+ if len(rows) == 0:
+ th_exc(DB_DeviceNotDefined,
+ "event " + event_name + " not defined in the database !",
+ "DataBase::ImportEvent()")
+ for row in rows:
+ result_str.append(event_name)
+ result_str.append(row[1])
+ result_str.append(row[2])
+ result_str.append(row[4])
+ exported = -1
+ if row[0] != None:
+ exported = row[0]
+ result_long.append(exported)
+ result_long.append(row[3])
+ result = (result_long, result_str)
+ return result
+
+
+ @use_cursor
+ def info(self):
+ cursor = self.cursor
+ result = []
+ # db name
+ info_str = "TANGO Database " + self.db_name
+ result.append(info_str)
+ # new line
+ result.append("")
+ # get start time of database
+ cursor.execute('SELECT started FROM device WHERE name =?',
+ (self.db_name,))
+ row = cursor.fetchone()
+ info_str = "Running since ..." + str(row[0])
+ result.append(info_str)
+ # new line
+ result.append("")
+ # get number of devices defined
+ cursor.execute('SELECT COUNT(*) FROM device')
+ row = cursor.fetchone()
+ info_str = "Devices defined = " + str(row[0])
+ result.append(info_str)
+ # get number of devices exported
+ cursor.execute('SELECT COUNT(*) FROM device WHERE exported = 1')
+ row = cursor.fetchone()
+ info_str = "Devices exported = " + str(row[0])
+ result.append(info_str)
+ # get number of device servers defined
+ cursor.execute('SELECT COUNT(*) FROM device WHERE class = \"DServer\" ')
+ row = cursor.fetchone()
+ info_str = "Device servers defined = " + str(row[0])
+ result.append(info_str)
+ # get number of device servers exported
+ cursor.execute('SELECT COUNT(*) FROM device WHERE class = \"DServer\" AND exported = 1')
+ row = cursor.fetchone()
+ info_str = "Device servers exported = " + str(row[0])
+ result.append(info_str)
+ # new line
+ result.append("")
+ # get number of device properties
+ cursor.execute('SELECT COUNT(*) FROM property_device')
+ row = cursor.fetchone()
+ info_str = "Device properties defineed = " + str(row[0])
+ cursor.execute('SELECT COUNT(*) FROM property_device_hist')
+ row = cursor.fetchone()
+ info_str = info_str + " [History lgth = " + str(row[0]) + "]"
+ result.append(info_str)
+ # get number of class properties
+ cursor.execute('SELECT COUNT(*) FROM property_class')
+ row = cursor.fetchone()
+ info_str = "Class properties defined = " + str(row[0])
+ cursor.execute('SELECT COUNT(*) FROM property_class_hist')
+ row = cursor.fetchone()
+ info_str = info_str + " [History lgth = " + str(row[0]) + "]"
+ result.append(info_str)
+ # get number of device attribute properties
+ cursor.execute('SELECT COUNT(*) FROM property_attribute_device')
+ row = cursor.fetchone()
+ info_str = "Device attribute properties defined = " + str(row[0])
+ cursor.execute('SELECT COUNT(*) FROM property_attribute_device_hist')
+ row = cursor.fetchone()
+ info_str = info_str + " [History lgth = " + str(row[0]) + "]"
+ result.append(info_str)
+ # get number of class attribute properties
+ cursor.execute('SELECT COUNT(*) FROM property_attribute_class')
+ row = cursor.fetchone()
+ info_str = "Class attribute properties defined = " + str(row[0])
+ cursor.execute('SELECT COUNT(*) FROM property_attribute_class_hist')
+ row = cursor.fetchone()
+ info_str = info_str + " [History lgth = " + str(row[0]) + "]"
+ result.append(info_str)
+ # get number of object properties
+ cursor.execute('SELECT COUNT(*) FROM property')
+ row = cursor.fetchone()
+ info_str = "Object properties defined = " + str(row[0])
+ cursor.execute('SELECT COUNT(*) FROM property_hist')
+ row = cursor.fetchone()
+ info_str = info_str + " [History lgth = " + str(row[0]) + "]"
+ result.append(info_str)
+
+ return result
+
+ @use_cursor
+ def put_attribute_alias(self, attribute_name, attribute_alias):
+ cursor = self.cursor
+ attribute_name = attribute_name.lower()
+ # first check if this alias exists
+ cursor.execute('SELECT alias from attribute_alias WHERE alias=? AND name <> ? ',
+ (attribute_alias,attribute_name))
+ rows = cursor.fetchall()
+ if len(rows) > 0:
+ self.warn_stream("DataBase::DbPutAttributeAlias(): this alias exists already ")
+ th_exc(DB_SQLError,
+ "alias " + attribute_alias + " already exists !",
+ "DataBase::DbPutAttributeAlias()")
+ tmp_names = attribute_name.split("/")
+ if len(tmp_names) != 4:
+ self.warn_stream("DataBase::DbPutAttributeAlias(): attribute name has bad syntax, must have 3 / in it")
+ th_exc(DB_SQLError,
+ "attribute name " + attribute_name + " has bad syntax, must have 3 / in it",
+ "DataBase::DbPutAttributeAlias()")
+ # first delete the current entry (if any)
+ cursor.execute('DELETE FROM attribute_alias WHERE name=?',
+ (attribute_name,))
+ # update the new value for this tuple
+ tmp_device = tmp_names[0] + "/" + tmp_names[1] + "/" + tmp_names[2]
+ tmp_attribute = tmp_names[3]
+ cursor.execute('INSERT attribute_alias SET alias=? ,name=?, device=?,updated=NOW()',
+ (attribute_alias, tmp_device, tmp_attribute))
+
+
+ @use_cursor
+ def put_class_attribute_property(self, class_name, nb_attributes, attr_prop_list):
+ cursor = self.cursor
+ k = 0
+ for i in range(0,nb_attributes):
+ tmp_attribute = attr_prop_list[k]
+ nb_properties = int(attr_prop_list[k+1])
+ for j in range(k+2,k+nb_properties*2+2,2):
+ tmp_name = attr_prop_list[j]
+ tmp_value = attr_prop_list[j+1]
+ # first delete the tuple (device,name,count) from the property table
+ cursor.execute('DELETE FROM property_attribute_class WHERE class LIKE ? AND attribute LIKE ? AND name LIKE ?', (class_name, tmp_attribute, tmp_name))
+ # then insert the new value for this tuple
+ cursor.execute('INSERT INTO property_attribute_class SET class=? ,attribute=?,name=?,count=\'1\',value=?,updated=NULL,accessed=NULL', (class_name, tmp_attribute, tmp_name, tmp_value))
+ # then insert the new value into the history table
+ hist_id = self.get_id("class_attribute", cursor=cursor)
+ cursor.execute('INSERT INTO property_attribute_class_hist SET class=?,attribute=?,name=?,id=?,count=\'1\',value=?', (class_name, tmp_attribute, tmp_name,hist_id,tmp_value))
+
+ self.purge_att_property("property_attribute_class_hist", "class",
+ class_name, tmp_attribute, tmp_name, cursor=cursor)
+ k = k + nb_properties*2+2
+
+ @use_cursor
+ def put_class_attribute_property2(self, class_name, nb_attributes, attr_prop_list):
+ cursor = self.cursor
+ k = 0
+ for i in range(0,nb_attributes):
+ tmp_attribute = attr_prop_list[k]
+ nb_properties = int(attr_prop_list[k+1])
+ for jj in range(0,nb_properties,1):
+ j = k + 2
+ tmp_name = attr_prop_list[j]
+ # first delete the tuple (device,name,count) from the property table
+ cursor.execute('DELETE FROM property_attribute_class WHERE class LIKE ? AND attribute LIKE ? AND name LIKE ?', (class_name, tmp_attribute, tmp_name))
+ n_rows = attr_prop_list[j+1]
+ tmp_count = 0
+ for l in range(j+1,j+n_rows+1,1):
+ tmp_value = attr_prop_list[l+1]
+ tmp_count = tmp_count + 1
+ # then insert the new value for this tuple
+ cursor.execute('INSERT INTO property_attribute_class SET class=? ,attribute=?,name=?,count=?,value=?,updated=NULL,accessed=NULL', (class_name, tmp_attribute, tmp_name, str(tmp_count), tmp_value))
+ # then insert the new value into the history table
+ hist_id = self.get_id("class_attribute", cursor=cursor)
+ cursor.execute('INSERT INTO property_attribute_class_hist SET class=?,attribute=?,name=?,id=?,count=?,value=?', (class_name, tmp_attribute, tmp_name,hist_id, str(tmp_count),tmp_value))
+
+ self.purge_att_property("property_attribute_class_hist", "class",
+ class_name, tmp_attribute, tmp_name, cursor=cursor)
+ k = k + n_rows + 2
+ k = k + 2
+
+ @use_cursor
+ def put_class_property(self, class_name, nb_properties, attr_prop_list):
+ cursor = self.cursor
+ k = 0
+ for i in range(0,nb_properties):
+ tmp_count = 0
+ tmp_name = attr_prop_list[k]
+ n_rows = attr_prop_list[k+1]
+ # first delete all tuples (device,name) from the property table
+ cursor.execute('DELETE FROM property_class WHERE class LIKE ? AND name LIKE ?', (class_name, tmp_name))
+
+ for j in range(k+2,k+n_rows+2,1):
+ tmp_value = attr_prop_list[j]
+ tmp_count = tmp_count+1
+ # then insert the new value for this tuple
+ cursor.execute('INSERT INTO property_class SET class=? ,name=?,count=?,value=?,updated=NULL,accessed=NULL', (class_name, tmp_name, str(tmp_count), tmp_value))
+ # then insert the new value into the history table
+ hist_id = self.get_id("class", cursor=cursor)
+ cursor.execute('INSERT INTO property_class_hist SET class=?,name=?,id=?,count=?,value=?', (class_name, tmp_name,hist_id, str(tmp_count),tmp_value))
+ self.purge_att_property("property_class_hist", "class",
+ class_name, tmp_name, cursor=cursor)
+ k = k + n_rows + 2
+
+ @use_cursor
+ def put_device_alias(self, device_name, device_alias):
+ cursor = self.cursor
+ device_name = device_name.lower()
+ # first check if this alias exists
+ cursor.execute('SELECT alias from device WHERE alias=? AND name <>?',
+ (device_alias, device_name))
+ rows = cursor.fetchall()
+ if len(rows) > 0:
+ self.warn_stream("DataBase::DbPutDeviceAlias(): this alias exists already ")
+ th_exc(DB_SQLError,
+ "alias " + device_alias + " already exists !",
+ "DataBase::DbPutDeviceAlias()")
+ # update the new value for this tuple
+ cursor.execute('UPDATE device SET alias=? ,started=NOW() where name LIKE ?',
+ (device_alias, device_name))
+
+ @use_cursor
+ def put_device_attribute_property(self, device_name, nb_attributes, attr_prop_list):
+ cursor = self.cursor
+ k = 0
+ for i in range(0,nb_attributes):
+ tmp_attribute = attr_prop_list[k]
+ nb_properties = int(attr_prop_list[k+1])
+ for j in range(k+2,k+nb_properties*2+2,2):
+ tmp_name = attr_prop_list[j]
+ tmp_value = attr_prop_list[j+1]
+ # first delete the tuple (device,name,count) from the property table
+ cursor.execute('DELETE FROM property_attribute_device WHERE device LIKE ? AND attribute LIKE ? AND name LIKE ?', (device_name, tmp_attribute, tmp_name))
+ # then insert the new value for this tuple
+ cursor.execute('INSERT INTO property_attribute_device SET device=? ,attribute=?,name=?,count=\'1\',value=?,updated=NULL,accessed=NULL', (device_name, tmp_attribute, tmp_name, tmp_value))
+ # then insert the new value into the history table
+ hist_id = self.get_id("device_attribute", cursor=cursor)
+ cursor.execute('INSERT INTO property_attribute_device_hist SET device=?,attribute=?,name=?,id=?,count=\'1\',value=?', (device_name, tmp_attribute, tmp_name,hist_id,tmp_value))
+
+ self.purge_att_property("property_attribute_device_hist", "device",
+ device_name, tmp_attribute, tmp_name, cursor=cursor)
+ k = k + nb_properties*2+2
+
+
+ @use_cursor
+ def put_device_attribute_property2(self, device_name, nb_attributes, attr_prop_list):
+ cursor = self.cursor
+ k = 0
+ for i in range(0,nb_attributes):
+ tmp_attribute = attr_prop_list[k]
+ nb_properties = int(attr_prop_list[k+1])
+ for jj in range(0,nb_properties,1):
+ j = k + 2
+ tmp_name = attr_prop_list[j]
+ # first delete the tuple (device,name,count) from the property table
+ cursor.execute('DELETE FROM property_attribute_device WHERE device LIKE ? AND attribute LIKE ? AND name LIKE ?', (device_name, tmp_attribute, tmp_name))
+ n_rows = attr_prop_list[j+1]
+ tmp_count = 0
+ for l in range(j+1,j+n_rows+1,1):
+ tmp_value = attr_prop_list[l+1]
+ tmp_count = tmp_count + 1
+ # then insert the new value for this tuple
+ cursor.execute('INSERT INTO property_attribute_device SET device=? ,attribute=?,name=?,count=?,value=?,updated=NULL,accessed=NULL', (device_name, tmp_attribute, tmp_name, str(tmp_count), tmp_value))
+ # then insert the new value into the history table
+ hist_id = self.get_id("device_attribute", cursor=cursor)
+ cursor.execute('INSERT INTO property_attribute_device_hist SET device=?,attribute=?,name=?,id=?,count=?,value=?', (device_name, tmp_attribute, tmp_name,hist_id, str(tmp_count),tmp_value))
+
+ self.purge_att_property("property_attribute_device_hist", "device",
+ device_name, tmp_attribute, tmp_name, cursor=cursor)
+ k = k + n_rows + 2
+ k = k + 2
+
+ @use_cursor
+ def put_device_property(self, device_name, nb_properties, attr_prop_list):
+ cursor = self.cursor
+ k = 0
+ hist_id = self.get_id("device", cursor=cursor)
+ for i in range(0,nb_properties):
+ tmp_count = 0
+ tmp_name = attr_prop_list[k]
+ n_rows = attr_prop_list[k+1]
+ # first delete all tuples (device,name) from the property table
+ cursor.execute('DELETE FROM property_device WHERE device LIKE ? AND name LIKE ?', (device_name, tmp_name))
+
+ for j in range(k+2,k+n_rows+2,1):
+ tmp_value = attr_prop_list[j]
+ tmp_count = tmp_count+1
+ # then insert the new value for this tuple
+ cursor.execute('INSERT INTO property_device SET device=? ,name=?,count=?,value=?,updated=NULL,accessed=NULL', (device_name, tmp_name, str(tmp_count), tmp_value))
+ # then insert the new value into the history table
+ cursor.execute('INSERT INTO property_device_hist SET device=?,name=?,id=?,count=?,value=?', (device_name, tmp_name,hist_id, str(tmp_count),tmp_value))
+ self.purge_att_property("property_device_hist", "device",
+ device_name, tmp_name, cursor=cursor)
+ k = k + n_rows + 2
+
+ @use_cursor
+ def put_property(self, object_name, nb_properties, attr_prop_list):
+ cursor = self.cursor
+ k = 0
+ hist_id = self.get_id("object", cursor=cursor)
+ for i in range(0,nb_properties):
+ tmp_count = 0
+ tmp_name = attr_prop_list[k]
+ n_rows = attr_prop_list[k+1]
+ # first delete the property from the property table
+ cursor.execute('DELETE FROM property WHERE object =? AND name =?', (object_name, tmp_name))
+
+ for j in range(k+2,k+n_rows+2,1):
+ tmp_value = attr_prop_list[j]
+ tmp_count = tmp_count+1
+ # then insert the new value for this tuple
+ cursor.execute('INSERT INTO property SET object=? ,name=?,count=?,value=?,updated=NULL,accessed=NULL', (object_name, tmp_name, str(tmp_count), tmp_value))
+ # then insert the new value into the history table
+ cursor.execute('INSERT INTO property_hist SET object=?,name=?,id=?,count=?,value=?', (object_name, tmp_name,hist_id, str(tmp_count),tmp_value))
+ self.purge_att_property("property_hist", "object",
+ object_name, tmp_name, cursor=cursor)
+ k = k + n_rows + 2
+
+ @use_cursor
+ def put_server_info(self, tmp_server, tmp_host, tmp_mode, tmp_level, tmp_extra):
+ cursor = self.cursor
+ # If it is an empty host name -> get previous host where running
+ previous_host = ""
+ if self.fire_to_starter:
+ if tmp_host == "":
+ adm_dev_name = "dserver/" + tmp_server
+ previous_host = self.get_device_host(adm_dev_name)
+ # first delete the server from the server table
+ cursor.execute('DELETE FROM server WHERE name=?', (tmp_server,))
+ # insert the new info for this server
+ cursor.execute('INSERT INTO server SET name=? ,host=? ,mode=? ,level=?', ( tmp_server, tmp_host, tmp_mode, tmp_level))
+ # Update host's starter to update controlled servers list
+ if self.fire_to_starter:
+ hosts = []
+ if previous_host == "":
+ hosts.append(tmp_host)
+ else:
+ hosts.append(previous_host)
+ self.send_starter_cmd(hosts)
+
+ @use_cursor
+ def uexport_device(self, dev_name):
+ cursor = self.cursor
+ self._info("un-export device(dev_name=%s)", dev_name)
+ cursor.execute('UPDATE device SET exported=0,stopped=NOW() WHERE name LIKE ?', (dev_name,))
+
+ @use_cursor
+ def uexport_event(self, event_name):
+ cursor = self.cursor
+ self._info("un-export event (event_name=%s)", event_name)
+ cursor.execute('UPDATE event SET exported=0,stopped=NOW() WHERE name LIKE ?', (event_name,))
+
+ @use_cursor
+ def uexport_server(self, server_name):
+ cursor = self.cursor
+ self._info("un-export all devices from server ", server_name)
+ cursor.execute('UPDATE device SET exported=0,stopped=NOW() WHERE server LIKE ?', (server_name,))
+
+ @use_cursor
+ def delete_all_device_attribute_property(self, dev_name, attr_list):
+ cursor = self.cursor
+ for attr_name in attr_list:
+ self._info("_delete_all_device_attribute_property(): delete device %s attribute %s property(ies) from database", dev_name, attr_name)
+ #Is there something to delete ?
+ cursor.execute('SELECT DISTINCT name FROM property_attribute_device WHERE device =? AND attribute = ?', (dev_name,attr_name))
+ rows = cursor.fetchall()
+ if len(rows) != 0:
+ cursor.execute('DELETE FROM property_attribute_device WHERE device = ? AND attribute = ?', (dev_name,attr_name))
+ # Mark this property as deleted
+ for row in rows:
+ hist_id = self.get_id('device_attribute', cursor=cursor)
+ cursor.execute('INSERT INTO property_attribute_device_hist SET device=?,attribute=?,name=?,id=?,count=\'0\',value=\'DELETED\'', (dev_name,attr_name,row[0], hist_id))
+ self.purge_att_property("property_attribute_device_hist", "device",
+ dev_name, attr_name, row[0], cursor=cursor)
+
+ @use_cursor
+ def my_sql_select(self, cmd):
+ cursor = self.cursor
+ cursor.execute(cmd)
+ result_long = []
+ result_str = []
+ rows = cursor.fetchall()
+ nb_fields = 0
+ for row in rows:
+ if row == None:
+ result_str.append("")
+ result_long.append(0)
+ else:
+ for field in row:
+ nb_fields = nb_fields + 1
+ if field != None:
+ result_str.append(str(field))
+ result_long.append(1)
+ else:
+ result_str.append("")
+ result_long.append(0)
+ result_long.append(len(rows))
+ result_long.append(nb_fields)
+
+ result = (result_long, result_str)
+ return result
+
+
+
+ @use_cursor
+ def get_csdb_server_list(self):
+ cursor = self.cursor
+
+ cursor.execute('SELECT DISTINCT ior FROM device WHERE exported=1 AND domain=\'sys\' AND family=\'database\'')
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_attribute_alias2(self, attr_name):
+ cursor = self.cursor
+ cursor.execute('SELECT alias from attribute_alias WHERE name LIKE ? ',(attr_name,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def get_alias_attribute(self, alias_name):
+ cursor = self.cursor
+ cursor.execute('SELECT name from attribute_alias WHERE alias LIKE ? ',(alias_name,))
+ return [ row[0] for row in cursor.fetchall() ]
+
+ @use_cursor
+ def rename_server(self, old_name, new_name):
+ cursor = self.cursor
+ # Check that the new name is not already used
+ new_adm_name = "dserver/" + new_name
+ cursor.execute('SELECT name from device WHERE name = ? ',(new_adm_name,))
+ rows = cursor.fetchall()
+ if len(rows) != 0:
+ th_exc(DB_SQLError,
+ "Device server process name " + attribute_alias + "is already used !",
+ "DataBase::DbRenameServer()")
+
+ # get host where running
+ previous_host = ""
+ if self.fire_to_starter:
+ try:
+ adm_dev = "dserver/" + old_name
+ previous_host = self.get_device_host(adm_dev)
+ except:
+ th_exc(DB_IncorrectServerName,
+ "Server " + old_name + "not defined in database!",
+ "DataBase::DbRenameServer()")
+ # Change ds exec name. This means
+ # 1 - Update the device's server column
+ # 2 - Change the ds admin device name
+ # 3 - Change admin device property (if any)
+ # 4 - Change admin device attribute property (if any)
+
+ old_adm_name = "dserver/" + old_name
+ tmp_new = new_name.split('/')
+ new_exec = tmp_new[0]
+ new_inst = tmp_new[1]
+ new_adm_name = "dserver/" + new_name
+
+ cursor.execute('UPDATE device SET name =?, family =?, mamber =? WHERE name =?', (new_adm_name, new_exec, new_inst, old_adm_name))
+
+ cursor.execute('UPDATE property_device set device=? WHERE device=?', (new_adm_name, old_adm_name))
+
+ cursor.execute('UPDATE property_attribute_device set device=? WHERE device=?', (new_adm_name, old_adm_name))
+
+ # Update host's starter to update controlled servers list
+ if self.fire_to_starter:
+ hosts = []
+ if previous_host == "":
+ hosts.append(tmp_host)
+ else:
+ hosts.append(previous_host)
+ self.send_starter_cmd(hosts)
+
+
+class sqlite3(Tango_dbapi2):
+
+ DB_API_NAME = 'sqlite3'
+
+def main():
+ db = Tango_sqlite3()
+ db.add_device("MyServer/my1", ("a/b/c", ("a", "b", "c")), "MyClass")
+ db.close_db()
+
+def get_db():
+ return Executor.submit(sqlite3).result()
+
+if __name__ == "__main__":
+ main()
diff --git a/src/boost/python/db.py b/src/boost/python/db.py
index 68352d5..416e021 100644
--- a/src/boost/python/db.py
+++ b/src/boost/python/db.py
@@ -1044,7 +1044,7 @@ def __doc_Database():
Example :
dev_info = DbDevInfo()
dev_info.name = 'my/own/device'
- dev_info.class = 'MyDevice'
+ dev_info._class = 'MyDevice'
dev_info.server = 'MyServer/test'
db.add_device(dev_info)
diff --git a/src/boost/python/device_proxy.py.new b/src/boost/python/device_proxy.py.new
new file mode 100644
index 0000000..19428a8
--- /dev/null
+++ b/src/boost/python/device_proxy.py.new
@@ -0,0 +1,2088 @@
+# ------------------------------------------------------------------------------
+# This file is part of PyTango (http://www.tinyurl.com/PyTango)
+#
+# Copyright 2006-2012 CELLS / ALBA Synchrotron, Bellaterra, Spain
+# Copyright 2013-2014 European Synchrotron Radiation Facility, Grenoble, France
+#
+# Distributed under the terms of the GNU Lesser General Public License,
+# either version 3 of the License, or (at your option) any later version.
+# See LICENSE.txt for more info.
+# ------------------------------------------------------------------------------
+
+"""
+This is an internal PyTango module.
+"""
+
+from __future__ import with_statement
+
+__all__ = ["device_proxy_init", "get_device_proxy"]
+
+__docformat__ = "restructuredtext"
+
+import threading
+import collections
+
+from ._PyTango import StdStringVector, DbData, DbDatum, AttributeInfo, \
+ AttributeInfoEx, AttributeInfoList, AttributeInfoListEx, DeviceProxy, \
+ __CallBackAutoDie, EventType, DevFailed, Except, \
+ ExtractAs, GreenMode
+from ._PyTango import __CallBackPushEvent as _CallBackPushEvent
+
+from .utils import callable_weakref
+from .utils import is_pure_str, is_non_str_seq, is_integer
+from .utils import seq_2_StdStringVector, StdStringVector_2_seq
+from .utils import seq_2_DbData, DbData_2_dict
+from .utils import document_method as __document_method
+from .green import result, submit, green, get_green_mode
+
+
+def get_device_proxy(*args, **kwargs):
+ """get_device_proxy(self, dev_name, green_mode=None, wait=True, timeout=True) -> DeviceProxy
+ get_device_proxy(self, dev_name, need_check_acc, green_mode=None, wait=True, timeout=None) -> DeviceProxy
+
+ Returns a new :class:`~PyTango.DeviceProxy`.
+ There is no difference between using this function and the direct
+ :class:`~PyTango.DeviceProxy` constructor if you use the default kwargs.
+
+ The added value of this function becomes evident when you choose a green_mode
+ to be *Futures* or *Gevent*. The DeviceProxy constructor internally makes some
+ network calls which makes it *slow*. By using one of the *green modes* as
+ green_mode you are allowing other python code to be executed in a cooperative way.
+
+ .. note::
+ The timeout parameter has no relation with the tango device client side
+ timeout (gettable by :meth:`~PyTango.DeviceProxy.get_timeout_millis` and
+ settable through :meth:`~PyTango.DeviceProxy.set_timeout_millis`)
+
+ :param dev_name: the device name or alias
+ :type dev_name: str
+ :param need_check_acc: in first version of the function it defaults to True.
+ Determines if at creation time of DeviceProxy it should check
+ for channel access (rarely used)
+ :type need_check_acc: bool
+ :param green_mode: determines the mode of execution of the device (including
+ the way it is created). Defaults to the current global
+ green_mode (check :func:`~PyTango.get_green_mode` and
+ :func:`~PyTango.set_green_mode`)
+ :type green_mode: :obj:`~PyTango.GreenMode`
+ :param wait: whether or not to wait for result. If green_mode
+ Ignored when green_mode is Synchronous (always waits).
+ :type wait: bool
+ :param timeout: The number of seconds to wait for the result.
+ If None, then there is no limit on the wait time.
+ Ignored when green_mode is Synchronous or wait is False.
+ :type timeout: float
+ :returns:
+ if green_mode is Synchronous or wait is True:
+ :class:`~PyTango.DeviceProxy`
+ else if green_mode is Futures:
+ :class:`concurrent.futures.Future`
+ else if green_mode is Gevent:
+ :class:`gevent.event.AsynchResult`
+ :throws:
+ * a *DevFailed* if green_mode is Synchronous or wait is True
+ and there is an error creating the device.
+ * a *concurrent.futures.TimeoutError* if green_mode is Futures,
+ wait is False, timeout is not None and the time to create the device
+ has expired.
+ * a *gevent.timeout.Timeout* if green_mode is Gevent, wait is False,
+ timeout is not None and the time to create the device has expired.
+
+ New in PyTango 8.1.0
+ """
+ # we cannot use the green wrapper because it consumes the green_mode and we
+ # want to forward it to the DeviceProxy constructor
+ green_mode = kwargs.get('green_mode', get_green_mode())
+ wait = kwargs.pop('wait', True)
+ timeout = kwargs.pop('timeout', None)
+
+ d = submit(green_mode, DeviceProxy, *args, **kwargs)
+ return result(d, green_mode, wait=wait, timeout=timeout)
+
+
+class __TangoInfo(object):
+ """Helper class for when DeviceProxy.info() is not available"""
+
+ def __init__(self):
+ self.dev_class = self.dev_type = 'Device'
+ self.doc_url = 'http://www.esrf.fr/computing/cs/tango/tango_doc/ds_doc/'
+ self.server_host = 'Unknown'
+ self.server_id = 'Unknown'
+ self.server_version = 1
+
+#-------------------------------------------------------------------------------
+# Pythonic API: transform tango commands into methods and tango attributes into
+# class members
+#-------------------------------------------------------------------------------
+
+def __check_read_attribute(dev_attr):
+ if dev_attr.has_failed:
+ raise DevFailed(*dev_attr.get_err_stack())
+ return dev_attr
+
+def __DeviceProxy__init__(self, *args, **kwargs):
+ self.__dict__['_green_mode'] = kwargs.pop('green_mode', None)
+ self.__dict__['_executors'] = executors = {}
+ executors[GreenMode.Futures] = kwargs.pop('executor', None)
+ executors[GreenMode.Gevent] = kwargs.pop('threadpool', None)
+ return DeviceProxy.__init_orig__(self, *args, **kwargs)
+
+def __DeviceProxy__get_green_mode(self):
+ """Returns the green mode in use by this DeviceProxy.
+
+ :returns: the green mode in use by this DeviceProxy.
+ :rtype: GreenMode
+
+ .. seealso::
+ :func:`PyTango.get_green_mode`
+ :func:`PyTango.set_green_mode`
+
+ New in PyTango 8.1.0
+ """
+ gm = self._green_mode
+ if gm is None:
+ gm = get_green_mode()
+ return gm
+
+def __DeviceProxy__set_green_mode(self, green_mode=None):
+ """Sets the green mode to be used by this DeviceProxy
+ Setting it to None means use the global PyTango green mode
+ (see :func:`PyTango.get_green_mode`).
+
+ :param green_mode: the new green mode
+ :type green_mode: GreenMode
+
+ New in PyTango 8.1.0
+ """
+ self._green_mode = green_mode
+
+
+def __DeviceProxy__refresh_cmd_cache(self):
+ cmd_list = self.command_list_query()
+ cmd_cache = {}
+ for cmd in cmd_list:
+ n = cmd.cmd_name.lower()
+ cmd_cache[n] = cmd, None
+ self.__dict__['__cmd_cache'] = cmd_cache
+
+def __DeviceProxy__refresh_attr_cache(self):
+ attr_cache = [attr_name.lower() for attr_name in self.get_attribute_list()]
+ self.__dict__['__attr_cache'] = attr_cache
+
+def __DeviceProxy__getattr(self, name):
+ # trait_names is a feature of IPython. Hopefully they will solve
+ # ticket http://ipython.scipy.org/ipython/ipython/ticket/229 someday
+ # and the ugly trait_names could be removed.
+ if name[:2] == "__" or name == 'trait_names':
+ raise AttributeError(name)
+
+ name_l = name.lower()
+ cmd_info = None
+ if not hasattr(self, '__cmd_cache'):
+ try:
+ self.__refresh_cmd_cache()
+ except:
+ pass
+ try:
+ cmd_info = self.__cmd_cache[name_l]
+ except:
+ pass
+
+ if cmd_info is not None:
+ d, f = cmd_info
+ if f is None:
+ doc = "%s(%s) -> %s\n\n" % (d.cmd_name, d.in_type, d.out_type)
+ doc += " - in (%s): %s\n" % (d.in_type, d.in_type_desc)
+ doc += " - out (%s): %s\n" % (d.out_type, d.out_type_desc)
+ def f(*args, **kwds): return self.command_inout(name, *args, **kwds)
+ f.__doc__ = doc
+ self.__cmd_cache[name_l] = d, f
+ return f
+
+ find_attr = True
+ if not hasattr(self, '__attr_cache') or name_l not in self.__attr_cache:
+ try:
+ self.__refresh_attr_cache()
+ except:
+ find_attr = False
+
+ if not find_attr or name_l not in self.__attr_cache:
+ raise AttributeError(name)
+
+ return self.read_attribute(name).value
+
+def __DeviceProxy__setattr(self, name, value):
+ try:
+ if not hasattr(self, '__attr_cache') or name.lower() not in self.__attr_cache:
+ self.__refresh_attr_cache()
+ except:
+ return super(DeviceProxy, self).__setattr__(name, value)
+
+ if name.lower() in self.__attr_cache:
+ self.write_attribute(name, value)
+ else:
+ return super(DeviceProxy, self).__setattr__(name, value)
+
+
+def __DeviceProxy__getAttributeNames(self):
+ """Return list of magic attributes to extend introspection."""
+ try:
+ lst = [cmd.cmd_name for cmd in self.command_list_query()]
+ lst += self.get_attribute_list()
+ lst += list(map(str.lower, lst))
+ lst.sort()
+ return lst
+ except Exception:
+ pass
+ return []
+
+def __DeviceProxy__getitem(self, key):
+ return self.read_attribute(key)
+
+def __DeviceProxy__setitem(self, key, value):
+ return self.write_attribute(key, value)
+
+def __DeviceProxy__contains(self, key):
+ return key.lower() in map(str.lower, self.get_attribute_list())
+
+def __DeviceProxy__read_attribute(self, value, extract_as=ExtractAs.Numpy):
+ return __check_read_attribute(self._read_attribute(value, extract_as))
+
+#def __DeviceProxy__read_attribute(self, value, extract_as=ExtractAs.Numpy,
+# green_mode=None, wait=True, timeout=None):
+# green_mode, submit = submitable(green_mode)
+# result = submit(__DeviceProxy__read_attribute_raw, self, value, extract_as=extract_as)
+# return get_result(result, green_mode, wait=wait, timeout=timeout)
+
+def __DeviceProxy__read_attributes_asynch(self, attr_names, cb=None, extract_as=ExtractAs.Numpy):
+ """
+ read_attributes_asynch( self, attr_names) -> int
+
+ Read asynchronously (polling model) the list of specified attributes.
+
+ Parameters :
+ - attr_names : (sequence<str>) A list of attributes to read.
+ It should be a StdStringVector or a sequence of str.
+ Return : an asynchronous call identifier which is needed to get
+ attributes value.
+
+ Throws : ConnectionFailed
+
+ New in PyTango 7.0.0
+
+ read_attributes_asynch( self, attr_names, callback, extract_as=Numpy) -> None
+
+ Read asynchronously (callback model) an attribute list.
+
+ Parameters :
+ - attr_names : (sequence<str>) A list of attributes to read. See read_attributes.
+ - callback : (callable) This callback object should be an instance of a
+ user class with an attr_read() method. It can also
+ be any callable object.
+ - extract_as : (ExtractAs) Defaults to numpy.
+ Return : None
+
+ Throws : ConnectionFailed
+
+ New in PyTango 7.0.0
+ """
+ if cb is None:
+ return self.__read_attributes_asynch(attr_names)
+
+ cb2 = __CallBackAutoDie()
+ if isinstance(cb, collections.Callable):
+ cb2.attr_read = cb
+ else:
+ cb2.attr_read = cb.attr_read
+ return self.__read_attributes_asynch(attr_names, cb2, extract_as)
+
+def __DeviceProxy__read_attribute_asynch(self, attr_name, cb=None):
+ """
+ read_attribute_asynch( self, attr_name) -> int
+ read_attribute_asynch( self, attr_name, callback) -> None
+
+ Shortcut to self.read_attributes_asynch([attr_name], cb)
+
+ New in PyTango 7.0.0
+ """
+ return self.read_attributes_asynch([attr_name], cb)
+
+def __DeviceProxy__read_attribute_reply(self, *args, **kwds):
+ """
+ read_attribute_reply( self, id, extract_as) -> int
+ read_attribute_reply( self, id, timeout, extract_as) -> None
+
+ Shortcut to self.read_attributes_reply()[0]
+
+ New in PyTango 7.0.0
+ """
+ return __check_read_attribute(self.read_attributes_reply(*args, **kwds)[0])
+
+def __DeviceProxy__write_attributes_asynch(self, attr_values, cb=None):
+ """
+ write_attributes_asynch( self, values) -> int
+
+ Write asynchronously (polling model) the specified attributes.
+
+ Parameters :
+ - values : (any) See write_attributes.
+ Return : An asynchronous call identifier which is needed to get the
+ server reply
+
+ Throws : ConnectionFailed
+
+ New in PyTango 7.0.0
+
+ write_attributes_asynch( self, values, callback) -> None
+
+ Write asynchronously (callback model) a single attribute.
+
+ Parameters :
+ - values : (any) See write_attributes.
+ - callback : (callable) This callback object should be an instance of a user
+ class with an attr_written() method . It can also be any
+ callable object.
+ Return : None
+
+ Throws : ConnectionFailed
+
+ New in PyTango 7.0.0
+ """
+ if cb is None:
+ return self.__write_attributes_asynch(attr_values)
+
+ cb2 = __CallBackAutoDie()
+ if isinstance(cb, collections.Callable):
+ cb2.attr_write = cb
+ else:
+ cb2.attr_write = cb.attr_write
+ return self.__write_attributes_asynch(attr_values, cb2)
+
+def __DeviceProxy__write_attribute_asynch(self, attr_name, value, cb=None):
+ """
+ write_attributes_asynch( self, values) -> int
+ write_attributes_asynch( self, values, callback) -> None
+
+ Shortcut to self.write_attributes_asynch([attr_name, value], cb)
+
+ New in PyTango 7.0.0
+ """
+ return self.write_attributes_asynch([(attr_name, value)], cb)
+
+def __DeviceProxy__write_read_attribute(self, attr_name, value, extract_as=ExtractAs.Numpy):
+ return __check_read_attribute(self._write_read_attribute(attr_name, value, extract_as))
+
+def __DeviceProxy__get_property(self, propname, value=None):
+ """
+ get_property(propname, value=None) -> PyTango.DbData
+
+ Get a (list) property(ies) for a device.
+
+ This method accepts the following types as propname parameter:
+ 1. string [in] - single property data to be fetched
+ 2. sequence<string> [in] - several property data to be fetched
+ 3. PyTango.DbDatum [in] - single property data to be fetched
+ 4. PyTango.DbData [in,out] - several property data to be fetched.
+ 5. sequence<DbDatum> - several property data to be feteched
+
+ Note: for cases 3, 4 and 5 the 'value' parameter if given, is IGNORED.
+
+ If value is given it must be a PyTango.DbData that will be filled with the
+ property values
+
+ Parameters :
+ - propname : (any) property(ies) name(s)
+ - value : (DbData) (optional, default is None meaning that the
+ method will create internally a PyTango.DbData and return
+ it filled with the property values
+
+ Return : (DbData) object containing the property(ies) value(s). If a
+ PyTango.DbData is given as parameter, it returns the same
+ object otherwise a new PyTango.DbData is returned
+
+ Throws : NonDbDevice, ConnectionFailed (with database),
+ CommunicationFailed (with database),
+ DevFailed from database device
+ """
+
+ if is_pure_str(propname) or isinstance(propname, StdStringVector):
+ new_value = value
+ if new_value is None:
+ new_value = DbData()
+ self._get_property(propname, new_value)
+ return DbData_2_dict(new_value)
+ elif isinstance(propname, DbDatum):
+ new_value = DbData()
+ new_value.append(propname)
+ self._get_property(new_value)
+ return DbData_2_dict(new_value)
+ elif isinstance(propname, collections.Sequence):
+ if isinstance(propname, DbData):
+ self._get_property(propname)
+ return DbData_2_dict(propname)
+
+ if is_pure_str(propname[0]):
+ new_propname = StdStringVector()
+ for i in propname: new_propname.append(i)
+ new_value = value
+ if new_value is None:
+ new_value = DbData()
+ self._get_property(new_propname, new_value)
+ return DbData_2_dict(new_value)
+ elif isinstance(propname[0], DbDatum):
+ new_value = DbData()
+ for i in propname: new_value.append(i)
+ self._get_property(new_value)
+ return DbData_2_dict(new_value)
+
+def __DeviceProxy__put_property(self, value):
+ """
+ put_property(self, value) -> None
+
+ Insert or update a list of properties for this device.
+ This method accepts the following types as value parameter:
+ 1. PyTango.DbDatum - single property data to be inserted
+ 2. PyTango.DbData - several property data to be inserted
+ 3. sequence<DbDatum> - several property data to be inserted
+ 4. dict<str, DbDatum> - keys are property names and value has data to be inserted
+ 5. dict<str, seq<str>> - keys are property names and value has data to be inserted
+ 6. dict<str, obj> - keys are property names and str(obj) is property value
+
+ Parameters :
+ - value : can be one of the following:
+ 1. PyTango.DbDatum - single property data to be inserted
+ 2. PyTango.DbData - several property data to be inserted
+ 3. sequence<DbDatum> - several property data to be inserted
+ 4. dict<str, DbDatum> - keys are property names and value has data to be inserted
+ 5. dict<str, seq<str>> - keys are property names and value has data to be inserted
+ 6. dict<str, obj> - keys are property names and str(obj) is property value
+
+ Return : None
+
+ Throws : ConnectionFailed, CommunicationFailed
+ DevFailed from device (DB_SQLError)
+ """
+ if isinstance(value, DbData):
+ pass
+ elif isinstance(value, DbDatum):
+ new_value = DbData()
+ new_value.append(value)
+ value = new_value
+ elif is_non_str_seq(value):
+ new_value = seq_2_DbData(value)
+ elif isinstance(value, collections.Mapping):
+ new_value = DbData()
+ for k, v in value.items():
+ if isinstance(v, DbDatum):
+ new_value.append(v)
+ continue
+ db_datum = DbDatum(k)
+ if is_non_str_seq(v):
+ seq_2_StdStringVector(v, db_datum.value_string)
+ else:
+ db_datum.value_string.append(str(v))
+ new_value.append(db_datum)
+ value = new_value
+ else:
+ raise TypeError('value must be a PyTango.DbDatum, PyTango.DbData,'\
+ 'a sequence<DbDatum> or a dictionary')
+ return self._put_property(value)
+
+def __DeviceProxy__delete_property(self, value):
+ """
+ delete_property(self, value)
+
+ Delete a the given of properties for this device.
+ This method accepts the following types as value parameter:
+
+ 1. string [in] - single property to be deleted
+ 2. PyTango.DbDatum [in] - single property data to be deleted
+ 3. PyTango.DbData [in] - several property data to be deleted
+ 4. sequence<string> [in]- several property data to be deleted
+ 5. sequence<DbDatum> [in] - several property data to be deleted
+ 6. dict<str, obj> [in] - keys are property names to be deleted (values are ignored)
+ 7. dict<str, DbDatum> [in] - several DbDatum.name are property names to be deleted (keys are ignored)
+
+ Parameters :
+ - value : can be one of the following:
+
+ 1. string [in] - single property data to be deleted
+ 2. PyTango.DbDatum [in] - single property data to be deleted
+ 3. PyTango.DbData [in] - several property data to be deleted
+ 4. sequence<string> [in]- several property data to be deleted
+ 5. sequence<DbDatum> [in] - several property data to be deleted
+ 6. dict<str, obj> [in] - keys are property names to be deleted (values are ignored)
+ 7. dict<str, DbDatum> [in] - several DbDatum.name are property names to be deleted (keys are ignored)
+
+ Return : None
+
+ Throws : ConnectionFailed, CommunicationFailed
+ DevFailed from device (DB_SQLError)
+ """
+ if isinstance(value, DbData) or isinstance(value, StdStringVector) or \
+ is_pure_str(value):
+ new_value = value
+ elif isinstance(value, DbDatum):
+ new_value = DbData()
+ new_value.append(value)
+ elif isinstance(value, collections.Sequence):
+ new_value = DbData()
+ for e in value:
+ if isinstance(e, DbDatum):
+ new_value.append(e)
+ else:
+ new_value.append(DbDatum(str(e)))
+ elif isinstance(value, collections.Mapping):
+ new_value = DbData()
+ for k, v in value.items():
+ if isinstance(v, DbDatum):
+ new_value.append(v)
+ else:
+ new_value.append(DbDatum(k))
+ else:
+ raise TypeError('value must be a string, PyTango.DbDatum, '\
+ 'PyTango.DbData, a sequence or a dictionary')
+
+ return self._delete_property(new_value)
+
+def __DeviceProxy__get_property_list(self, filter, array=None):
+ """
+ get_property_list(self, filter, array=None) -> obj
+
+ Get the list of property names for the device. The parameter
+ filter allows the user to filter the returned name list. The
+ wildcard character is '*'. Only one wildcard character is
+ allowed in the filter parameter.
+
+ Parameters :
+ - filter[in] : (str) the filter wildcard
+ - array[out] : (sequence obj or None) (optional, default is None)
+ an array to be filled with the property names. If None
+ a new list will be created internally with the values.
+
+ Return : the given array filled with the property names (or a new list
+ if array is None)
+
+ Throws : NonDbDevice, WrongNameSyntax,
+ ConnectionFailed (with database),
+ CommunicationFailed (with database)
+ DevFailed from database device
+
+ New in PyTango 7.0.0
+ """
+
+ if array is None:
+ new_array = StdStringVector()
+ self._get_property_list(filter, new_array)
+ return new_array
+
+ if isinstance(array, StdStringVector):
+ self._get_property_list(filter, array)
+ return array
+ elif isinstance(array, collections.Sequence):
+ new_array = StdStringVector()
+ self._get_property_list(filter, new_array)
+ StdStringVector_2_seq(new_array, array)
+ return array
+
+ raise TypeError('array must be a mutable sequence<string>')
+
+def __DeviceProxy__get_attribute_config(self, value):
+ """
+ get_attribute_config( self, name) -> AttributeInfoEx
+
+ Return the attribute configuration for a single attribute.
+
+ Parameters :
+ - name : (str) attribute name
+ Return : (AttributeInfoEx) Object containing the attribute
+ information
+
+ Throws : ConnectionFailed, CommunicationFailed,
+ DevFailed from device
+
+ Deprecated: use get_attribute_config_ex instead
+
+ get_attribute_config( self, names) -> AttributeInfoList
+
+ Return the attribute configuration for the list of specified attributes. To get all the
+ attributes pass a sequence containing the constant PyTango.constants.AllAttr
+
+ Parameters :
+ - names : (sequence<str>) attribute names
+ Return : (AttributeInfoList) Object containing the attributes
+ information
+
+ Throws : ConnectionFailed, CommunicationFailed,
+ DevFailed from device
+
+ Deprecated: use get_attribute_config_ex instead
+ """
+ if isinstance(value, StdStringVector) or is_pure_str(value):
+ return self._get_attribute_config(value)
+ elif isinstance(value, collections.Sequence):
+ v = seq_2_StdStringVector(value)
+ return self._get_attribute_config(v)
+
+ raise TypeError('value must be a string or a sequence<string>')
+
+def __DeviceProxy__get_attribute_config_ex(self, value):
+ """
+ get_attribute_config_ex( self, name) -> AttributeInfoListEx :
+
+ Return the extended attribute configuration for a single attribute.
+
+ Parameters :
+ - name : (str) attribute name
+ Return : (AttributeInfoEx) Object containing the attribute
+ information
+
+ Throws : ConnectionFailed, CommunicationFailed,
+ DevFailed from device
+
+ get_attribute_config( self, names) -> AttributeInfoListEx :
+
+ Return the extended attribute configuration for the list of
+ specified attributes. To get all the attributes pass a sequence
+ containing the constant PyTango.constants.AllAttr
+
+ Parameters :
+ - names : (sequence<str>) attribute names
+ Return : (AttributeInfoList) Object containing the attributes
+ information
+
+ Throws : ConnectionFailed, CommunicationFailed,
+ DevFailed from device
+ """
+ if isinstance(value, StdStringVector):
+ return self._get_attribute_config_ex(value)
+ elif is_pure_str(value):
+ v = StdStringVector()
+ v.append(value)
+ return self._get_attribute_config_ex(v)
+ elif isinstance(value, collections.Sequence):
+ v = seq_2_StdStringVector(value)
+ return self._get_attribute_config_ex(v)
+
+ raise TypeError('value must be a string or a sequence<string>')
+
+def __DeviceProxy__set_attribute_config(self, value):
+ """
+ set_attribute_config( self, attr_info) -> None
+
+ Change the attribute configuration for the specified attribute
+
+ Parameters :
+ - attr_info : (AttributeInfo) attribute information
+ Return : None
+
+ Throws : ConnectionFailed, CommunicationFailed,
+ DevFailed from device
+
+ set_attribute_config( self, attr_info_ex) -> None
+
+ Change the extended attribute configuration for the specified attribute
+
+ Parameters :
+ - attr_info_ex : (AttributeInfoEx) extended attribute information
+ Return : None
+
+ Throws : ConnectionFailed, CommunicationFailed,
+ DevFailed from device
+
+ set_attribute_config( self, attr_info) -> None
+
+ Change the attributes configuration for the specified attributes
+
+ Parameters :
+ - attr_info : (sequence<AttributeInfo>) attributes information
+ Return : None
+
+ Throws : ConnectionFailed, CommunicationFailed,
+ DevFailed from device
+
+ set_attribute_config( self, attr_info_ex) -> None
+
+ Change the extended attributes configuration for the specified attributes
+
+ Parameters :
+ - attr_info_ex : (sequence<AttributeInfoListEx>) extended
+ attributes information
+ Return : None
+
+ Throws : ConnectionFailed, CommunicationFailed,
+ DevFailed from device
+
+ """
+ if isinstance(value, AttributeInfoEx):
+ v = AttributeInfoListEx()
+ v.append(value)
+ elif isinstance(value, AttributeInfo):
+ v = AttributeInfoList()
+ v.append(value)
+ elif isinstance(value, AttributeInfoList):
+ v = value
+ elif isinstance(value, AttributeInfoListEx):
+ v = value
+ elif isinstance(value, collections.Sequence):
+ if not len(value): return
+ if isinstance(value[0], AttributeInfoEx):
+ v = AttributeInfoListEx()
+ elif isinstance(value[0], AttributeInfo):
+ v = AttributeInfoList()
+ else:
+ raise TypeError('value must be a AttributeInfo, AttributeInfoEx, ' \
+ 'sequence<AttributeInfo> or sequence<AttributeInfoEx')
+ for i in value: v.append(i)
+ else:
+ raise TypeError('value must be a AttributeInfo, AttributeInfoEx, ' \
+ 'sequence<AttributeInfo> or sequence<AttributeInfoEx')
+
+ return self._set_attribute_config(v)
+
+def __DeviceProxy__get_event_map_lock(self):
+ """
+ Internal helper method"""
+ if not hasattr(self, '_subscribed_events_lock'):
+ # do it like this instead of self._subscribed_events = dict() to avoid
+ # calling __setattr__ which requests list of tango attributes from device
+ self.__dict__['_subscribed_events_lock'] = threading.Lock()
+ return self._subscribed_events_lock
+
+def __DeviceProxy__get_event_map(self):
+ """
+ Internal helper method"""
+ if not hasattr(self, '_subscribed_events'):
+ # do it like this instead of self._subscribed_events = dict() to avoid
+ # calling __setattr__ which requests list of tango attributes from device
+ self.__dict__['_subscribed_events'] = dict()
+ return self._subscribed_events
+
+
+import weakref
+class _CB(_CallBackPushEvent):
+
+ def __init__(self, f):
+ _CallBackPushEvent.__init__(self)
+ self.__f = callable_weakref(f)
+
+ def push_event(self, e):
+ f = self.__f()
+ if f is not None:
+ return f(e)
+
+
+def __DeviceProxy__subscribe_event (self, attr_name, event_type, cb_or_queuesize, filters=[], stateless=False, extract_as=ExtractAs.Numpy):
+ """
+ subscribe_event(self, attr_name, event, callback, filters=[], stateless=False, extract_as=Numpy) -> int
+
+ The client call to subscribe for event reception in the push model.
+ The client implements a callback method which is triggered when the
+ event is received. Filtering is done based on the reason specified and
+ the event type. For example when reading the state and the reason
+ specified is "change" the event will be fired only when the state
+ changes. Events consist of an attribute name and the event reason.
+ A standard set of reasons are implemented by the system, additional
+ device specific reasons can be implemented by device servers programmers.
+
+ Parameters :
+ - attr_name : (str) The device attribute name which will be sent
+ as an event e.g. "current".
+ - event_type: (EventType) Is the event reason and must be on the enumerated values:
+ * EventType.CHANGE_EVENT
+ * EventType.PERIODIC_EVENT
+ * EventType.ARCHIVE_EVENT
+ * EventType.ATTR_CONF_EVENT
+ * EventType.DATA_READY_EVENT
+ * EventType.USER_EVENT
+ - callback : (callable) Is any callable object or an object with a
+ callable "push_event" method.
+ - filters : (sequence<str>) A variable list of name,value pairs
+ which define additional filters for events.
+ - stateless : (bool) When the this flag is set to false, an exception will
+ be thrown when the event subscription encounters a problem.
+ With the stateless flag set to true, the event subscription
+ will always succeed, even if the corresponding device server
+ is not running. The keep alive thread will try every 10
+ seconds to subscribe for the specified event. At every
+ subscription retry, a callback is executed which contains
+ the corresponding exception
+ - extract_as : (ExtractAs)
+
+ Return : An event id which has to be specified when unsubscribing
+ from this event.
+
+ Throws : EventSystemFailed
+
+
+ subscribe_event(self, attr_name, event, queuesize, filters=[], stateless=False ) -> int
+
+ The client call to subscribe for event reception in the pull model.
+ Instead of a callback method the client has to specify the size of the
+ event reception buffer.
+ The event reception buffer is implemented as a round robin buffer. This
+ way the client can set-up different ways to receive events:
+
+ * Event reception buffer size = 1 : The client is interested only
+ in the value of the last event received. All other events that
+ have been received since the last reading are discarded.
+ * Event reception buffer size > 1 : The client has chosen to keep
+ an event history of a given size. When more events arrive since
+ the last reading, older events will be discarded.
+ * Event reception buffer size = ALL_EVENTS : The client buffers all
+ received events. The buffer size is unlimited and only restricted
+ by the available memory for the client.
+
+ All other parameters are similar to the descriptions given in the
+ other subscribe_event() version.
+ """
+
+ if isinstance(cb_or_queuesize, collections.Callable):
+# cb = _CallBackPushEvent()
+# cb.push_event = cb_or_queuesize
+ cb = _CB(cb_or_queuesize)
+ elif hasattr(cb_or_queuesize, "push_event") and isinstance(cb_or_queuesize.push_event, collections.Callable):
+# cb = _CallBackPushEvent()
+# cb.push_event = cb_or_queuesize.push_event
+ cb = _CB(cb_or_queuesize.push_event)
+ elif is_integer(cb_or_queuesize):
+ cb = cb_or_queuesize # queuesize
+ else:
+ raise TypeError("Parameter cb_or_queuesize should be a number, a "
+ "callable object or an object with a 'push_event' method.")
+
+ event_id = self.__subscribe_event(attr_name, event_type, cb, filters, stateless, extract_as)
+
+ with self.__get_event_map_lock():
+ se = self.__get_event_map()
+ evt_data = se.get(event_id)
+ if evt_data is not None:
+ desc = "Internal PyTango error:\n" \
+ "%s.subscribe_event(%s, %s) already has key %d assigned to (%s, %s)\n" \
+ "Please report error to PyTango" % \
+ (self, attr_name, event_type, event_id, evt_data[2], evt_data[1])
+ Except.throw_exception("Py_InternalError", desc, "DeviceProxy.subscribe_event")
+ se[event_id] = (cb, event_type, attr_name)
+ return event_id
+
+def __DeviceProxy__unsubscribe_event(self, event_id):
+ """
+ unsubscribe_event(self, event_id) -> None
+
+ Unsubscribes a client from receiving the event specified by event_id.
+
+ Parameters :
+ - event_id : (int) is the event identifier returned by the
+ DeviceProxy::subscribe_event(). Unlike in
+ TangoC++ we chech that the event_id has been
+ subscribed in this DeviceProxy.
+
+ Return : None
+
+ Throws : EventSystemFailed
+ """
+ with self.__get_event_map_lock():
+ se = self.__get_event_map()
+ if event_id not in se:
+ raise IndexError("This device proxy does not own this subscription " + str(event_id))
+ del se[event_id]
+ self.__unsubscribe_event(event_id)
+
+def __DeviceProxy__unsubscribe_event_all(self):
+ with self.__get_event_map_lock():
+ se = self.__get_event_map()
+ event_ids = list(se.keys())
+ se.clear()
+ for event_id in event_ids:
+ self.__unsubscribe_event(event_id)
+
+def __DeviceProxy__get_events(self, event_id, callback=None, extract_as=ExtractAs.Numpy):
+ """
+ get_events( event_id, callback=None, extract_as=Numpy) -> None
+
+ The method extracts all waiting events from the event reception buffer.
+
+ If callback is not None, it is executed for every event. During event
+ subscription the client must have chosen the pull model for this event.
+ The callback will receive a parameter of type EventData,
+ AttrConfEventData or DataReadyEventData depending on the type of the
+ event (event_type parameter of subscribe_event).
+
+ If callback is None, the method extracts all waiting events from the
+ event reception buffer. The returned event_list is a vector of
+ EventData, AttrConfEventData or DataReadyEventData pointers, just
+ the same data the callback would have received.
+
+ Parameters :
+ - event_id : (int) is the event identifier returned by the
+ DeviceProxy.subscribe_event() method.
+
+ - callback : (callable) Any callable object or any object with a "push_event"
+ method.
+
+ - extract_as: (ExtractAs)
+
+ Return : None
+
+ Throws : EventSystemFailed
+
+ See Also : subscribe_event
+
+ New in PyTango 7.0.0
+ """
+ if callback is None:
+ queuesize, event_type, attr_name = self.__get_event_map().get(event_id, (None, None, None))
+ if event_type is None:
+ raise ValueError("Invalid event_id. You are not subscribed to event %s." % str(event_id))
+ if event_type in ( EventType.CHANGE_EVENT,
+ EventType.QUALITY_EVENT,
+ EventType.PERIODIC_EVENT,
+ EventType.ARCHIVE_EVENT,
+ EventType.USER_EVENT ):
+ return self.__get_data_events(event_id, extract_as)
+ elif event_type in (EventType.ATTR_CONF_EVENT,):
+ return self.__get_attr_conf_events(event_id, extract_as)
+ elif event_type in (EventType.DATA_READY_EVENT,):
+ return self.__get_data_ready_events(event_id, extract_as)
+ else:
+ assert (False)
+ raise ValueError("Unknown event_type: " + str(event_type))
+ elif isinstance(callback, collections.Callable):
+ cb = _CallBackPushEvent()
+ cb.push_event = callback
+ return self.__get_callback_events(event_id, cb, extract_as)
+ elif hasattr(callback, 'push_event') and isinstance(callback.push_event, collections.Callable):
+ cb = _CallBackPushEvent()
+ cb.push_event = callback.push_event
+ return self.__get_callback_events(event_id, cb, extract_as)
+ else:
+ raise TypeError("Parameter 'callback' should be None, a callable object or an object with a 'push_event' method.")
+
+def __DeviceProxy___get_info_(self):
+ """Protected method that gets device info once and stores it in cache"""
+ if not hasattr(self, '_dev_info'):
+ try:
+ self.__dict__["_dev_info"] = self.info()
+ except:
+ return __TangoInfo()
+ return self._dev_info
+
+def __DeviceProxy__str(self):
+ info = self._get_info_()
+ return "%s(%s)" % (info.dev_class, self.dev_name())
+
+def __DeviceProxy__str(self):
+ info = self._get_info_()
+ return "%s(%s)" % (info.dev_class, self.dev_name())
+
+def __DeviceProxy__read_attributes(self, *args, **kwargs):
+ return self._read_attributes(*args, **kwargs)
+
+def __DeviceProxy__write_attribute(self, *args, **kwargs):
+ return self._write_attribute(*args, **kwargs)
+
+def __DeviceProxy__write_attributes(self, *args, **kwargs):
+ return self._write_attributes(*args, **kwargs)
+
+def __DeviceProxy__ping(self, *args, **kwargs):
+ return self._ping(*args, **kwargs)
+
+def __DeviceProxy__state(self, *args, **kwargs):
+ """state(self) -> DevState
+
+ A method which returns the state of the device.
+
+ Parameters : None
+ Return : (DevState) constant
+ Example :
+ dev_st = dev.state()
+ if dev_st == DevState.ON : ...
+ """
+ return self._state(*args, **kwargs)
+
+def __DeviceProxy__status(self, *args, **kwargs):
+ """status(self) -> str
+
+ A method which returns the status of the device as a string.
+
+ Parameters : None
+ Return : (str) describing the device status
+ """
+ return self._status(*args, **kwargs)
+
+def __DeviceProxy__write_attribute_reply(self, *args, **kwargs):
+ """
+ write_attribute_reply(self, id) -> None
+
+ Check if the answer of an asynchronous write_attribute is arrived
+ (polling model). If the reply is arrived and if it is a valid reply,
+ the call returned. If the reply is an exception, it is re-thrown by
+ this call. An exception is also thrown in case of the reply is not
+ yet arrived.
+
+ Parameters :
+ - id : (int) the asynchronous call identifier.
+ Return : None
+
+ Throws : AsynCall, AsynReplyNotArrived, CommunicationFailed, DevFailed from device.
+
+ New in PyTango 7.0.0
+
+ write_attribute_reply(self, id, timeout) -> None
+
+ Check if the answer of an asynchronous write_attribute is arrived
+ (polling model). id is the asynchronous call identifier. If the
+ reply is arrived and if it is a valid reply, the call returned. If
+ the reply is an exception, it is re-thrown by this call. If the
+ reply is not yet arrived, the call will wait (blocking the process)
+ for the time specified in timeout. If after timeout milliseconds,
+ the reply is still not there, an exception is thrown. If timeout is
+ set to 0, the call waits until the reply arrived.
+
+ Parameters :
+ - id : (int) the asynchronous call identifier.
+ - timeout : (int) the timeout
+
+ Return : None
+
+ Throws : AsynCall, AsynReplyNotArrived, CommunicationFailed, DevFailed from device.
+
+ New in PyTango 7.0.0
+ """
+ return self.write_attributes_reply(*args, **kwargs)
+
+def __init_DeviceProxy():
+ DeviceProxy.__init_orig__ = DeviceProxy.__init__
+ DeviceProxy.__init__ = __DeviceProxy__init__
+
+ DeviceProxy.get_green_mode = __DeviceProxy__get_green_mode
+ DeviceProxy.set_green_mode = __DeviceProxy__set_green_mode
+
+ DeviceProxy.__getattr__ = __DeviceProxy__getattr
+ DeviceProxy.__setattr__ = __DeviceProxy__setattr
+ DeviceProxy.__getitem__ = __DeviceProxy__getitem
+ DeviceProxy.__setitem__ = __DeviceProxy__setitem
+ DeviceProxy.__contains__ = __DeviceProxy__contains
+
+ DeviceProxy._getAttributeNames = __DeviceProxy__getAttributeNames
+
+ DeviceProxy.__refresh_cmd_cache = __DeviceProxy__refresh_cmd_cache
+ DeviceProxy.__refresh_attr_cache = __DeviceProxy__refresh_attr_cache
+
+ DeviceProxy.ping = green(__DeviceProxy__ping)
+ DeviceProxy.state = green(__DeviceProxy__state)
+ DeviceProxy.status = green(__DeviceProxy__status)
+
+ DeviceProxy.read_attribute = green(__DeviceProxy__read_attribute)
+ DeviceProxy.read_attributes = green(__DeviceProxy__read_attributes)
+ DeviceProxy.write_attribute = green(__DeviceProxy__write_attribute)
+ DeviceProxy.write_attributes = green(__DeviceProxy__write_attributes)
+ DeviceProxy.write_read_attribute = green(__DeviceProxy__write_read_attribute)
+
+ DeviceProxy.read_attributes_asynch = __DeviceProxy__read_attributes_asynch
+ DeviceProxy.read_attribute_asynch = __DeviceProxy__read_attribute_asynch
+ DeviceProxy.read_attribute_reply = __DeviceProxy__read_attribute_reply
+ DeviceProxy.write_attributes_asynch = __DeviceProxy__write_attributes_asynch
+ DeviceProxy.write_attribute_asynch = __DeviceProxy__write_attribute_asynch
+ DeviceProxy.write_attribute_reply = __DeviceProxy__write_attribute_reply
+
+ DeviceProxy.get_property = __DeviceProxy__get_property
+ DeviceProxy.put_property = __DeviceProxy__put_property
+ DeviceProxy.delete_property = __DeviceProxy__delete_property
+ DeviceProxy.get_property_list = __DeviceProxy__get_property_list
+ DeviceProxy.get_attribute_config = __DeviceProxy__get_attribute_config
+ DeviceProxy.get_attribute_config_ex = __DeviceProxy__get_attribute_config_ex
+ DeviceProxy.set_attribute_config = __DeviceProxy__set_attribute_config
+
+ DeviceProxy.__get_event_map = __DeviceProxy__get_event_map
+ DeviceProxy.__get_event_map_lock = __DeviceProxy__get_event_map_lock
+ DeviceProxy.subscribe_event = green(__DeviceProxy__subscribe_event)
+ DeviceProxy.unsubscribe_event = green(__DeviceProxy__unsubscribe_event)
+ DeviceProxy.__unsubscribe_event_all = __DeviceProxy__unsubscribe_event_all
+ DeviceProxy.get_events = __DeviceProxy__get_events
+ DeviceProxy.__str__ = __DeviceProxy__str
+ DeviceProxy.__repr__ = __DeviceProxy__str
+
+ DeviceProxy._get_info_ = __DeviceProxy___get_info_
+
+
+def __doc_DeviceProxy():
+ def document_method(method_name, desc, append=True):
+ return __document_method(DeviceProxy, method_name, desc, append)
+
+ DeviceProxy.__doc__ = """\
+ DeviceProxy is the high level Tango object which provides the client with
+ an easy-to-use interface to TANGO devices. DeviceProxy provides interfaces
+ to all TANGO Device interfaces.The DeviceProxy manages timeouts, stateless
+ connections and reconnection if the device server is restarted. To create
+ a DeviceProxy, a Tango Device name must be set in the object constructor.
+
+ Example :
+ dev = PyTango.DeviceProxy("sys/tg_test/1")
+
+ DeviceProxy(dev_name, green_mode=None, wait=True, timeout=True) -> DeviceProxy
+ DeviceProxy(self, dev_name, need_check_acc, green_mode=None, wait=True, timeout=True) -> DeviceProxy
+
+ Creates a new :class:`~PyTango.DeviceProxy`.
+
+ :param dev_name: the device name or alias
+ :type dev_name: str
+ :param need_check_acc: in first version of the function it defaults to True.
+ Determines if at creation time of DeviceProxy it should check
+ for channel access (rarely used)
+ :type need_check_acc: bool
+ :param green_mode: determines the mode of execution of the device (including.
+ the way it is created). Defaults to the current global
+ green_mode (check :func:`~PyTango.get_green_mode` and
+ :func:`~PyTango.set_green_mode`)
+ :type green_mode: :obj:`~PyTango.GreenMode`
+ :param wait: whether or not to wait for result. If green_mode
+ Ignored when green_mode is Synchronous (always waits).
+ :type wait: bool
+ :param timeout: The number of seconds to wait for the result.
+ If None, then there is no limit on the wait time.
+ Ignored when green_mode is Synchronous or wait is False.
+ :type timeout: float
+ :returns:
+ if green_mode is Synchronous or wait is True:
+ :class:`~PyTango.DeviceProxy`
+ elif green_mode is Futures:
+ :class:`concurrent.futures.Future`
+ elif green_mode is Gevent:
+ :class:`gevent.event.AsynchResult`
+ :throws:
+ * :class:`~PyTango.DevFailed` if green_mode is Synchronous or wait is True
+ and there is an error creating the device.
+ * :class:`concurrent.futures.TimeoutError` if green_mode is Futures,
+ wait is False, timeout is not None and the time to create the device
+ has expired.
+ * :class:`gevent.timeout.Timeout` if green_mode is Gevent, wait is False,
+ timeout is not None and the time to create the device has expired.
+
+ .. versionadded:: 8.1.0
+ *green_mode* parameter.
+ *wait* parameter.
+ *timeout* parameter.
+
+ """
+
+#-------------------------------------
+# General methods
+#-------------------------------------
+
+ document_method("info", """
+ info(self) -> DeviceInfo
+
+ A method which returns information on the device
+
+ Parameters : None
+ Return : (DeviceInfo) object
+ Example :
+ dev_info = dev.info()
+ print(dev_info.dev_class)
+ print(dev_info.server_id)
+ print(dev_info.server_host)
+ print(dev_info.server_version)
+ print(dev_info.doc_url)
+ print(dev_info.dev_type)
+
+ All DeviceInfo fields are strings except for the server_version
+ which is an integer"
+ """)
+
+ document_method("get_device_db", """
+ get_device_db(self) -> Database
+
+ Returns the internal database reference
+
+ Parameters : None
+ Return : (Database) object
+
+ New in PyTango 7.0.0
+ """)
+
+ document_method("adm_name", """
+ adm_name(self) -> str
+
+ Return the name of the corresponding administrator device. This is
+ useful if you need to send an administration command to the device
+ server, e.g restart it
+
+ New in PyTango 3.0.4
+ """)
+
+ document_method("description", """
+ description(self) -> str
+
+ Get device description.
+
+ Parameters : None
+ Return : (str) describing the device
+ """)
+
+ document_method("name", """
+ name(self) -> str
+
+ Return the device name from the device itself.
+ """)
+
+ document_method("alias", """
+ alias(self) -> str
+
+ Return the device alias if one is defined.
+ Otherwise, throws exception.
+
+ Return : (str) device alias
+ """)
+
+ document_method("get_tango_lib_version", """
+ get_tango_lib_version(self) -> int
+
+ Returns the Tango lib version number used by the remote device
+ Otherwise, throws exception.
+
+ Return : (int) The device Tango lib version as a 3 or 4 digits number.
+ Possible return value are: 100,200,500,520,700,800,810,...
+
+ New in PyTango 8.1.0
+ """)
+
+ document_method("ping", """
+ ping(self) -> int
+
+ A method which sends a ping to the device
+
+ Parameters : None
+ Return : (int) time elapsed in microseconds
+ Throws : exception if device is not alive
+ """)
+
+ document_method("black_box", """
+ black_box(self, n) -> sequence<str>
+
+ Get the last commands executed on the device server
+
+ Parameters :
+ - n : n number of commands to get
+ Return : (sequence<str>) sequence of strings containing the date, time,
+ command and from which client computer the command
+ was executed
+ Example :
+ print(black_box(4))
+ """)
+
+#-------------------------------------
+# Device methods
+#-------------------------------------
+
+ document_method("command_query", """
+ command_query(self, command) -> CommandInfo
+
+ Query the device for information about a single command.
+
+ Parameters :
+ - command : (str) command name
+ Return : (CommandInfo) object
+ Throws : ConnectionFailed, CommunicationFailed, DevFailed from device
+ Example :
+ com_info = dev.command_query(""DevString"")
+ print(com_info.cmd_name)
+ print(com_info.cmd_tag)
+ print(com_info.in_type)
+ print(com_info.out_type)
+ print(com_info.in_type_desc)
+ print(com_info.out_type_desc)
+ print(com_info.disp_level)
+
+ See CommandInfo documentation string form more detail
+ """)
+
+ document_method("command_list_query", """
+ command_list_query(self) -> sequence<CommandInfo>
+
+ Query the device for information on all commands.
+
+ Parameters : None
+ Return : (CommandInfoList) Sequence of CommandInfo objects
+ """)
+
+ document_method("import_info", """
+ import_info(self) -> DbDevImportInfo
+
+ Query the device for import info from the database.
+
+ Parameters : None
+ Return : (DbDevImportInfo)
+ Example :
+ dev_import = dev.import_info()
+ print(dev_import.name)
+ print(dev_import.exported)
+ print(dev_ior.ior)
+ print(dev_version.version)
+
+ All DbDevImportInfo fields are strings except for exported which
+ is an integer"
+ """)
+
+#-------------------------------------
+# Property methods
+#-------------------------------------
+ # get_property -> in code
+ # put_property -> in code
+ # delete_property -> in code
+ # get_property_list -> in code
+
+#-------------------------------------
+# Attribute methods
+#-------------------------------------
+ document_method("get_attribute_list", """
+ get_attribute_list(self) -> sequence<str>
+
+ Return the names of all attributes implemented for this device.
+
+ Parameters : None
+ Return : sequence<str>
+
+ Throws : ConnectionFailed, CommunicationFailed,
+ DevFailed from device
+ """)
+
+ # get_attribute_config -> in code
+ # get_attribute_config_ex -> in code
+
+ document_method("attribute_query", """
+ attribute_query(self, attr_name) -> AttributeInfoEx
+
+ Query the device for information about a single attribute.
+
+ Parameters :
+ - attr_name :(str) the attribute name
+ Return : (AttributeInfoEx) containing the attribute
+ configuration
+
+ Throws : ConnectionFailed, CommunicationFailed,
+ DevFailed from device
+ """)
+
+ document_method("attribute_list_query", """
+ attribute_list_query(self) -> sequence<AttributeInfo>
+
+ Query the device for info on all attributes. This method returns
+ a sequence of PyTango.AttributeInfo.
+
+ Parameters : None
+ Return : (sequence<AttributeInfo>) containing the
+ attributes configuration
+
+ Throws : ConnectionFailed, CommunicationFailed,
+ DevFailed from device
+ """)
+
+ document_method("attribute_list_query_ex", """
+ attribute_list_query_ex(self) -> sequence<AttributeInfoEx>
+
+ Query the device for info on all attributes. This method returns
+ a sequence of PyTango.AttributeInfoEx.
+
+ Parameters : None
+ Return : (sequence<AttributeInfoEx>) containing the
+ attributes configuration
+
+ Throws : ConnectionFailed, CommunicationFailed,
+ DevFailed from device
+ """)
+
+ # set_attribute_config -> in code
+
+ document_method("read_attribute", """
+ read_attribute(self, attr_name, extract_as=ExtractAs.Numpy, green_mode=None, wait=True, timeout=None) -> DeviceAttribute
+
+ Read a single attribute.
+
+ Parameters :
+ - attr_name : (str) The name of the attribute to read.
+ - extract_as : (ExtractAs) Defaults to numpy.
+ - green_mode : (GreenMode) Defaults to the current DeviceProxy GreenMode.
+ (see :meth:`~PyTango.DeviceProxy.get_green_mode` and
+ :meth:`~PyTango.DeviceProxy.set_green_mode`).
+ - wait : (bool) whether or not to wait for result. If green_mode
+ is *Synchronous*, this parameter is ignored as it always
+ waits for the result.
+ Ignored when green_mode is Synchronous (always waits).
+ - timeout : (float) The number of seconds to wait for the result.
+ If None, then there is no limit on the wait time.
+ Ignored when green_mode is Synchronous or wait is False.
+
+ Return : (DeviceAttribute)
+
+ Throws : ConnectionFailed, CommunicationFailed, DevFailed from device
+ TimeoutError (green_mode == Futures) If the future didn't finish executing before the given timeout.
+ Timeout (green_mode == Gevent) If the async result didn't finish executing before the given timeout.
+
+ .. versionchanged:: 7.1.4
+ For DevEncoded attributes, before it was returning a DeviceAttribute.value
+ as a tuple **(format<str>, data<str>)** no matter what was the *extract_as*
+ value was. Since 7.1.4, it returns a **(format<str>, data<buffer>)**
+ unless *extract_as* is String, in which case it returns
+ **(format<str>, data<str>)**.
+
+ .. versionchanged:: 8.0.0
+ For DevEncoded attributes, now returns a DeviceAttribute.value
+ as a tuple **(format<str>, data<bytes>)** unless *extract_as* is String,
+ in which case it returns **(format<str>, data<str>)**. Carefull, if
+ using python >= 3 data<str> is decoded using default python
+ *utf-8* encoding. This means that PyTango assumes tango DS was written
+ encapsulating string into *utf-8* which is the default python encoding.
+
+ .. versionadded:: 8.1.0
+ *green_mode* parameter.
+ *wait* parameter.
+ *timeout* parameter.
+ """)
+
+ document_method("read_attributes", """
+ read_attributes(self, attr_names, extract_as=ExtractAs.Numpy, green_mode=None, wait=True, timeout=None) -> sequence<DeviceAttribute>
+
+ Read the list of specified attributes.
+
+ Parameters :
+ - attr_names : (sequence<str>) A list of attributes to read.
+ - extract_as : (ExtractAs) Defaults to numpy.
+ - green_mode : (GreenMode) Defaults to the current DeviceProxy GreenMode.
+ (see :meth:`~PyTango.DeviceProxy.get_green_mode` and
+ :meth:`~PyTango.DeviceProxy.set_green_mode`).
+ - wait : (bool) whether or not to wait for result. If green_mode
+ is *Synchronous*, this parameter is ignored as it always
+ waits for the result.
+ Ignored when green_mode is Synchronous (always waits).
+ - timeout : (float) The number of seconds to wait for the result.
+ If None, then there is no limit on the wait time.
+ Ignored when green_mode is Synchronous or wait is False.
+
+ Return : (sequence<DeviceAttribute>)
+
+ Throws : ConnectionFailed, CommunicationFailed, DevFailed from device
+ TimeoutError (green_mode == Futures) If the future didn't finish executing before the given timeout.
+ Timeout (green_mode == Gevent) If the async result didn't finish executing before the given timeout.
+
+ .. versionadded:: 8.1.0
+ *green_mode* parameter.
+ *wait* parameter.
+ *timeout* parameter.
+ """)
+
+ document_method("write_attribute", """
+ write_attribute(self, attr_name, value, green_mode=None, wait=True, timeout=None) -> None
+ write_attribute(self, attr_info, value, green_mode=None, wait=True, timeout=None) -> None
+
+ Write a single attribute.
+
+ Parameters :
+ - attr_name : (str) The name of the attribute to write.
+ - attr_info : (AttributeInfo)
+ - value : The value. For non SCALAR attributes it may be any sequence of sequences.
+ - green_mode : (GreenMode) Defaults to the current DeviceProxy GreenMode.
+ (see :meth:`~PyTango.DeviceProxy.get_green_mode` and
+ :meth:`~PyTango.DeviceProxy.set_green_mode`).
+ - wait : (bool) whether or not to wait for result. If green_mode
+ is *Synchronous*, this parameter is ignored as it always
+ waits for the result.
+ Ignored when green_mode is Synchronous (always waits).
+ - timeout : (float) The number of seconds to wait for the result.
+ If None, then there is no limit on the wait time.
+ Ignored when green_mode is Synchronous or wait is False.
+
+ Throws : ConnectionFailed, CommunicationFailed, DeviceUnlocked, DevFailed from device
+ TimeoutError (green_mode == Futures) If the future didn't finish executing before the given timeout.
+ Timeout (green_mode == Gevent) If the async result didn't finish executing before the given timeout.
+
+ .. versionadded:: 8.1.0
+ *green_mode* parameter.
+ *wait* parameter.
+ *timeout* parameter.
+ """)
+
+ document_method("write_attributes", """
+ write_attributes(self, name_val, green_mode=None, wait=True, timeout=None) -> None
+
+ Write the specified attributes.
+
+ Parameters :
+ - name_val: A list of pairs (attr_name, value). See write_attribute
+ - green_mode : (GreenMode) Defaults to the current DeviceProxy GreenMode.
+ (see :meth:`~PyTango.DeviceProxy.get_green_mode` and
+ :meth:`~PyTango.DeviceProxy.set_green_mode`).
+ - wait : (bool) whether or not to wait for result. If green_mode
+ is *Synchronous*, this parameter is ignored as it always
+ waits for the result.
+ Ignored when green_mode is Synchronous (always waits).
+ - timeout : (float) The number of seconds to wait for the result.
+ If None, then there is no limit on the wait time.
+ Ignored when green_mode is Synchronous or wait is False.
+
+ Throws : ConnectionFailed, CommunicationFailed, DeviceUnlocked,
+ DevFailed or NamedDevFailedList from device
+ TimeoutError (green_mode == Futures) If the future didn't finish executing before the given timeout.
+ Timeout (green_mode == Gevent) If the async result didn't finish executing before the given timeout.
+
+ .. versionadded:: 8.1.0
+ *green_mode* parameter.
+ *wait* parameter.
+ *timeout* parameter.
+ """)
+
+ document_method("write_read_attribute", """
+ write_read_attribute(self, attr_name, value, extract_as=ExtractAs.Numpy, green_mode=None, wait=True, timeout=None) -> DeviceAttribute
+
+ Write then read a single attribute in a single network call.
+ By default (serialisation by device), the execution of this call in
+ the server can't be interrupted by other clients.
+
+ Parameters : see write_attribute(attr_name, value)
+ Return : A PyTango.DeviceAttribute object.
+
+ Throws : ConnectionFailed, CommunicationFailed, DeviceUnlocked,
+ DevFailed from device, WrongData
+ TimeoutError (green_mode == Futures) If the future didn't finish executing before the given timeout.
+ Timeout (green_mode == Gevent) If the async result didn't finish executing before the given timeout.
+
+ New in PyTango 7.0.0
+
+ .. versionadded:: 8.1.0
+ *green_mode* parameter.
+ *wait* parameter.
+ *timeout* parameter.
+ """)
+
+#-------------------------------------
+# History methods
+#-------------------------------------
+ document_method("command_history", """
+ command_history(self, cmd_name, depth) -> sequence<DeviceDataHistory>
+
+ Retrieve command history from the command polling buffer. See
+ chapter on Advanced Feature for all details regarding polling
+
+ Parameters :
+ - cmd_name : (str) Command name.
+ - depth : (int) The wanted history depth.
+ Return : This method returns a vector of DeviceDataHistory types.
+
+ Throws : NonSupportedFeature, ConnectionFailed,
+ CommunicationFailed, DevFailed from device
+ """)
+
+ document_method("attribute_history", """
+ attribute_history(self, attr_name, depth, extract_as=ExtractAs.Numpy) -> sequence<DeviceAttributeHistory>
+
+ Retrieve attribute history from the attribute polling buffer. See
+ chapter on Advanced Feature for all details regarding polling
+
+ Parameters :
+ - attr_name : (str) Attribute name.
+ - depth : (int) The wanted history depth.
+ - extract_as : (ExtractAs)
+
+ Return : This method returns a vector of DeviceAttributeHistory types.
+
+ Throws : NonSupportedFeature, ConnectionFailed,
+ CommunicationFailed, DevFailed from device
+ """)
+
+#-------------------------------------
+# Polling administration methods
+#-------------------------------------
+
+ document_method("polling_status", """
+ polling_status(self) -> sequence<str>
+
+ Return the device polling status.
+
+ Parameters : None
+ Return : (sequence<str>) One string for each polled command/attribute.
+ Each string is multi-line string with:
+
+ - attribute/command name
+ - attribute/command polling period in milliseconds
+ - attribute/command polling ring buffer
+ - time needed for last attribute/command execution in milliseconds
+ - time since data in the ring buffer has not been updated
+ - delta time between the last records in the ring buffer
+ - exception parameters in case of the last execution failed
+ """)
+
+ document_method("poll_command", """
+ poll_command(self, cmd_name, period) -> None
+
+ Add a command to the list of polled commands.
+
+ Parameters :
+ - cmd_name : (str) command name
+ - period : (int) polling period in milliseconds
+ Return : None
+ """)
+
+ document_method("poll_attribute", """
+ poll_attribute(self, attr_name, period) -> None
+
+ Add an attribute to the list of polled attributes.
+
+ Parameters :
+ - attr_name : (str) attribute name
+ - period : (int) polling period in milliseconds
+ Return : None
+ """)
+
+ document_method("get_command_poll_period", """
+ get_command_poll_period(self, cmd_name) -> int
+
+ Return the command polling period.
+
+ Parameters :
+ - cmd_name : (str) command name
+ Return : polling period in milliseconds
+ """)
+
+ document_method("get_attribute_poll_period", """
+ get_attribute_poll_period(self, attr_name) -> int
+
+ Return the attribute polling period.
+
+ Parameters :
+ - attr_name : (str) attribute name
+ Return : polling period in milliseconds
+ """)
+
+ document_method("is_command_polled", """
+ is_command_polled(self, cmd_name) -> bool
+
+ True if the command is polled.
+
+ Parameters :
+ - cmd_name : (str) command name
+ Return : boolean value
+ """)
+
+ document_method("is_attribute_polled", """
+ is_attribute_polled(self, attr_name) -> bool
+
+ True if the attribute is polled.
+
+ Parameters :
+ - attr_name : (str) attribute name
+ Return : boolean value
+ """)
+
+ document_method("stop_poll_command", """
+ stop_poll_command(self, cmd_name) -> None
+
+ Remove a command from the list of polled commands.
+
+ Parameters :
+ - cmd_name : (str) command name
+ Return : None
+ """)
+
+ document_method("stop_poll_attribute", """
+ stop_poll_attribute(self, attr_name) -> None
+
+ Remove an attribute from the list of polled attributes.
+
+ Parameters :
+ - attr_name : (str) attribute name
+ Return : None
+ """)
+
+#-------------------------------------
+# Asynchronous methods
+#-------------------------------------
+
+ # read_attribute_asynch -> in code
+ # read_attributes_asynch -> in code
+ # read_attribute_reply -> in code
+ document_method("read_attributes_reply", """
+ read_attributes_reply(self, id, extract_as=ExtractAs.Numpy) -> DeviceAttribute
+
+ Check if the answer of an asynchronous read_attribute is
+ arrived (polling model).
+
+ Parameters :
+ - id : (int) is the asynchronous call identifier.
+ - extract_as : (ExtractAs)
+ Return : If the reply is arrived and if it is a valid reply, it is
+ returned to the caller in a list of DeviceAttribute. If the
+ reply is an exception, it is re-thrown by this call. An
+ exception is also thrown in case of the reply is not yet
+ arrived.
+
+ Throws : AsynCall, AsynReplyNotArrived, ConnectionFailed,
+ CommunicationFailed, DevFailed from device
+
+ New in PyTango 7.0.0
+
+
+ read_attributes_reply(self, id, timeout, extract_as=ExtractAs.Numpy) -> DeviceAttribute
+
+ Check if the answer of an asynchronous read_attributes is arrived (polling model).
+
+ Parameters :
+ - id : (int) is the asynchronous call identifier.
+ - timeout : (int)
+ - extract_as : (ExtractAs)
+ Return : If the reply is arrived and if it is a valid reply, it is
+ returned to the caller in a list of DeviceAttribute. If the
+ reply is an exception, it is re-thrown by this call. If the
+ reply is not yet arrived, the call will wait (blocking the
+ process) for the time specified in timeout. If after
+ timeout milliseconds, the reply is still not there, an
+ exception is thrown. If timeout is set to 0, the call waits
+ until the reply arrived.
+
+ Throws : AsynCall, AsynReplyNotArrived, ConnectionFailed,
+ CommunicationFailed, DevFailed from device
+
+ New in PyTango 7.0.0
+ """)
+
+ document_method("pending_asynch_call", """
+ pending_asynch_call(self) -> int
+
+ Return number of device asynchronous pending requests"
+
+ New in PyTango 7.0.0
+ """)
+
+ # write_attributes_asynch -> in code
+
+ document_method("write_attributes_reply", """
+ write_attributes_reply(self, id) -> None
+
+ Check if the answer of an asynchronous write_attributes is arrived
+ (polling model). If the reply is arrived and if it is a valid reply,
+ the call returned. If the reply is an exception, it is re-thrown by
+ this call. An exception is also thrown in case of the reply is not
+ yet arrived.
+
+ Parameters :
+ - id : (int) the asynchronous call identifier.
+ Return : None
+
+ Throws : AsynCall, AsynReplyNotArrived, CommunicationFailed, DevFailed from device.
+
+ New in PyTango 7.0.0
+
+ write_attributes_reply(self, id, timeout) -> None
+
+ Check if the answer of an asynchronous write_attributes is arrived
+ (polling model). id is the asynchronous call identifier. If the
+ reply is arrived and if it is a valid reply, the call returned. If
+ the reply is an exception, it is re-thrown by this call. If the
+ reply is not yet arrived, the call will wait (blocking the process)
+ for the time specified in timeout. If after timeout milliseconds,
+ the reply is still not there, an exception is thrown. If timeout is
+ set to 0, the call waits until the reply arrived.
+
+ Parameters :
+ - id : (int) the asynchronous call identifier.
+ - timeout : (int) the timeout
+
+ Return : None
+
+ Throws : AsynCall, AsynReplyNotArrived, CommunicationFailed, DevFailed from device.
+
+ New in PyTango 7.0.0
+ """)
+
+#-------------------------------------
+# Logging administration methods
+#-------------------------------------
+
+ document_method("add_logging_target", """
+ add_logging_target(self, target_type_target_name) -> None
+
+ Adds a new logging target to the device.
+
+ The target_type_target_name input parameter must follow the
+ format: target_type::target_name. Supported target types are:
+ console, file and device. For a device target, the target_name
+ part of the target_type_target_name parameter must contain the
+ name of a log consumer device (as defined in A.8). For a file
+ target, target_name is the full path to the file to log to. If
+ omitted, the device's name is used to build the file name
+ (which is something like domain_family_member.log). Finally, the
+ target_name part of the target_type_target_name input parameter
+ is ignored in case of a console target and can be omitted.
+
+ Parameters :
+ - target_type_target_name : (str) logging target
+ Return : None
+
+ Throws : DevFailed from device
+
+ New in PyTango 7.0.0
+ """)
+
+ document_method("remove_logging_target", """
+ remove_logging_target(self, target_type_target_name) -> None
+
+ Removes a logging target from the device's target list.
+
+ The target_type_target_name input parameter must follow the
+ format: target_type::target_name. Supported target types are:
+ console, file and device. For a device target, the target_name
+ part of the target_type_target_name parameter must contain the
+ name of a log consumer device (as defined in ). For a file
+ target, target_name is the full path to the file to remove.
+ If omitted, the default log file is removed. Finally, the
+ target_name part of the target_type_target_name input parameter
+ is ignored in case of a console target and can be omitted.
+ If target_name is set to '*', all targets of the specified
+ target_type are removed.
+
+ Parameters :
+ - target_type_target_name : (str) logging target
+ Return : None
+
+ New in PyTango 7.0.0
+ """)
+
+ document_method("get_logging_target", """
+ get_logging_target(self) -> sequence<str>
+
+ Returns a sequence of string containing the current device's
+ logging targets. Each vector element has the following format:
+ target_type::target_name. An empty sequence is returned is the
+ device has no logging targets.
+
+ Parameters : None
+ Return : a squence<str> with the logging targets
+
+ New in PyTango 7.0.0
+ """)
+
+ document_method("get_logging_level", """
+ get_logging_level(self) -> int
+
+ Returns the current device's logging level, where:
+ - 0=OFF
+ - 1=FATAL
+ - 2=ERROR
+ - 3=WARNING
+ - 4=INFO
+ - 5=DEBUG
+
+ Parameters :None
+ Return : (int) representing the current logging level
+
+ New in PyTango 7.0.0
+ """)
+
+ document_method("set_logging_level", """
+ set_logging_level(self, (int)level) -> None
+
+ Changes the device's logging level, where:
+ - 0=OFF
+ - 1=FATAL
+ - 2=ERROR
+ - 3=WARNING
+ - 4=INFO
+ - 5=DEBUG
+
+ Parameters :
+ - level : (int) logging level
+ Return : None
+
+ New in PyTango 7.0.0
+ """)
+
+#-------------------------------------
+# Event methods
+#-------------------------------------
+
+ # subscribe_event -> in code
+ # unsubscribe_event -> in code
+ # get_events -> in code
+
+ document_method("event_queue_size", """
+ event_queue_size(self, event_id) -> int
+
+ Returns the number of stored events in the event reception
+ buffer. After every call to DeviceProxy.get_events(), the event
+ queue size is 0. During event subscription the client must have
+ chosen the 'pull model' for this event. event_id is the event
+ identifier returned by the DeviceProxy.subscribe_event() method.
+
+ Parameters :
+ - event_id : (int) event identifier
+ Return : an integer with the queue size
+
+ Throws : EventSystemFailed
+
+ New in PyTango 7.0.0
+ """)
+
+ document_method("get_last_event_date", """
+ get_last_event_date(self, event_id) -> TimeVal
+
+ Returns the arrival time of the last event stored in the event
+ reception buffer. After every call to DeviceProxy:get_events(),
+ the event reception buffer is empty. In this case an exception
+ will be returned. During event subscription the client must have
+ chosen the 'pull model' for this event. event_id is the event
+ identifier returned by the DeviceProxy.subscribe_event() method.
+
+ Parameters :
+ - event_id : (int) event identifier
+ Return : (PyTango.TimeVal) representing the arrival time
+
+ Throws : EventSystemFailed
+
+ New in PyTango 7.0.0
+ """)
+
+ document_method("is_event_queue_empty", """
+ is_event_queue_empty(self, event_id) -> bool
+
+ Returns true when the event reception buffer is empty. During
+ event subscription the client must have chosen the 'pull model'
+ for this event. event_id is the event identifier returned by the
+ DeviceProxy.subscribe_event() method.
+
+ Parameters :
+ - event_id : (int) event identifier
+ Return : (bool) True if queue is empty or False otherwise
+
+ Throws : EventSystemFailed
+
+ New in PyTango 7.0.0
+ """)
+
+#-------------------------------------
+# Locking methods
+#-------------------------------------
+ document_method("lock", """
+ lock(self, (int)lock_validity) -> None
+
+ Lock a device. The lock_validity is the time (in seconds) the
+ lock is kept valid after the previous lock call. A default value
+ of 10 seconds is provided and should be fine in most cases. In
+ case it is necessary to change the lock validity, it's not
+ possible to ask for a validity less than a minimum value set to
+ 2 seconds. The library provided an automatic system to
+ periodically re lock the device until an unlock call. No code is
+ needed to start/stop this automatic re-locking system. The
+ locking system is re-entrant. It is then allowed to call this
+ method on a device already locked by the same process. The
+ locking system has the following features:
+
+ * It is impossible to lock the database device or any device
+ server process admin device
+ * Destroying a locked DeviceProxy unlocks the device
+ * Restarting a locked device keeps the lock
+ * It is impossible to restart a device locked by someone else
+ * Restarting a server breaks the lock
+
+ A locked device is protected against the following calls when
+ executed by another client:
+
+ * command_inout call except for device state and status
+ requested via command and for the set of commands defined as
+ allowed following the definition of allowed command in the
+ Tango control access schema.
+ * write_attribute call
+ * write_read_attribute call
+ * set_attribute_config call
+
+ Parameters :
+ - lock_validity : (int) lock validity time in seconds
+ (optional, default value is
+ PyTango.constants.DEFAULT_LOCK_VALIDITY)
+ Return : None
+
+ New in PyTango 7.0.0
+ """)
+
+ document_method("unlock", """
+ unlock(self, (bool)force) -> None
+
+ Unlock a device. If used, the method argument provides a back
+ door on the locking system. If this argument is set to true,
+ the device will be unlocked even if the caller is not the locker.
+ This feature is provided for administration purpopse and should
+ be used very carefully. If this feature is used, the locker will
+ receive a DeviceUnlocked during the next call which is normally
+ protected by the locking Tango system.
+
+ Parameters :
+ - force : (bool) force unlocking even if we are not the
+ locker (optional, default value is False)
+ Return : None
+
+ New in PyTango 7.0.0
+ """)
+
+ document_method("locking_status", """
+ locking_status(self) -> str
+
+ This method returns a plain string describing the device locking
+ status. This string can be:
+
+ * 'Device <device name> is not locked' in case the device is
+ not locked
+ * 'Device <device name> is locked by CPP or Python client with
+ PID <pid> from host <host name>' in case the device is
+ locked by a CPP client
+ * 'Device <device name> is locked by JAVA client class
+ <main class> from host <host name>' in case the device is
+ locked by a JAVA client
+
+ Parameters : None
+ Return : a string representing the current locking status
+
+ New in PyTango 7.0.0"
+ """)
+
+ document_method("is_locked", """
+ is_locked(self) -> bool
+
+ Returns True if the device is locked. Otherwise, returns False.
+
+ Parameters : None
+ Return : (bool) True if the device is locked. Otherwise, False
+
+ New in PyTango 7.0.0
+ """)
+
+ document_method("is_locked_by_me", """
+ is_locked_by_me(self) -> bool
+
+ Returns True if the device is locked by the caller. Otherwise,
+ returns False (device not locked or locked by someone else)
+
+ Parameters : None
+ Return : (bool) True if the device is locked by us.
+ Otherwise, False
+
+ New in PyTango 7.0.0
+ """)
+
+ document_method("get_locker", """
+ get_locker(self, lockinfo) -> bool
+
+ If the device is locked, this method returns True an set some
+ locker process informations in the structure passed as argument.
+ If the device is not locked, the method returns False.
+
+ Parameters :
+ - lockinfo [out] : (PyTango.LockInfo) object that will be filled
+ with lock informantion
+ Return : (bool) True if the device is locked by us.
+ Otherwise, False
+
+ New in PyTango 7.0.0
+ """)
+
+def device_proxy_init(doc=True):
+ __init_DeviceProxy()
+ if doc:
+ __doc_DeviceProxy()
diff --git a/src/boost/python/device_server.py b/src/boost/python/device_server.py
index e19cb5b..068b567 100644
--- a/src/boost/python/device_server.py
+++ b/src/boost/python/device_server.py
@@ -413,12 +413,10 @@ def __DeviceImpl___remove_attr_meth(self,attr_name):
delattr(self.__class__, allo_meth_name)
cl.dyn_att_added_methods.remove(attr_name)
-def __join_msg(msg):
- return ' '.join(map(str, msg))
-def __DeviceImpl__debug_stream(self, *msg):
+def __DeviceImpl__debug_stream(self, msg, *args):
"""
- debug_stream(self, *msg) -> None
+ debug_stream(self, msg, *args) -> None
Sends the given message to the tango debug stream.
@@ -430,11 +428,11 @@ def __DeviceImpl__debug_stream(self, *msg):
- msg : (str) the message to be sent to the debug stream
Return : None
"""
- self.__debug_stream(__join_msg(msg))
+ self.__debug_stream(msg % args)
-def __DeviceImpl__info_stream(self, *msg):
+def __DeviceImpl__info_stream(self, msg, *args):
"""
- info_stream(self, *msg) -> None
+ info_stream(self, msg, *args) -> None
Sends the given message to the tango info stream.
@@ -446,11 +444,11 @@ def __DeviceImpl__info_stream(self, *msg):
- msg : (str) the message to be sent to the info stream
Return : None
"""
- self.__info_stream(__join_msg(msg))
+ self.__info_stream(msg % args)
-def __DeviceImpl__warn_stream(self, *msg):
+def __DeviceImpl__warn_stream(self, msg, *args):
"""
- warn_stream(self, *msg) -> None
+ warn_stream(self, msg, *args) -> None
Sends the given message to the tango warn stream.
@@ -462,11 +460,11 @@ def __DeviceImpl__warn_stream(self, *msg):
- msg : (str) the message to be sent to the warn stream
Return : None
"""
- self.__warn_stream(__join_msg(msg))
+ self.__warn_stream(msg % args)
-def __DeviceImpl__error_stream(self, *msg):
+def __DeviceImpl__error_stream(self, msg, *args):
"""
- error_stream(self, *msg) -> None
+ error_stream(self, msg, *args) -> None
Sends the given message to the tango error stream.
@@ -478,11 +476,11 @@ def __DeviceImpl__error_stream(self, *msg):
- msg : (str) the message to be sent to the error stream
Return : None
"""
- self.__error_stream(__join_msg(msg))
+ self.__error_stream(msg % args)
-def __DeviceImpl__fatal_stream(self, *msg):
+def __DeviceImpl__fatal_stream(self, msg, *args):
"""
- fatal_stream(self, *msg) -> None
+ fatal_stream(self, msg, *args) -> None
Sends the given message to the tango fatal stream.
@@ -494,7 +492,7 @@ def __DeviceImpl__fatal_stream(self, *msg):
- msg : (str) the message to be sent to the fatal stream
Return : None
"""
- self.__fatal_stream(__join_msg(msg))
+ self.__fatal_stream(msg % args)
@property
def __DeviceImpl__debug(self):
@@ -549,22 +547,25 @@ def __init_DeviceImpl():
DeviceImpl.log_error = __DeviceImpl__error
DeviceImpl.log_fatal = __DeviceImpl__fatal
-def __Logger__log(self, level, *msg):
+def __Logger__log(self, level, msg, *args):
"""
- log(self, level, *msg) -> None
+ log(self, level, msg, *args) -> None
Sends the given message to the tango the selected stream.
Parameters :
- level: (Level.LevelLevel) Log level
- msg : (str) the message to be sent to the stream
+ - args: (seq<str>) list of optional message arguments
Return : None
+
+ .. versionchanged:
"""
- self.__log(level, __join_msg(msg))
+ self.__log(level, msg % args)
-def __Logger__log_unconditionally(self, level, *msg):
+def __Logger__log_unconditionally(self, level, msg, *args):
"""
- log_unconditionally(self, level, *msg) -> None
+ log_unconditionally(self, level, msg, *args) -> None
Sends the given message to the tango the selected stream,
without checking the level.
@@ -572,69 +573,75 @@ def __Logger__log_unconditionally(self, level, *msg):
Parameters :
- level: (Level.LevelLevel) Log level
- msg : (str) the message to be sent to the stream
+ - args: (seq<str>) list of optional message arguments
Return : None
"""
- self.__log_unconditionally(level, __join_msg(msg))
+ self.__log_unconditionally(level, msg % args)
-def __Logger__debug(self, *msg):
+def __Logger__debug(self, msg, *args):
"""
- debug(self, *msg) -> None
+ debug(self, msg, *args) -> None
Sends the given message to the tango debug stream.
Parameters :
- msg : (str) the message to be sent to the debug stream
+ - args: (seq<str>) list of optional message arguments
Return : None
"""
- self.__debug(__join_msg(msg))
+ self.__debug(msg % args)
-def __Logger__info(self, *msg):
+def __Logger__info(self, msg, *args):
"""
- info(self, *msg) -> None
+ info(self, msg, *args) -> None
Sends the given message to the tango info stream.
Parameters :
- msg : (str) the message to be sent to the info stream
+ - args: (seq<str>) list of optional message arguments
Return : None
"""
- self.__info(__join_msg(msg))
+ self.__info(msg % args)
-def __Logger__warn(self, *msg):
+def __Logger__warn(self, msg, *args):
"""
- warn(self, *msg) -> None
+ warn(self, msg, *args) -> None
Sends the given message to the tango warn stream.
Parameters :
- msg : (str) the message to be sent to the warn stream
+ - args: (seq<str>) list of optional message arguments
Return : None
"""
- self.__warn(__join_msg(msg))
+ self.__warn(msg % args)
-def __Logger__error(self, *msg):
+def __Logger__error(self, msg, *args):
"""
- error(self, *msg) -> None
+ error(self, msg, *args) -> None
Sends the given message to the tango error stream.
Parameters :
- msg : (str) the message to be sent to the error stream
+ - args: (seq<str>) list of optional message arguments
Return : None
"""
- self.__error(__join_msg(msg))
+ self.__error(msg % args)
-def __Logger__fatal(self, *msg):
+def __Logger__fatal(self, msg, *args):
"""
- fatal(self, *msg) -> None
+ fatal(self, msg, *args) -> None
Sends the given message to the tango fatal stream.
Parameters :
- msg : (str) the message to be sent to the fatal stream
+ - args: (seq<str>) list of optional message arguments
Return : None
"""
- self.__fatal(__join_msg(msg))
+ self.__fatal(msg % args)
def __Attr__str(self):
return '%s(%s)' % (self.__class__.__name__, self.get_name())
@@ -866,8 +873,8 @@ def __doc_DeviceImpl():
attribute type and format.
for SPECTRUM and IMAGE attributes, data can be any type of sequence of elements
compatible with the attribute type
- - str_data : (str) special variation for DevEncoded data type. In this case 'data' must also
- be a str.
+ - str_data : (str) special variation for DevEncoded data type. In this case 'data' must
+ be a str or an object with the buffer interface.
- except: (DevFailed) Instead of data, you may want to send an exception.
- dim_x : (int) the attribute x length. Default value is 1
- dim_y : (int) the attribute y length. Default value is 0
@@ -894,8 +901,8 @@ def __doc_DeviceImpl():
attribute type and format.
for SPECTRUM and IMAGE attributes, data can be any type of sequence of elements
compatible with the attribute type
- - str_data : (str) special variation for DevEncoded data type. In this case 'data' must also
- be a str.
+ - str_data : (str) special variation for DevEncoded data type. In this case 'data' must
+ be a str or an object with the buffer interface.
- except: (DevFailed) Instead of data, you may want to send an exception.
- dim_x : (int) the attribute x length. Default value is 1
- dim_y : (int) the attribute y length. Default value is 0
@@ -923,8 +930,8 @@ def __doc_DeviceImpl():
attribute type and format.
for SPECTRUM and IMAGE attributes, data can be any type of sequence of elements
compatible with the attribute type
- - str_data : (str) special variation for DevEncoded data type. In this case 'data' must also
- be a str.
+ - str_data : (str) special variation for DevEncoded data type. In this case 'data' must
+ be a str or an object with the buffer interface.
- dim_x : (int) the attribute x length. Default value is 1
- dim_y : (int) the attribute y length. Default value is 0
- time_stamp : (double) the time stamp
@@ -1061,6 +1068,32 @@ def __doc_DeviceImpl():
New in PyTango 7.1.2
""" )
+ document_method("get_attribute_poll_period", """
+ get_attribute_poll_period(self, attr_name) -> int
+
+ Returns the attribute polling period (ms) or 0 if the attribute
+ is not polled.
+
+ Parameters :
+ - attr_name : (str) attribute name
+ Return : (int) attribute polling period (ms) or 0 if it is not polled
+
+ New in PyTango 8.0.0
+ """ )
+
+ document_method("get_command_poll_period", """
+ get_command_poll_period(self, cmd_name) -> int
+
+ Returns the command polling period (ms) or 0 if the command
+ is not polled.
+
+ Parameters :
+ - cmd_name : (str) command name
+ Return : (int) command polling period (ms) or 0 if it is not polled
+
+ New in PyTango 8.0.0
+ """ )
+
document_method("check_command_exists", """
check_command_exists(self) -> None
@@ -1529,8 +1562,8 @@ def __doc_Attribute():
for IMAGE attributes.
The recommended sequence is a C continuous and aligned numpy
array, as it can be optimized.
- - str_data : (str) special variation for DevEncoded data type. In this case 'data' must also
- be a str.
+ - str_data : (str) special variation for DevEncoded data type. In this case 'data' must
+ be a str or an object with the buffer interface.
- dim_x : (int) [DEPRECATED] the attribute x length. Default value is 1
- dim_y : (int) [DEPRECATED] the attribute y length. Default value is 0
Return : None
@@ -1555,8 +1588,8 @@ def __doc_Attribute():
for IMAGE attributes.
The recommended sequence is a C continuous and aligned numpy
array, as it can be optimized.
- - str_data : (str) special variation for DevEncoded data type. In this case 'data' must also
- be a str.
+ - str_data : (str) special variation for DevEncoded data type. In this case 'data' must
+ be a str or an object with the buffer interface.
- dim_x : (int) [DEPRECATED] the attribute x length. Default value is 1
- dim_y : (int) [DEPRECATED] the attribute y length. Default value is 0
- time_stamp : (double) the time stamp
diff --git a/src/boost/python/group.py b/src/boost/python/group.py
index cbd75b7..d97e9c4 100644
--- a/src/boost/python/group.py
+++ b/src/boost/python/group.py
@@ -20,7 +20,7 @@ __docformat__ = "restructuredtext"
import operator
from ._PyTango import __Group as _RealGroup, StdStringVector
-from .utils import seq_2_StdStringVector
+from .utils import seq_2_StdStringVector, is_pure_str
from .utils import document_method as __document_method
import collections
@@ -54,7 +54,7 @@ class Group:
same attribute(s)/command(s) to be able to do parallel requests."""
def __init__(self, name):
- if isinstance(name, str):
+ if is_pure_str(name):
name = _RealGroup(name)
if not isinstance(name, _RealGroup):
raise TypeError("Constructor expected receives a str")
diff --git a/src/boost/python/ipython/__init__.py b/src/boost/python/ipython/__init__.py
index 7b7b29b..360eb89 100644
--- a/src/boost/python/ipython/__init__.py
+++ b/src/boost/python/ipython/__init__.py
@@ -10,7 +10,7 @@
# -----------------------------------------------------------------------------
__all__ = ["init_ipython", "install", "load_ipython_extension",
- "unload_ipython_extension", "load_config"]
+ "unload_ipython_extension", "load_config", "run", "run_qt"]
from .common import get_python_version
from .common import get_ipython_version
@@ -33,6 +33,9 @@ init_ipython = default_init_ipython
install = default_install
is_installed = lambda : False
+__run = None
+__run_qt = None
+
ipv = get_ipython_version()
if ipv >= "0.10" and ipv < "0.11":
@@ -67,3 +70,8 @@ def run():
if not is_installed():
install(verbose=False)
__run()
+
+def run_qt():
+ if not is_installed():
+ install(verbose=False)
+ __run(qt=True)
\ No newline at end of file
diff --git a/src/boost/python/ipython/common.py b/src/boost/python/ipython/common.py
index 1ae516f..2ee7ada 100644
--- a/src/boost/python/ipython/common.py
+++ b/src/boost/python/ipython/common.py
@@ -35,16 +35,19 @@ def get_ipython_version():
"""Returns the current IPython version"""
import IPython
v = None
- try:
+ if hasattr(IPython, "version_info"):
+ v = '.'.join(map(str, IPython.version_info[:3]))
+ else:
try:
- v = IPython.Release.version
- except:
try:
- v = IPython.release.version
+ v = IPython.Release.version
except:
- pass
- except:
- pass
+ try:
+ v = IPython.release.version
+ except:
+ pass
+ except:
+ pass
return StrictVersion(v)
#-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
diff --git a/src/boost/python/ipython/ipython_00_10/ipy_install.py b/src/boost/python/ipython/ipython_00_10/ipy_install.py
index 8fb2144..a8bc514 100644
--- a/src/boost/python/ipython/ipython_00_10/ipy_install.py
+++ b/src/boost/python/ipython/ipython_00_10/ipy_install.py
@@ -13,7 +13,6 @@
import sys
import os
-import io
import IPython.genutils
import PyTango
@@ -38,13 +37,16 @@ def is_installed(ipydir=None):
return os.path.isfile(f_name)
-def install(ipydir=None,verbose=True):
+def install(ipydir=None, verbose=True, profile='tango'):
install_dir = ipydir or IPython.genutils.get_ipython_dir()
f_name = os.path.join(install_dir, 'ipy_profile_tango.py')
if verbose:
- out = sys.stdout
+ def out(msg):
+ sys.stdout.write(msg)
+ sys.stdout.flush()
else:
- out = io.BytesIO()
+ out = lambda x : None
+
if ipydir is None and os.path.isfile(f_name):
print("Warning: The file '%s' already exists." % f_name)
r = ''
@@ -55,19 +57,18 @@ def install(ipydir=None,verbose=True):
return
profile = __PROFILE.format(pytangover=PyTango.Release.version, ipyver=IPython.Release.version)
- out.write("Installing tango extension to ipython... ")
- out.flush()
+ out("Installing tango extension to ipython... ")
try:
f = open(f_name, "w")
f.write(profile)
f.close()
- out.write("[DONE]\n\n")
+ out("[DONE]\n\n")
except:
- out.write("[FAILED]\n\n")
+ out("[FAILED]\n\n")
raise
ipy_user_config = os.path.join(IPython.genutils.get_ipython_dir(), 'ipy_user_conf.py')
- out.write("""\
+ out("""\
To start ipython with tango interface simply type on the command line:
%% ipython -p tango
diff --git a/src/boost/python/ipython/ipython_00_11/ipy_install.py b/src/boost/python/ipython/ipython_00_11/ipy_install.py
index d246cb9..cbf09a0 100644
--- a/src/boost/python/ipython/ipython_00_11/ipy_install.py
+++ b/src/boost/python/ipython/ipython_00_11/ipy_install.py
@@ -53,10 +53,12 @@ def is_installed(ipydir=None, profile='tango'):
def install(ipydir=None, verbose=True, profile='tango'):
if verbose:
- out = sys.stdout
+ def out(msg):
+ sys.stdout.write(msg)
+ sys.stdout.flush()
else:
- out = io.BytesIO()
-
+ out = lambda x : None
+
ipython_dir = ipydir or get_ipython_dir()
try:
p_dir = ProfileDir.find_profile_dir_by_name(ipython_dir, profile)
@@ -71,16 +73,15 @@ def install(ipydir=None, verbose=True, profile='tango'):
if not create_config:
return
- out.write("Installing tango extension to ipython... ")
- out.flush()
+ out("Installing tango extension to ipython... ")
profile = __PROFILE.format(pytangover=PyTango.Release.version,
ipyver=IPython.release.version)
with open(abs_config_file_name, "w") as f:
f.write(profile)
f.close()
- out.write("[DONE]\n\n")
- out.write("""\
+ out("[DONE]\n\n")
+ out("""\
To start ipython with tango interface simply type on the command line:
%% ipython --profile=tango
diff --git a/src/boost/python/ipython/ipython_00_11/ipython_00_11.py b/src/boost/python/ipython/ipython_00_11/ipython_00_11.py
index bbf5911..ac3a8d2 100644
--- a/src/boost/python/ipython/ipython_00_11/ipython_00_11.py
+++ b/src/boost/python/ipython/ipython_00_11/ipython_00_11.py
@@ -536,7 +536,7 @@ def __exc_handler(ip, etype, value, tb, tb_offset=None):
global _TANGO_ERR
user_ns[_TANGO_ERR] = etype, value, tb, tb_offset
if len(value.args):
- v = value[0]
+ v = value.args[0]
print("%s: %s" % (v.reason ,v.desc))
else:
print("Empty DevFailed")
diff --git a/src/boost/python/ipython/ipython_10_00/ipy_install.py b/src/boost/python/ipython/ipython_10_00/ipy_install.py
index d246cb9..b0f7bc4 100644
--- a/src/boost/python/ipython/ipython_10_00/ipy_install.py
+++ b/src/boost/python/ipython/ipython_10_00/ipy_install.py
@@ -13,9 +13,8 @@
from __future__ import with_statement
-import sys
import os
-import io
+import sys
import IPython
from IPython.core.profiledir import ProfileDirError, ProfileDir
@@ -51,11 +50,14 @@ def is_installed(ipydir=None, profile='tango'):
abs_config_file_name = os.path.join(p_dir.location, _CONFIG_FILE_NAME)
return os.path.isfile(abs_config_file_name)
+
def install(ipydir=None, verbose=True, profile='tango'):
if verbose:
- out = sys.stdout
+ def out(msg):
+ sys.stdout.write(msg)
+ sys.stdout.flush()
else:
- out = io.BytesIO()
+ out = lambda x : None
ipython_dir = ipydir or get_ipython_dir()
try:
@@ -71,16 +73,15 @@ def install(ipydir=None, verbose=True, profile='tango'):
if not create_config:
return
- out.write("Installing tango extension to ipython... ")
- out.flush()
+ out("Installing tango extension to ipython... ")
profile = __PROFILE.format(pytangover=PyTango.Release.version,
ipyver=IPython.release.version)
with open(abs_config_file_name, "w") as f:
f.write(profile)
f.close()
- out.write("[DONE]\n\n")
- out.write("""\
+ out("[DONE]\n\n")
+ out("""\
To start ipython with tango interface simply type on the command line:
%% ipython --profile=tango
diff --git a/src/boost/python/ipython/ipython_10_00/ipython_10_00.py b/src/boost/python/ipython/ipython_10_00/ipython_10_00.py
index 926f119..9137bc8 100644
--- a/src/boost/python/ipython/ipython_10_00/ipython_10_00.py
+++ b/src/boost/python/ipython/ipython_10_00/ipython_10_00.py
@@ -13,12 +13,13 @@
from __future__ import print_function
-__all__ = ["load_config", "load_ipython_extension", "unload_ipython_extension"]
+__all__ = ["load_config", "load_ipython_extension", "unload_ipython_extension",
+ "run"]
-import sys
import os
import re
import io
+import sys
import operator
import textwrap
@@ -398,12 +399,12 @@ def mon(self, parameter_s=''):
raise UsageError("%mon -d: must provide an attribute to unmonitor")
else:
try:
- dev, sep, attr = todel.rpartition("/")
+ dev, _, attr = todel.rpartition("/")
subscriptions = __get_device_subscriptions(dev)
- id = subscriptions[attr.lower()]
+ attr_id = subscriptions[attr.lower()]
del subscriptions[attr.lower()]
d = __get_device_proxy(dev)
- d.unsubscribe_event(id)
+ d.unsubscribe_event(attr_id)
print("Stopped monitoring '%s'" % todel)
except KeyError:
raise UsageError("%%mon -d: Not monitoring '%s'" % todel)
@@ -413,22 +414,23 @@ def mon(self, parameter_s=''):
toadd = args[0]
except IndexError:
raise UsageError("%mon -a: must provide an attribute to monitor")
- dev, sep, attr = toadd.rpartition("/")
+ dev, _, attr = toadd.rpartition("/")
subscriptions = __get_device_subscriptions(dev)
- id = subscriptions.get(attr.lower())
- if id is not None:
+ attr_id = subscriptions.get(attr.lower())
+ if attr_id is not None:
raise UsageError("%%mon -a: Already monitoring '%s'" % toadd)
d = __get_device_proxy(dev)
w = __get_event_log()
model = w.model()
- id = d.subscribe_event(attr, PyTango.EventType.CHANGE_EVENT, model, [])
- subscriptions[attr.lower()] = id
+ attr_id = d.subscribe_event(attr, PyTango.EventType.CHANGE_EVENT,
+ model, [])
+ subscriptions[attr.lower()] = attr_id
print("'%s' is now being monitored. Type 'mon' to see all events" % toadd)
elif 'r' in opts:
for d, v in db._db_cache.devices.items():
d, subs = v[3], v[4]
- for id in subs.values():
- d.unsubscribe_event(id)
+ for _id in subs.values():
+ d.unsubscribe_event(_id)
v[4] = {}
elif 'i' in opts:
try:
@@ -535,7 +537,7 @@ def __exc_handler(ip, etype, value, tb, tb_offset=None):
global _TANGO_ERR
user_ns[_TANGO_ERR] = etype, value, tb, tb_offset
if len(value.args):
- v = value[0]
+ v = value.args[0]
print("%s: %s" % (v.reason ,v.desc))
else:
print("Empty DevFailed")
@@ -742,7 +744,7 @@ def init_db(parameter_s=''):
r = db.command_inout("DbMySqlSelect", query)
row_nb, column_nb = r[0][-2], r[0][-1]
- results, data = r[0][:-2], r[1]
+ data = r[1]
assert row_nb == len(data) / column_nb
devices, aliases, servers, klasses = data[0::4], data[1::4], data[2::4], data[3::4]
@@ -795,7 +797,7 @@ def init_db(parameter_s=''):
r = db.command_inout("DbMySqlSelect", query)
row_nb, column_nb = r[0][-2], r[0][-1]
- results, data = r[0][:-2], r[1]
+ data = r[1]
assert row_nb == len(data) / column_nb
attributes, aliases = data[0::2], data[1::2]
@@ -879,7 +881,7 @@ def complete(text):
__DIRNAME = os.path.dirname(os.path.abspath(__file__))
__RES_DIR = os.path.join(__DIRNAME, os.path.pardir, 'resource')
-class __TangoInfo(object):
+class __TangoDeviceInfo(object):
"""Helper class for when DeviceProxy.info() is not available"""
def __init__(self, dev):
@@ -893,6 +895,69 @@ class __TangoInfo(object):
self.server_host = 'Unknown'
self.server_id = 'Unknown'
self.server_version = 1
+
+
+def __get_device_class_icon(klass="Device"):
+ icon_prop = "__icon"
+ db = __get_db()
+ try:
+ icon_filename = db.get_class_property(klass, icon_prop)[icon_prop]
+ if icon_filename:
+ icon_filename = icon_filename[0]
+ else:
+ icon_filename = klass.lower() + os.path.extsep + "png"
+ except:
+ icon_filename = klass.lower() + os.path.extsep + "png"
+
+ if os.path.isabs(icon_filename):
+ icon = icon_filename
+ else:
+ icon = os.path.join(__RES_DIR, icon_filename)
+ if not os.path.isfile(icon):
+ icon = os.path.join(__RES_DIR, "_class.png")
+ return icon
+
+
+__DEV_CLASS_HTML_TEMPLATE = """\
+<table border="0" cellpadding="2" width="100%">
+<tr><td width="140" rowspan="7" valign="middle" align="center"><img src="{icon}" height="128"/></td>
+ <td width="140">Name:</td><td><b>{name}</b></td></tr>
+<tr><td width="140">Super class:</td><td>{super_class}</td></tr>
+<tr><td width="140">Database:</td><td>{database}</td></tr>
+<tr><td width="140">Description:</td><td>{description}</td></tr>
+<tr><td width="140">Documentation:</td><td><a target="_blank" href="{doc_url}">{doc_url}</a></td></tr>
+</table>"""
+
+def __get_class_property_str(dev_class, prop_name, default=""):
+ data = __get_db().get_class_property(dev_class, prop_name)[prop_name]
+ if len(data):
+ return data[0]
+ else:
+ return default
+
+def display_deviceclass_html(dev_class):
+ """displayhook function for PyTango.DeviceProxy, rendered as HTML"""
+ fmt = dict(name=dev_class)
+ db = __get_db()
+ try:
+ fmt["database"] = db.get_db_host() + ":" + db.get_db_port()
+ except:
+ try:
+ fmt["database"] = db.get_file_name()
+ except:
+ fmt["database"] = "Unknown"
+
+ doc_url = __get_class_property_str(dev_class, "doc_url", "www.tango-controls.org")
+ try:
+ fmt["doc_url"] = doc_url[doc_url.index("http"):]
+ except ValueError:
+ fmt["doc_url"] = doc_url
+
+ fmt['icon'] = __get_device_class_icon(dev_class)
+ fmt['super_class'] = __get_class_property_str(dev_class, "InheritedFrom", "DeviceImpl")
+ fmt['description'] = __get_class_property_str(dev_class, "Description", "A Tango device class")
+ return __DEV_CLASS_HTML_TEMPLATE.format(**fmt)
+
def __get_device_icon(dev_proxy, klass="Device"):
icon_prop = "__icon"
@@ -935,7 +1000,7 @@ def display_deviceproxy_html(dev_proxy):
try:
info = dev_proxy.info()
except:
- info = __TangoInfo(dev_proxy)
+ info = __TangoDeviceInfo(dev_proxy)
name = dev_proxy.dev_name()
fmt = dict(dev_class=info.dev_class, server_id=info.server_id,
server_host=info.server_host, name=name)
@@ -1153,18 +1218,12 @@ def load_config(config):
i_shell = config.InteractiveShell
i_shell.colors = 'Linux'
- if ipy_ver >= "0.12":
- # ------------------------------------
- # PromptManager (ipython >= 0.12)
- # ------------------------------------
- prompt = config.PromptManager
- prompt.in_template = 'ITango [\\#]: '
- prompt.out_template = 'Result [\\#]: '
- else:
- # (Deprecated in ipython >= 0.12 use PromptManager.in_template)
- i_shell.prompt_in1 = 'ITango [\\#]: '
- # (Deprecated in ipython >= 0.12 use PromptManager.out_template)
- i_shell.prompt_out = 'Result [\\#]: '
+ # ------------------------------------
+ # PromptManager (ipython >= 0.12)
+ # ------------------------------------
+ prompt = config.PromptManager
+ prompt.in_template = 'ITango [\\#]: '
+ prompt.out_template = 'Result [\\#]: '
# ------------------------------------
# InteractiveShellApp
@@ -1232,7 +1291,7 @@ def unload_ipython_extension(ipython):
#print "Unloading PyTango IPython extension"
pass
-def run():
+def run(qt=False):
# overwrite the original IPython Qt widget with our own so we can put a
# customized banner. IPython may have been installed without Qt support so we
@@ -1265,6 +1324,10 @@ def run():
argv.append("--profile=tango")
except:
pass
-
- launch_new_instance()
+ if qt:
+ if not 'qtconsole' in argv:
+ argv.insert(1, 'qtconsole')
+ argv.append('--pylab=inline')
+
+ launch_new_instance()
diff --git a/src/boost/python/ipython/resource/serial.png b/src/boost/python/ipython/resource/serial.png
index 789d999..a7eef8d 100644
Binary files a/src/boost/python/ipython/resource/serial.png and b/src/boost/python/ipython/resource/serial.png differ
diff --git a/src/boost/python/pytango_init.py b/src/boost/python/pytango_init.py
index 3dc2573..3512914 100644
--- a/src/boost/python/pytango_init.py
+++ b/src/boost/python/pytango_init.py
@@ -64,8 +64,8 @@ def init_constants():
#UNAME = tuple(map(str, json.loads(constants.UNAME)))
tg_rt_ver_nb = _get_tango_lib_release()
- tg_rt_major_ver = tg_rt_ver_nb / 100
- tg_rt_minor_ver = tg_rt_ver_nb / 10 % 10
+ tg_rt_major_ver = tg_rt_ver_nb // 100
+ tg_rt_minor_ver = tg_rt_ver_nb // 10 % 10
tg_rt_patch_ver = tg_rt_ver_nb % 10
tg_rt_ver = ".".join(map(str, (tg_rt_major_ver, tg_rt_minor_ver,
tg_rt_patch_ver)))
diff --git a/src/boost/python/release.py b/src/boost/python/release.py
index b2d7a64..489d1c8 100644
--- a/src/boost/python/release.py
+++ b/src/boost/python/release.py
@@ -40,7 +40,7 @@ class Release:
- keywords : (seq<str>) list of keywords
- license : (str) the license"""
name = 'PyTango'
- version_info = (8, 1, 1, 'final', 0)
+ version_info = (8, 1, 2, 'final', 0)
version = '.'.join(map(str, version_info[:3]))
version_long = version + ''.join(map(str, version_info[3:]))
version_description = 'This version implements the C++ Tango 8.1 API.'
diff --git a/src/boost/python/server.py b/src/boost/python/server.py
index 3384ef3..989a54d 100644
--- a/src/boost/python/server.py
+++ b/src/boost/python/server.py
@@ -9,145 +9,7 @@
# See LICENSE.txt for more info.
# ------------------------------------------------------------------------------
-"""High Level API for writing Tango device servers.
-
-.. _pytango-hlapi-datatypes:
-
-.. rubric:: Data types
-
-When declaring attributes, properties or commands, one of the most important
-information is the data type. It is given by the keyword argument *dtype*.
-In order to provide a more *pythonic* interface, this argument is not restricted
-to the :obj:`~PyTango.CmdArgType` options.
-
-For example, to define a *SCALAR* :obj:`~PyTango.CmdArgType.DevLong`
-attribute you have several possibilities:
-
-#. :obj:`int`
-#. 'int'
-#. 'int32'
-#. 'integer'
-#. :obj:`PyTango.CmdArgType.DevLong`
-#. 'DevLong'
-#. :obj:`numpy.int32`
-
-To define a *SPECTRUM* attribute simply wrap the scalar data type in any
-python sequence:
-
-* using a *tuple*: ``(:obj:`int`,)`` or
-* using a *list*: ``[:obj:`int`]`` or
-* any other sequence type
-
-To define an *IMAGE* attribute simply wrap the scalar data type in any
-python sequence of sequences:
-
-* using a *tuple*: ``((:obj:`int`,),)`` or
-* using a *list*: ``[[:obj:`int`]]`` or
-* any other sequence type
-
-Below is the complete table of equivalences.
-
-======================================== ========================================
- type tango type
-======================================== ========================================
- ``None`` ``DevVoid``
- ``DevVoid`` ``DevVoid``
- ``DevBoolean`` ``DevBoolean``
- ``DevShort`` ``DevShort``
- ``DevLong`` ``DevLong``
- ``DevFloat`` ``DevFloat``
- ``DevDouble`` ``DevDouble``
- ``DevUShort`` ``DevUShort``
- ``DevULong`` ``DevULong``
- ``DevString`` ``DevString``
- ``DevVarCharArray`` ``DevVarCharArray``
- ``DevVarShortArray`` ``DevVarShortArray``
- ``DevVarLongArray`` ``DevVarLongArray``
- ``DevVarFloatArray`` ``DevVarFloatArray``
- ``DevVarDoubleArray`` ``DevVarDoubleArray``
- ``DevVarUShortArray`` ``DevVarUShortArray``
- ``DevVarULongArray`` ``DevVarULongArray``
- ``DevVarStringArray`` ``DevVarStringArray``
- ``DevVarLongStringArray`` ``DevVarLongStringArray``
- ``DevVarDoubleStringArray`` ``DevVarDoubleStringArray``
- ``DevState`` ``DevState``
- ``DevVarBooleanArray`` ``DevVarBooleanArray``
- ``DevUChar`` ``DevUChar``
- ``DevLong64`` ``DevLong64``
- ``DevULong64`` ``DevULong64``
- ``DevVarLong64Array`` ``DevVarLong64Array``
- ``DevVarULong64Array`` ``DevVarULong64Array``
- ``DevInt`` ``DevInt``
- ``DevEncoded`` ``DevEncoded``
- ``chr`` ``DevUChar``
- ``'DevBoolean'`` ``DevBoolean``
- ``'DevDouble'`` ``DevDouble``
- ``'DevEncoded'`` ``DevEncoded``
- ``'DevFloat'`` ``DevFloat``
- ``'DevInt'`` ``DevInt``
- ``'DevLong'`` ``DevLong``
- ``'DevLong64'`` ``DevLong64``
- ``'DevShort'`` ``DevShort``
- ``'DevState'`` ``DevState``
- ``'DevString'`` ``DevString``
- ``'DevUChar'`` ``DevUChar``
- ``'DevULong'`` ``DevULong``
- ``'DevULong64'`` ``DevULong64``
- ``'DevUShort'`` ``DevUShort``
- ``'DevVarBooleanArray'`` ``DevVarBooleanArray``
- ``'DevVarCharArray'`` ``DevVarCharArray``
- ``'DevVarDoubleArray'`` ``DevVarDoubleArray``
- ``'DevVarDoubleStringArray'`` ``DevVarDoubleStringArray``
- ``'DevVarFloatArray'`` ``DevVarFloatArray``
- ``'DevVarLong64Array'`` ``DevVarLong64Array``
- ``'DevVarLongArray'`` ``DevVarLongArray``
- ``'DevVarLongStringArray'`` ``DevVarLongStringArray``
- ``'DevVarShortArray'`` ``DevVarShortArray``
- ``'DevVarStringArray'`` ``DevVarStringArray``
- ``'DevVarULong64Array'`` ``DevVarULong64Array``
- ``'DevVarULongArray'`` ``DevVarULongArray``
- ``'DevVarUShortArray'`` ``DevVarUShortArray``
- ``'DevVoid'`` ``DevVoid``
- ``'None'`` ``DevVoid``
- ``'bool'`` ``DevBoolean``
- ``'boolean'`` ``DevBoolean``
- ``'byte'`` ``DevUChar``
- ``'bytearray'`` ``DevEncoded``
- ``'bytes'`` ``DevEncoded``
- ``'char'`` ``DevUChar``
- ``'chr'`` ``DevUChar``
- ``'double'`` ``DevDouble``
- ``'float'`` ``DevDouble``
- ``'float32'`` ``DevFloat``
- ``'float64'`` ``DevDouble``
- ``'int'`` ``DevLong``
- ``'int16'`` ``DevShort``
- ``'int32'`` ``DevLong``
- ``'int64'`` ``DevLong64``
- ``'str'`` ``DevString``
- ``'string'`` ``DevString``
- ``'text'`` ``DevString``
- ``'uint'`` ``DevULong``
- ``'uint16'`` ``DevUShort``
- ``'uint32'`` ``DevULong``
- ``'uint64'`` ``DevULong64``
- :py:obj:`float` ``DevDouble``
- :py:obj:`int` ``DevLong``
- :py:obj:`str` ``DevString``
- :py:obj:`bool` ``DevBoolean``
- :py:obj:`bytearray` ``DevEncoded``
- :py:obj:`numpy.bool_` ``DevBoolean``
- :py:obj:`numpy.int16` ``DevShort``
- :py:obj:`numpy.int32` ``DevLong``
- :py:obj:`numpy.int64` ``DevLong64``
- :py:obj:`numpy.uint8` ``DevUChar``
- :py:obj:`numpy.uint16` ``DevUShort``
- :py:obj:`numpy.uint32` ``DevULong``
- :py:obj:`numpy.uint64` ``DevULong64``
- :py:obj:`numpy.float32` ``DevFloat``
- :py:obj:`numpy.float64` ``DevDouble``
-======================================== ========================================
-"""
+"""Server helper classes for writing Tango device servers."""
from __future__ import with_statement
from __future__ import print_function
@@ -156,8 +18,10 @@ __all__ = ["DeviceMeta", "Device", "LatestDeviceImpl", "attribute", "command",
"device_property", "class_property", "server_run"]
import __builtin__
+
import sys
import inspect
+import operator
import functools
import traceback
@@ -213,7 +77,7 @@ def __build_to_tango_type():
if key.startswith("Dev"):
value = getattr(CmdArgType, key)
ret[key] = ret[value] = value
-
+
if constants.NUMPY_SUPPORT:
import numpy
FROM_TANGO_TO_NUMPY_TYPE = { \
@@ -230,10 +94,10 @@ def __build_to_tango_type():
CmdArgType.DevFloat : numpy.float32,
}
- for key,value in FROM_TANGO_TO_NUMPY_TYPE.items():
+ for key, value in FROM_TANGO_TO_NUMPY_TYPE.items():
ret[value] = key
return ret
-
+
TO_TANGO_TYPE = __build_to_tango_type()
@@ -256,7 +120,7 @@ def from_typeformat_to_type(dtype, dformat):
raise TypeError("Cannot translate IMAGE to tango type")
return scalar_to_array_type(dtype)
-
+
def set_complex_value(attr, value):
is_tuple = isinstance(value, tuple)
dtype, fmt = attr.get_data_type(), attr.get_data_format()
@@ -287,8 +151,8 @@ def set_complex_value(attr, value):
else:
attr.set_value(value)
-
-def check_tango_device_klass_attribute_read_method(tango_device_klass, method_name):
+
+def check_tango_device_klass_attribute_read_method(tango_device_klass, attribute):
"""Checks if method given by it's name for the given DeviceImpl class has
the correct signature. If a read/write method doesn't have a parameter
(the traditional Attribute), then the method is wrapped into another method
@@ -296,9 +160,15 @@ def check_tango_device_klass_attribute_read_method(tango_device_klass, method_na
:param tango_device_klass: a DeviceImpl class
:type tango_device_klass: class
- :param method_name: method to be cheched
- :type attr_data: str"""
- read_method = getattr(tango_device_klass, method_name)
+ :param attribute: the attribute data information
+ :type attribute: AttrData"""
+ read_method = getattr(attribute, "fget", None)
+ if read_method:
+ method_name = "__read_{0}__".format(attribute.attr_name)
+ attribute.read_method_name = method_name
+ else:
+ method_name = attribute.read_method_name
+ read_method = getattr(tango_device_klass, method_name)
@functools.wraps(read_method)
def read_attr(self, attr):
@@ -308,8 +178,8 @@ def check_tango_device_klass_attribute_read_method(tango_device_klass, method_na
return ret
setattr(tango_device_klass, method_name, read_attr)
-
-def check_tango_device_klass_attribute_write_method(tango_device_klass, method_name):
+
+def check_tango_device_klass_attribute_write_method(tango_device_klass, attribute):
"""Checks if method given by it's name for the given DeviceImpl class has
the correct signature. If a read/write method doesn't have a parameter
(the traditional Attribute), then the method is wrapped into another method
@@ -317,9 +187,15 @@ def check_tango_device_klass_attribute_write_method(tango_device_klass, method_n
:param tango_device_klass: a DeviceImpl class
:type tango_device_klass: class
- :param method_name: method to be cheched
- :type attr_data: str"""
- write_method = getattr(tango_device_klass, method_name)
+ :param attribute: the attribute data information
+ :type attribute: AttrData"""
+ write_method = getattr(attribute, "fset", None)
+ if write_method:
+ method_name = "__write_{0}__".format(attribute.attr_name)
+ attribute.write_method_name = method_name
+ else:
+ method_name = attribute.write_method_name
+ write_method = getattr(tango_device_klass, method_name)
@functools.wraps(write_method)
def write_attr(self, attr):
@@ -327,20 +203,22 @@ def check_tango_device_klass_attribute_write_method(tango_device_klass, method_n
return write_method(self, value)
setattr(tango_device_klass, method_name, write_attr)
-
-def check_tango_device_klass_attribute_methods(tango_device_klass, attr_data):
+
+def check_tango_device_klass_attribute_methods(tango_device_klass, attribute):
"""Checks if the read and write methods have the correct signature. If a
read/write method doesn't have a parameter (the traditional Attribute),
then the method is wrapped into another method to make this work
:param tango_device_klass: a DeviceImpl class
:type tango_device_klass: class
- :param attr_data: the attribute data information
- :type attr_data: AttrData"""
- if attr_data.attr_write in (AttrWriteType.READ, AttrWriteType.READ_WRITE):
- check_tango_device_klass_attribute_read_method(tango_device_klass, attr_data.read_method_name)
- if attr_data.attr_write in (AttrWriteType.WRITE, AttrWriteType.READ_WRITE):
- check_tango_device_klass_attribute_write_method(tango_device_klass, attr_data.write_method_name)
+ :param attribute: the attribute data information
+ :type attribute: AttrData"""
+ if attribute.attr_write in (AttrWriteType.READ, AttrWriteType.READ_WRITE):
+ check_tango_device_klass_attribute_read_method(tango_device_klass,
+ attribute)
+ if attribute.attr_write in (AttrWriteType.WRITE, AttrWriteType.READ_WRITE):
+ check_tango_device_klass_attribute_write_method(tango_device_klass,
+ attribute)
class _DeviceClass(DeviceClass):
@@ -366,17 +244,17 @@ class _DeviceClass(DeviceClass):
import traceback
dev.warn_stream("Failed to initialize dynamic attributes")
dev.debug_stream("Details: " + traceback.format_exc())
-
-
+
+
def create_tango_deviceclass_klass(tango_device_klass, attrs=None):
klass_name = tango_device_klass.__name__
if not issubclass(tango_device_klass, (Device)):
msg = "{0} device must inherit from PyTango.server.Device".format(klass_name)
raise Exception(msg)
-
+
if attrs is None:
attrs = tango_device_klass.__dict__
-
+
attr_list = {}
class_property_list = {}
device_property_list = {}
@@ -384,20 +262,25 @@ def create_tango_deviceclass_klass(tango_device_klass, attrs=None):
for attr_name, attr_obj in attrs.items():
if isinstance(attr_obj, attribute):
- attr_obj._set_name(attr_name)
+ if attr_obj.attr_name is None:
+ attr_obj._set_name(attr_name)
+ else:
+ attr_name = attr_obj.attr_name
attr_list[attr_name] = attr_obj
check_tango_device_klass_attribute_methods(tango_device_klass, attr_obj)
elif isinstance(attr_obj, device_property):
- device_property_list[attr_name] = [attr_obj.dtype, attr_obj.doc, attr_obj.default_value]
+ device_property_list[attr_name] = [attr_obj.dtype, attr_obj.doc,
+ attr_obj.default_value]
elif isinstance(attr_obj, class_property):
- class_property_list[attr_name] = [attr_obj.dtype, attr_obj.doc, attr_obj.default_value]
+ class_property_list[attr_name] = [attr_obj.dtype, attr_obj.doc,
+ attr_obj.default_value]
elif inspect.isroutine(attr_obj):
if hasattr(attr_obj, "__tango_command__"):
cmd_name, cmd_info = attr_obj.__tango_command__
cmd_list[cmd_name] = cmd_info
-
+
devclass_name = klass_name + "Class"
-
+
devclass_attrs = dict(class_property_list=class_property_list,
device_property_list=device_property_list,
cmd_list=cmd_list, attr_list=attr_list)
@@ -450,7 +333,7 @@ def DeviceMeta(name, bases, attrs):
class Device(LatestDeviceImpl):
"""High level DeviceImpl API. All Device specific classes should inherit
from this class."""
-
+
def __init__(self, cl, name):
LatestDeviceImpl.__init__(self, cl, name)
self.init_device()
@@ -459,7 +342,7 @@ class Device(LatestDeviceImpl):
"""Tango init_device method. Default implementation calls
:meth:`get_device_properties`"""
self.get_device_properties()
-
+
def always_executed_hook(self):
"""Tango always_executed_hook. Default implementation does nothing"""
pass
@@ -471,163 +354,205 @@ class Device(LatestDeviceImpl):
class attribute(AttrData):
- """declares a new tango attribute in a :class:`Device`. To be used like
-the python native :obj:`property` function. For example, to declare a
-scalar, `PyTango.DevDouble`, read-only attribute called *voltage* in a
-*PowerSupply* :class:`Device` do::
+ '''
+ declares a new tango attribute in a :class:`Device`. To be used like
+ the python native :obj:`property` function. For example, to declare a
+ scalar, `PyTango.DevDouble`, read-only attribute called *voltage* in a
+ *PowerSupply* :class:`Device` do::
- class PowerSupply(Device):
+ class PowerSupply(Device):
+ __metaclass__ = DeviceMeta
- voltage = attribute()
+ voltage = attribute()
+
+ def read_voltage(self):
+ return 999.999
+
+ The same can be achieved with::
+
+ class PowerSupply(Device):
+ __metaclass__ = DeviceMeta
+
+ @attribute
+ def voltage(self):
+ return 999.999
+
- def read_voltage(self):
- self.voltage = 1.0
-
-It receives multiple keyword arguments.
-
-===================== ================================ ======================================= =======================================================================================
-parameter type default value description
-===================== ================================ ======================================= =======================================================================================
-name :obj:`str` class member name alternative attribute name
-dtype :obj:`object` :obj:`~PyTango.CmdArgType.DevDouble` data type (see :ref:`Data type equivalence <pytango-hlapi-datatypes>`)
-dformat :obj:`~PyTango.AttrDataFormat` :obj:`~PyTango.AttrDataFormat.SCALAR` data format
-max_dim_x :obj:`int` 1 maximum size for x dimension (ignored for SCALAR format)
-max_dim_y :obj:`int` 0 maximum size for y dimension (ignored for SCALAR and SPECTRUM formats)
-display_level :obj:`~PyTango.DispLevel` :obj:`~PyTango.DisLevel.OPERATOR` display level
-polling_period :obj:`int` -1 polling period
-memorized :obj:`bool` False attribute should or not be memorized
-hw_memorized :obj:`bool` False write method should be called at startup when restoring memorize value (dangerous!)
-access :obj:`~PyTango.AttrWriteType` :obj:`~PyTango.AttrWriteType.READ` read only/ read write / write only access
-fget (or fread) :obj:`str` or :obj:`callable` 'read_<attr_name>' read method name or method object
-fset (or fwrite) :obj:`str` or :obj:`callable` 'write_<attr_name>' write method name or method object
-is_allowed :obj:`str` or :obj:`callable` 'is_<attr_name>_allowed' is allowed method name or method object
-label :obj:`str` '<attr_name>' attribute label
-doc (or description) :obj:`str` '' attribute description
-unit :obj:`str` '' physical units the attribute value is in
-standard_unit :obj:`str` '' physical standard unit
-display_unit :obj:`str` '' physical display unit (hint for clients)
-format :obj:`str` '6.2f' attribute representation format
-min_value :obj:`str` None minimum allowed value
-max_value :obj:`str` None maximum allowed value
-min_alarm :obj:`str` None minimum value to trigger attribute alarm
-max_alarm :obj:`str` None maximum value to trigger attribute alarm
-min_warning :obj:`str` None minimum value to trigger attribute warning
-max_warning :obj:`str` None maximum value to trigger attribute warning
-delta_val :obj:`str` None
-delta_t :obj:`str` None
-abs_change :obj:`str` None minimum value change between events that causes event filter to send the event
-rel_change :obj:`str` None minimum relative change between events that causes event filter to send the event (%)
-period :obj:`str` None
-archive_abs_change :obj:`str` None
-archive_rel_change :obj:`str` None
-archive_period :obj:`str` None
-===================== ================================ ======================================= ======================================================================================="""
-
- def __init__(self, **kwargs):
+ It receives multiple keyword arguments.
+
+ ===================== ================================ ======================================= =======================================================================================
+ parameter type default value description
+ ===================== ================================ ======================================= =======================================================================================
+ name :obj:`str` class member name alternative attribute name
+ dtype :obj:`object` :obj:`~PyTango.CmdArgType.DevDouble` data type (see :ref:`Data type equivalence <pytango-hlapi-datatypes>`)
+ dformat :obj:`~PyTango.AttrDataFormat` :obj:`~PyTango.AttrDataFormat.SCALAR` data format
+ max_dim_x :obj:`int` 1 maximum size for x dimension (ignored for SCALAR format)
+ max_dim_y :obj:`int` 0 maximum size for y dimension (ignored for SCALAR and SPECTRUM formats)
+ display_level :obj:`~PyTango.DispLevel` :obj:`~PyTango.DisLevel.OPERATOR` display level
+ polling_period :obj:`int` -1 polling period
+ memorized :obj:`bool` False attribute should or not be memorized
+ hw_memorized :obj:`bool` False write method should be called at startup when restoring memorize value (dangerous!)
+ access :obj:`~PyTango.AttrWriteType` :obj:`~PyTango.AttrWriteType.READ` read only/ read write / write only access
+ fget (or fread) :obj:`str` or :obj:`callable` 'read_<attr_name>' read method name or method object
+ fset (or fwrite) :obj:`str` or :obj:`callable` 'write_<attr_name>' write method name or method object
+ is_allowed :obj:`str` or :obj:`callable` 'is_<attr_name>_allowed' is allowed method name or method object
+ label :obj:`str` '<attr_name>' attribute label
+ doc (or description) :obj:`str` '' attribute description
+ unit :obj:`str` '' physical units the attribute value is in
+ standard_unit :obj:`str` '' physical standard unit
+ display_unit :obj:`str` '' physical display unit (hint for clients)
+ format :obj:`str` '6.2f' attribute representation format
+ min_value :obj:`str` None minimum allowed value
+ max_value :obj:`str` None maximum allowed value
+ min_alarm :obj:`str` None minimum value to trigger attribute alarm
+ max_alarm :obj:`str` None maximum value to trigger attribute alarm
+ min_warning :obj:`str` None minimum value to trigger attribute warning
+ max_warning :obj:`str` None maximum value to trigger attribute warning
+ delta_val :obj:`str` None
+ delta_t :obj:`str` None
+ abs_change :obj:`str` None minimum value change between events that causes event filter to send the event
+ rel_change :obj:`str` None minimum relative change between events that causes event filter to send the event (%)
+ period :obj:`str` None
+ archive_abs_change :obj:`str` None
+ archive_rel_change :obj:`str` None
+ archive_period :obj:`str` None
+ ===================== ================================ ======================================= =======================================================================================
+
+ .. note::
+ avoid using *dformat* parameter. If you need a SPECTRUM attribute of say,
+ boolean type, use instead ``dtype=(bool,)``.
+
+ Example of a integer writable attribute with a customized label, unit and
+ description::
+
+ class PowerSupply(Device):
+ __metaclass__ = DeviceMeta
+
+ current = attribute(label="Current", unit="mA", dtype=int,
+ access=AttrWriteType.READ_WRITE,
+ doc="the power supply current")
+
+ def init_device(self):
+ Device.init_device(self)
+ self._current = -1
+
+ def read_current(self):
+ return self._current
+
+ def write_current(self, current):
+ self._current = current
+
+ The same, but using attribute as a decorator::
+
+ class PowerSupply(Device):
+ __metaclass__ = DeviceMeta
+
+ def init_device(self):
+ Device.init_device(self)
+ self._current = -1
+
+ @attribute(label="Current", unit="mA", dtype=int)
+ def current(self):
+ """the power supply current"""
+ return 999.999
+
+ @current.write
+ def current(self, current):
+ self._current = current
+
+ In this second format, defining the `write` implies setting the attribute
+ access to READ_WRITE.
+ '''
+
+ def __init__(self, fget=None, **kwargs):
+ self._kwargs = dict(kwargs)
name = kwargs.pop("name", None)
class_name = kwargs.pop("class_name", None)
+
+ if fget:
+ if inspect.isroutine(fget):
+ self.fget = fget
+ if 'doc' not in kwargs and 'description' not in kwargs:
+ kwargs['doc'] = fget.__doc__
+ else:
+ kwargs['fget'] = fget
+
super(attribute, self).__init__(name, class_name)
if 'dtype' in kwargs:
kwargs['dtype'], kwargs['dformat'] = \
get_tango_type_format(kwargs['dtype'], kwargs.get('dformat'))
self.build_from_dict(kwargs)
-
+
def get_attribute(self, obj):
return obj.get_device_attr().get_attr_by_name(self.attr_name)
# --------------------
# descriptor interface
# --------------------
-
+
def __get__(self, obj, objtype):
return self.get_attribute(obj)
def __set__(self, obj, value):
attr = self.get_attribute(obj)
set_complex_value(attr, value)
-
+
def __delete__(self, obj):
obj.remove_attribute(self.attr_name)
-
-def _attribute(**kwargs):
- """declares a new tango attribute in a :class:`Device`. To be used like
-the python native :obj:`property` function. For example, to declare a
-scalar, `PyTango.DevDouble`, read-only attribute called *voltage* in a
-*PowerSupply* :class:`Device` do::
+ def setter(self, fset):
+ """To be used as a decorator. Will define the decorated method as a
+ write attribute method to be called when client writes the attribute"""
+ self.fset = fset
+ if self.attr_write == AttrWriteType.READ:
+ if getattr(self, 'fget', None):
+ self.attr_write = AttrWriteType.READ_WRITE
+ else:
+ self.attr_write = AttrWriteType.WRITE
+ return self
- class PowerSupply(Device):
-
- voltage = attribute()
-
- def read_voltage(self):
- self.voltage = 1.0
-
-It receives multiple keyword arguments.
-
-===================== ================================ ======================================= =======================================================================================
-parameter type default value description
-===================== ================================ ======================================= =======================================================================================
-name :obj:`str` class member name alternative attribute name
-dtype :obj:`object` :obj:`~PyTango.CmdArgType.DevDouble` data type (see :ref:`Data type equivalence <pytango-hlapi-datatypes>`)
-dformat :obj:`~PyTango.AttrDataFormat` :obj:`~PyTango.AttrDataFormat.SCALAR` data format
-max_dim_x :obj:`int` 1 maximum size for x dimension (ignored for SCALAR format)
-max_dim_y :obj:`int` 0 maximum size for y dimension (ignored for SCALAR and SPECTRUM formats)
-display_level :obj:`~PyTango.DispLevel` :obj:`~PyTango.DisLevel.OPERATOR` display level
-polling_period :obj:`int` -1 polling period
-memorized :obj:`bool` False attribute should or not be memorized
-hw_memorized :obj:`bool` False write method should be called at startup when restoring memorize value (dangerous!)
-access :obj:`~PyTango.AttrWriteType` :obj:`~PyTango.AttrWriteType.READ` read only/ read write / write only access
-fget (or fread) :obj:`str` or :obj:`callable` 'read_<attr_name>' read method name or method object
-fset (or fwrite) :obj:`str` or :obj:`callable` 'write_<attr_name>' write method name or method object
-is_allowed :obj:`str` or :obj:`callable` 'is_<attr_name>_allowed' is allowed method name or method object
-label :obj:`str` '<attr_name>' attribute label
-doc (or description) :obj:`str` '' attribute description
-unit :obj:`str` '' physical units the attribute value is in
-standard_unit :obj:`str` '' physical standard unit
-display_unit :obj:`str` '' physical display unit (hint for clients)
-format :obj:`str` '6.2f' attribute representation format
-min_value :obj:`str` None minimum allowed value
-max_value :obj:`str` None maximum allowed value
-min_alarm :obj:`str` None minimum value to trigger attribute alarm
-max_alarm :obj:`str` None maximum value to trigger attribute alarm
-min_warning :obj:`str` None minimum value to trigger attribute warning
-max_warning :obj:`str` None maximum value to trigger attribute warning
-delta_val :obj:`str` None
-delta_t :obj:`str` None
-abs_change :obj:`str` None minimum value change between events that causes event filter to send the event
-rel_change :obj:`str` None minimum relative change between events that causes event filter to send the event (%)
-period :obj:`str` None
-archive_abs_change :obj:`str` None
-archive_rel_change :obj:`str` None
-archive_period :obj:`str` None
-===================== ================================ ======================================= ======================================================================================="""
- if 'dtype' in kwargs:
- kwargs['dtype'], kwargs['dformat'] = \
- get_tango_type_format(kwargs['dtype'], kwargs.get('dformat'))
- return attribute.from_dict(kwargs)
+ def write(self, fset):
+ """To be used as a decorator. Will define the decorated method as a
+ write attribute method to be called when client writes the attribute"""
+ return self.setter(fset)
+
+ def __call__(self, fget):
+ return type(self)(fget=fget, **self._kwargs)
def command(f=None, dtype_in=None, dformat_in=None, doc_in="",
dtype_out=None, dformat_out=None, doc_out="",):
- """declares a new tango command in a :class:`Device`.
+ """
+ Declares a new tango command in a :class:`Device`.
To be used like a decorator in the methods you want to declare as tango
- commands. For example, to declare a *ramp* command that receives a
- `PyTango.DevDouble` parameter called *current*, do::
+ commands. The following example declares commands:
+
+ * `void TurnOn(void)`
+ * `void Ramp(DevDouble current)`
+ * `DevBool Pressurize(DevDouble pressure)`
+
+ ::
class PowerSupply(Device):
+ __metaclass__ = DeviceMeta
+ @command
+ def TurnOn(self):
+ self.info_stream("Turning on the power supply")
+
@command(dtype_in=float)
- def ramp(self, current):
+ def Ramp(self, current):
self.info_stream("Ramping on %f..." % current)
- # Another more elaborate command
-
@command(dtype_in=float, doc_in="the pressure to be set",
- dtype_out=(bool, doc_out="True if it worked, False otherwise")
- def setPressure(self, pressure):
- self.info_stream("Setting pressure on %f..." % pressure)
-
+ dtype_out=bool, doc_out="True if it worked, False otherwise")
+ def Pressurize(self, pressure):
+ self.info_stream("Pressurizing to %f..." % pressure)
+
+ .. note::
+ avoid using *dformat* parameter. If you need a SPECTRUM attribute of
+ say, boolean type, use instead ``dtype=(bool,)``.
+
:param dtype_in: a :ref:`data type <pytango-hlapi-datatypes>`
describing the type of parameter. Default is None meaning
no parameter.
@@ -650,7 +575,7 @@ def command(f=None, dtype_in=None, dformat_in=None, doc_in="",
dtype_in=dtype_in, dformat_in=dformat_in, doc_in=doc_in,
dtype_out=dtype_out, dformat_out=dformat_out, doc_out=doc_out)
name = f.__name__
-
+
dtype_in, dformat_in = get_tango_type_format(dtype_in, dformat_in)
dtype_out, dformat_out = get_tango_type_format(dtype_out, dformat_out)
@@ -668,35 +593,98 @@ class _property(object):
self.dtype = dtype
self.doc = doc
self.default_value = default_value
-
+
def __get__(self, obj, objtype):
return self.__value
def __set__(self, obj, value):
self.__value = value
-
+
def __delete__(self, obj):
del self.__value
class device_property(_property):
- pass
+ """
+ Declares a new tango device property in a :class:`Device`. To be used like
+ the python native :obj:`property` function. For example, to declare a
+ scalar, `PyTango.DevString`, device property called *host* in a
+ *PowerSupply* :class:`Device` do::
+ from PyTango.server import Device, DeviceMeta
+ from PyTango.server import device_property
+
+ class PowerSupply(Device):
+ __metaclass__ = DeviceMeta
+
+ host = device_property(dtype=str)
+
+ :param dtype: Data type (see :ref:`pytango-data-types`)
+ :param doc: property documentation (optional)
+ :param default_value: default value for the property (optional)
+ """
+ pass
class class_property(_property):
+ """
+ Declares a new tango class property in a :class:`Device`. To be used like
+ the python native :obj:`property` function. For example, to declare a
+ scalar, `PyTango.DevString`, class property called *port* in a *PowerSupply*
+ :class:`Device` do::
+
+ from PyTango.server import Device, DeviceMeta
+ from PyTango.server import class_property
+
+ class PowerSupply(Device):
+ __metaclass__ = DeviceMeta
+
+ port = class_property(dtype=int, default_value=9788)
+
+ :param dtype: Data type (see :ref:`pytango-data-types`)
+ :param doc: property documentation (optional)
+ :param default_value: default value for the property (optional)
+ """
pass
+def __to_cb(post_init_callback):
+ if post_init_callback is None:
+ return lambda : None
+
+ err_msg = "post_init_callback must be a callable or " \
+ "sequence <callable [, args, [, kwargs]]>"
+ if operator.isCallable(post_init_callback):
+ return post_init_callback
+ elif is_non_str_seq(post_init_callback):
+ length = len(post_init_callback)
+ if length < 1 or length > 3:
+ raise TypeError(err_msg)
+ cb = post_init_callback[0]
+ if not operator.isCallable(cb):
+ raise TypeError(err_msg)
+ args, kwargs = [], {}
+ if length > 1:
+ args = post_init_callback[1]
+ if length > 2:
+ kwargs = post_init_callback[2]
+ return functools.partial(cb, *args, **kwargs)
+
+ raise TypeError(err_msg)
+
+
def __server_run(classes, args=None, msg_stream=sys.stdout, util=None,
- event_loop=None):
+ event_loop=None, post_init_callback=None):
import PyTango
if msg_stream is None:
- import io
- msg_stream = io.BytesIO()
-
+ write = lambda msg: None
+ else:
+ write = msg_stream.write
+
if args is None:
args = sys.argv
+ post_init_callback = __to_cb(post_init_callback)
+
if util is None:
util = PyTango.Util(args)
@@ -719,91 +707,110 @@ def __server_run(classes, args=None, msg_stream=sys.stdout, util=None,
klass_name = klass_info._DeviceClassName
klass = klass_info
util.add_class(klass_klass, klass, klass_name)
+
u_instance = PyTango.Util.instance()
if event_loop is not None:
u_instance.server_set_event_loop(event_loop)
u_instance.server_init()
- msg_stream.write("Ready to accept request\n")
+ post_init_callback()
+ write("Ready to accept request\n")
u_instance.server_run()
return util
-def server_run(classes, args=None, msg_stream=sys.stdout,
- verbose=False, util=None, event_loop=None):
- """Provides a simple way to run a tango server. It handles exceptions
- by writting a message to the msg_stream.
+def run(classes, args=None, msg_stream=sys.stdout,
+ verbose=False, util=None, event_loop=None,
+ post_init_callback=None):
+ """
+ Provides a simple way to run a tango server. It handles exceptions
+ by writting a message to the msg_stream.
- The `classes` parameter can be either a sequence of :class:`~PyTango.server.Device`
- classes or a dictionary where:
-
- * key is the tango class name
- * value is either:
- #. a :class:`~PyTango.server.Device` class or
- #. a a sequence of two elements :class:`~PyTango.DeviceClass`, :class:`~PyTango.DeviceImpl`
+ The `classes` parameter can be either a sequence of :class:`~PyTango.server.Device`
+ classes or a dictionary where:
+
+ * key is the tango class name
+ * value is either:
+ * a :class:`~PyTango.server.Device` class or
+ * a sequence of two elements :class:`~PyTango.DeviceClass` , :class:`~PyTango.DeviceImpl`
+
+ The optional `post_init_callback` can be a callable (without arguments)
+ or a tuple where the first element is the callable, the second is a list
+ of arguments(optional) and the third is a dictionary of keyword arguments
+ (also optional).
- Example 1: registering and running a PowerSupply inheriting from :class:`~PyTango.server.Device`::
+ Example 1: registering and running a PowerSupply inheriting from :class:`~PyTango.server.Device`::
- from PyTango import server_run
- from PyTango.server import Device, DeviceMeta
+ from PyTango.server import Device, DeviceMeta, run
- class PowerSupply(Device):
- __metaclass__ = DeviceMeta
+ class PowerSupply(Device):
+ __metaclass__ = DeviceMeta
- server_run((PowerSupply,))
+ run((PowerSupply,))
- Example 2: registering and running a MyServer defined by tango classes
- `MyServerClass` and `MyServer`::
+ Example 2: registering and running a MyServer defined by tango classes
+ `MyServerClass` and `MyServer`::
- import PyTango
-
- class MyServer(PyTango.Device_4Impl):
- pass
+ import PyTango
+ from PyTango.server import run
+
+ class MyServer(PyTango.Device_4Impl):
+ pass
- class MyServerClass(PyTango.DeviceClass):
- pass
+ class MyServerClass(PyTango.DeviceClass):
+ pass
- PyTango.server_run({"MyServer": (MyServerClass, MyServer)})
+ run({"MyServer": (MyServerClass, MyServer)})
- :param classes:
- a sequence of :class:`~PyTango.server.Device` classes or
- a dictionary where keyword is the tango class name and value is a
- sequence of Tango Device Class python class, and Tango Device python class
- :type classes: sequence or dict
+ :param classes:
+ a sequence of :class:`~PyTango.server.Device` classes or
+ a dictionary where keyword is the tango class name and value is a
+ sequence of Tango Device Class python class, and Tango Device python class
+ :type classes: sequence or dict
- :param args:
- list of command line arguments [default: None, meaning use sys.argv]
- :type args: list
+ :param args:
+ list of command line arguments [default: None, meaning use sys.argv]
+ :type args: list
- :param msg_stream:
- stream where to put messages [default: sys.stdout]
+ :param msg_stream:
+ stream where to put messages [default: sys.stdout]
- :param util:
- PyTango Util object [default: None meaning create a Util instance]
- :type util: :class:`~PyTango.Util`
+ :param util:
+ PyTango Util object [default: None meaning create a Util instance]
+ :type util: :class:`~PyTango.Util`
- :param event_loop: event_loop callable
- :type event_loop: callable
+ :param event_loop: event_loop callable
+ :type event_loop: callable
- :return: The Util singleton object
- :rtype: :class:`~PyTango.Util`
+ :param post_init_callback:
+ an optional callback that is executed between the calls Util.server_init
+ and Util.server_run
+ :type post_init_callback: callable or tuple (see description above)
+
+ :return: The Util singleton object
+ :rtype: :class:`~PyTango.Util`
- .. versionadded:: 8.0.0
+ .. versionadded:: 8.0.0
- .. versionchanged:: 8.0.3
- Added `util` keyword parameter.
- Returns util object
+ .. versionchanged:: 8.0.3
+ Added `util` keyword parameter.
+ Returns util object
- .. versionchanged:: 8.1.1
- Changed default msg_stream from *stderr* to *stdout*
- Added `event_loop` keyword parameter.
- Returns util object"""
+ .. versionchanged:: 8.1.1
+ Changed default msg_stream from *stderr* to *stdout*
+ Added `event_loop` keyword parameter.
+ Returns util object
+ .. versionchanged:: 8.1.2
+ Added `post_init_callback` keyword parameter
+ """
if msg_stream is None:
- import io
- msg_stream = io.BytesIO()
- write = msg_stream.write
+ write = lambda msg : None
+ else:
+ write = msg_stream.write
try:
- return __server_run(classes, args=args, util=util, event_loop=event_loop)
+ return __server_run(classes, args=args, msg_stream=msg_stream,
+ util=util, event_loop=event_loop,
+ post_init_callback=post_init_callback)
except KeyboardInterrupt:
write("Exiting: Keyboard interrupt\n")
except DevFailed as df:
@@ -815,3 +822,15 @@ def server_run(classes, args=None, msg_stream=sys.stdout,
if verbose:
write(traceback.format_exc())
write("\nExited\n")
+
+def server_run(classes, args=None, msg_stream=sys.stdout,
+ verbose=False, util=None, event_loop=None,
+ post_init_callback=None):
+ """
+ Just an alias to :func:`~PyTango.server.run`.
+ Use :func:`~PyTango.server.run` instead.
+ """
+ return run(classes, args=args, msg_stream=msg_stream,
+ verbose=verbose, util=util, event_loop=event_loop,
+ post_init_callback=post_init_callback)
+
diff --git a/src/boost/python/utils.py b/src/boost/python/utils.py
index ca72698..b9e53dc 100644
--- a/src/boost/python/utils.py
+++ b/src/boost/python/utils.py
@@ -16,28 +16,31 @@ This is an internal PyTango module.
from __future__ import with_statement
from __future__ import print_function
-__all__ = [ "is_pure_str", "is_seq", "is_non_str_seq", "is_integer",
- "is_number", "is_scalar_type", "is_array_type", "is_numerical_type",
- "is_int_type", "is_float_type", "is_bool_type", "is_bin_type",
- "is_str_type", "obj_2_str", "seqStr_2_obj",
- "scalar_to_array_type",
- "document_method", "document_static_method", "document_enum",
- "CaselessList", "CaselessDict", "EventCallBack", "get_home",
- "from_version_str_to_hex_str", "from_version_str_to_int",
- "seq_2_StdStringVector", "StdStringVector_2_seq" ]
+__all__ = ["is_pure_str", "is_seq", "is_non_str_seq", "is_integer",
+ "is_number", "is_scalar_type", "is_array_type", "is_numerical_type",
+ "is_int_type", "is_float_type", "is_bool_type", "is_bin_type",
+ "is_str_type", "obj_2_str", "seqStr_2_obj",
+ "scalar_to_array_type",
+ "document_method", "document_static_method", "document_enum",
+ "CaselessList", "CaselessDict", "callable_weakref",
+ "EventCallBack", "get_home",
+ "from_version_str_to_hex_str", "from_version_str_to_int",
+ "seq_2_StdStringVector", "StdStringVector_2_seq"]
__docformat__ = "restructuredtext"
import os
import sys
import numbers
+import inspect
+import weakref
import collections
from ._PyTango import StdStringVector, StdDoubleVector, \
DbData, DbDevInfos, DbDevExportInfos, CmdArgType, AttrDataFormat, \
EventData, AttrConfEventData, DataReadyEventData, DevFailed, constants, \
GreenMode
-
+from .constants import AlrmValueNotSpec, StatusNotSet
_scalar_int_types = (CmdArgType.DevShort, CmdArgType.DevUShort,
CmdArgType.DevInt, CmdArgType.DevLong, CmdArgType.DevULong,
@@ -88,8 +91,12 @@ _scalar_to_array_type = {
CmdArgType.ConstDevString : CmdArgType.DevVarStringArray,
}
+__NO_STR_VALUE = AlrmValueNotSpec, StatusNotSet
+
__device_classes = None
+bool_ = lambda value_str : value_str.lower() == "true"
+
def get_tango_device_classes():
global __device_classes
@@ -564,7 +571,33 @@ def scalar_to_array_type(dtype):
return _scalar_to_array_type[dtype]
-def obj_2_str(obj, tg_type):
+def str_2_obj(obj_str, tg_type=None):
+ """Converts a string into an object according to the given tango type
+
+ :param obj_str: the string to be converted
+ :type obj_str: :py:obj:`str`
+ :param tg_type: tango type
+ :type tg_type: :class:`PyTango.CmdArgType`
+ :return: an object calculated from the given string
+ :rtype: :py:obj:`object`
+ """
+ if tg_type is None:
+ return obj_str
+ f = str
+ if is_scalar_type(tg_type):
+ if is_numerical_type(tg_type):
+ if obj_str in __NO_STR_VALUE:
+ return None
+ if is_int_type(tg_type):
+ f = int
+ elif is_float_type(tg_type):
+ f = float
+ elif is_bool_type(tg_type):
+ f = bool_
+ return f(obj_str)
+
+
+def obj_2_str(obj, tg_type=None):
"""Converts a python object into a string according to the given tango type
:param obj: the object to be converted
@@ -574,6 +607,8 @@ def obj_2_str(obj, tg_type):
:return: a string representation of the given object
:rtype: :py:obj:`str`
"""
+ if tg_type is None:
+ return obj
if tg_type in _scalar_types:
# scalar cases
if is_pure_str(obj):
@@ -586,6 +621,7 @@ def obj_2_str(obj, tg_type):
# sequence cases
return '\n'.join([str(i) for i in obj])
+
def __get_meth_func(klass, method_name):
meth = getattr(klass, method_name)
func = meth
@@ -883,6 +919,65 @@ class CaselessDict(dict):
def keys(self):
return CaselessList(dict.keys(self))
+
+class _MethodWeakref(object):
+ """This class represents a weak reference to a method of an object since
+ weak references to methods don't work by themselves"""
+
+ def __init__(self, method, del_cb=None):
+ cb = del_cb and self.__on_deleted
+ self.__func = weakref.ref(method.im_func, cb)
+ self.__obj = weakref.ref(method.im_self, cb)
+ if cb:
+ self.__del_cb = callable_weakref(del_cb)
+ self.__deleted = 0
+
+ def __on_deleted(self, obj):
+ if not self.__deleted:
+ del_cb = self.__del_cb()
+ if del_cb is not None:
+ del_cb(self)
+ self.__deleted = 1
+
+ def __call__(self):
+ obj = self.__obj()
+ if obj is not None:
+ func = self.__func()
+ if func is not None:
+ return func.__get__(obj)
+
+ def __hash__(self):
+ return id(self)
+
+ def __cmp__(self, other):
+ if other.__class__ == self.__class__:
+ ret = cmp((self.__func, self.__obj),
+ (other.__func, other.__obj))
+ return ret
+ return 1
+
+ def __repr__(self):
+ return '_MethodWeakRef()'
+ #obj, f_name = self.__obj(), self.__func().__name__
+ #return '_MethodWeakRef(obj={0}, func={1})'.format % (obj, f_name)
+
+
+def callable_weakref(obj, del_cb=None):
+ """This function returns a callable weak reference to a callable object.
+ Object can be a callable object, a function or a method.
+
+ :param object: a callable object
+ :type object: callable object
+ :param del_cb: calback function. Default is None meaning to callback.
+ :type del_cb: callable object or None
+
+ :return: a weak reference for the given callable
+ :rtype: BoundMethodWeakref or weakref.ref"""
+ if inspect.ismethod(obj):
+ return _MethodWeakref(obj, del_cb)
+ return weakref.ref(obj, del_cb)
+
+
__DEFAULT_FACT_IOR_FILE = "/tmp/rdifact.ior"
__BASE_LINE = "notifd"
__END_NOTIFD_LINE = "/DEVICE/notifd:"
@@ -959,7 +1054,6 @@ def _notifd2db_real_db(ior_string, host=None, out=sys.stdout):
print("Failed to export notification service event factory " \
"to TANGO database", file=out)
-
class EventCallBack(object):
"""
Useful event callback for test purposes
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/pytango.git
More information about the debian-science-commits
mailing list