Skip to content
Snippets Groups Projects
Commit e0ede97d authored by Jan David Mol's avatar Jan David Mol
Browse files

Task #8888: Merged trunk into branch

parents 28795d9e 92c24182
No related branches found
No related tags found
No related merge requests found
Showing
with 672 additions and 118 deletions
......@@ -2648,6 +2648,9 @@ LCS/PyCommon/CMakeLists.txt -text
LCS/PyCommon/__init__.py -text
LCS/PyCommon/datetimeutils.py -text
LCS/PyCommon/factory.py -text
LCS/PyCommon/test/python-coverage.sh eol=lf
LCS/PyCommon/test/t_dbcredentials.run eol=lf
LCS/PyCommon/test/t_dbcredentials.sh eol=lf
LCS/PyCommon/util.py -text
LCS/Tools/src/checkcomp.py -text
LCS/Tools/src/countalllines -text
......@@ -3426,6 +3429,9 @@ MAC/Deployment/data/PVSS/License/HwCode.mcu001 -text
MAC/Deployment/data/PVSS/License/License[!!-~]administration.xlsx -text
MAC/Deployment/data/PVSS/License/MCU001_option.old.txt -text
MAC/Deployment/data/PVSS/License/MCU099_option.txt -text
MAC/Deployment/data/PVSS/License/PL610C_option.txt -text
MAC/Deployment/data/PVSS/License/PL611C_option.txt -text
MAC/Deployment/data/PVSS/License/PL612C_option.txt -text
MAC/Deployment/data/PVSS/License/PVSStoolLicense.exe -text
MAC/Deployment/data/PVSS/License/PVSStoolLicense_3_7.exe -text
MAC/Deployment/data/PVSS/License/RS106C_option.txt -text
......@@ -3486,6 +3492,9 @@ MAC/Deployment/data/PVSS/License/shield.DE609C.txt -text
MAC/Deployment/data/PVSS/License/shield.FR606C.txt -text
MAC/Deployment/data/PVSS/License/shield.FR606C_lcu101.txt -text
MAC/Deployment/data/PVSS/License/shield.MCU099.txt -text
MAC/Deployment/data/PVSS/License/shield.PL610C.txt -text
MAC/Deployment/data/PVSS/License/shield.PL611C.txt -text
MAC/Deployment/data/PVSS/License/shield.PL612C.txt -text
MAC/Deployment/data/PVSS/License/shield.RS106C.txt -text
MAC/Deployment/data/PVSS/License/shield.RS205C.txt -text
MAC/Deployment/data/PVSS/License/shield.RS208C.txt -text
......
......@@ -41,7 +41,7 @@ logger = logging.getLogger(__name__)
# Default settings for often used parameters.
DEFAULT_ADDRESS_OPTIONS = {'create': 'never'}
DEFAULT_BROKER = "localhost:5672"
DEFAULT_BROKER_OPTIONS = {}
DEFAULT_BROKER_OPTIONS = {'reconnect': True}
DEFAULT_RECEIVER_CAPACITY = 1
DEFAULT_TIMEOUT = 5
......@@ -62,19 +62,20 @@ class FromBus(object):
but that of __new__().
"""
def __init__(self, address, options=None, broker=None):
def __init__(self, address, options=None, broker=None, broker_options=None):
"""
Initializer.
:param address: valid Qpid address
:param options: valid Qpid address options, e.g. {'create': 'never'}
:param broker: valid Qpid broker URL, e.g. "localhost:5672"
:param broker_options: valid Qpid broker options, e.g. {'reconnect': True}
"""
self.address = address
self.options = options if options else DEFAULT_ADDRESS_OPTIONS
self.broker = broker if broker else DEFAULT_BROKER
self.broker_options = broker_options if broker_options else DEFAULT_BROKER_OPTIONS
self.connection = qpid.messaging.Connection(self.broker,
**DEFAULT_BROKER_OPTIONS)
self.connection = qpid.messaging.Connection(self.broker, **self.broker_options)
self.session = None
self.opened=0
......@@ -253,19 +254,20 @@ class ToBus(object):
but that of __new__().
"""
def __init__(self, address, options=None, broker=None):
def __init__(self, address, options=None, broker=None, broker_options=None):
"""
Initializer.
:param address: valid Qpid address
:param options: valid Qpid address options, e.g. {'create': 'never'}
:param broker: valid Qpid broker URL, e.g. "localhost:5672"
:param broker_options: valid Qpid broker options, e.g. {'reconnect': True}
"""
self.address = address
self.options = options if options else DEFAULT_ADDRESS_OPTIONS
self.broker = broker if broker else DEFAULT_BROKER
self.broker_options = broker_options if broker_options else DEFAULT_BROKER_OPTIONS
self.connection = qpid.messaging.Connection(self.broker,
**DEFAULT_BROKER_OPTIONS)
self.connection = qpid.messaging.Connection(self.broker, **self.broker_options)
self.session = None
self.opened = 0
......
......@@ -10,29 +10,5 @@ queue=$(< /dev/urandom tr -dc [:alnum:] | head -c16)
qpid-config add exchange topic $queue
# Run the unit test
# either with or without code coverage measurements,
# depending wheter coverage has been installed
if type "coverage" > /dev/null; then
#run test using python coverage tool
#erase previous results
coverage erase
#setup coverage config file
printf "[report]\nexclude_lines = \n if __name__ == .__main__.\n def main\n" > .coveragerc
coverage run --branch --include=*Messaging/python* t_RPC.py $queue
RESULT=$?
if [ $RESULT -eq 0 ]; then
echo " *** Code coverage results *** "
coverage report -m
echo " *** End coverage results *** "
fi
exit $RESULT
else
#coverage not available
echo "Please run: 'pip install coverage' to enable code coverage reporting of the unit tests"
#run plain test script
python t_RPC.py $queue
fi
source python-coverage.sh
python_coverage_test "Messaging/python" t_RPC.py $queue
......@@ -53,7 +53,7 @@ class FromBusInitFailed(unittest.TestCase):
regexp = re.escape(self.error)
regexp += '.*' + 'No address associated with hostname'
with self.assertRaisesRegexp(MessageBusError, regexp):
with FromBus(QUEUE, broker="foo.bar"):
with FromBus(QUEUE, broker="foo.bar", broker_options={'reconnect': False}):
pass
def test_connection_refused(self):
......@@ -62,7 +62,7 @@ class FromBusInitFailed(unittest.TestCase):
"""
regexp = re.escape(self.error) + '.*' + 'Connection refused'
with self.assertRaisesRegexp(MessageBusError, regexp):
with FromBus("fake" + QUEUE, broker="localhost:4"):
with FromBus("fake" + QUEUE, broker="localhost:4", broker_options={'reconnect': False}):
pass
......@@ -164,7 +164,7 @@ class ToBusInitFailed(unittest.TestCase):
regexp = re.escape(self.error)
regexp += '.*' + 'No address associated with hostname'
with self.assertRaisesRegexp(MessageBusError, regexp):
with ToBus(QUEUE, broker="foo.bar"):
with ToBus(QUEUE, broker="foo.bar", broker_options={'reconnect': False}):
pass
def test_connection_refused(self):
......@@ -173,7 +173,7 @@ class ToBusInitFailed(unittest.TestCase):
"""
regexp = re.escape(self.error) + '.*' + 'Connection refused'
with self.assertRaisesRegexp(MessageBusError, regexp):
with ToBus(QUEUE, broker="localhost:4"):
with ToBus(QUEUE, broker="localhost:4", broker_options={'reconnect': False}):
pass
......
......@@ -10,29 +10,5 @@ queue=$(< /dev/urandom tr -dc [:alnum:] | head -c16)
qpid-config add queue $queue
# Run the unit test
# either with or without code coverage measurements,
# depending wheter coverage has been installed
if type "coverage" > /dev/null; then
#run test using python coverage tool
#erase previous results
coverage erase
#setup coverage config file
printf "[report]\nexclude_lines = \n if __name__ == .__main__.\n def main\n" > .coveragerc
coverage run --branch --include=*Messaging/python* t_messagebus.py $queue
RESULT=$?
if [ $RESULT -eq 0 ]; then
echo " *** Code coverage results *** "
coverage report -m
echo " *** End coverage results *** "
fi
exit $RESULT
else
#coverage not available
echo "Please run: 'pip install coverage' to enable code coverage reporting of the unit tests"
#run plain test script
python t_messagebus.py $queue
fi
source python-coverage.sh
python_coverage_test "Messaging/python" t_messagebus.py $queue
#!/bin/bash -e
# Run the unit test
# either with or without code coverage measurements,
# depending wheter coverage has been installed
if type "coverage" > /dev/null; then
#run test using python coverage tool
#erase previous results
coverage erase
#setup coverage config file
printf "[report]\nexclude_lines = \n if __name__ == .__main__.\n def main\n" > .coveragerc
coverage run --branch --include=*Messaging/python* t_messages.py
RESULT=$?
if [ $RESULT -eq 0 ]; then
echo " *** Code coverage results *** "
coverage report -m
echo " *** End coverage results *** "
fi
exit $RESULT
else
#coverage not available
echo "Please run: 'pip install coverage' to enable code coverage reporting of the unit tests"
#run plain test script
python t_messages.py
fi
source python-coverage.sh
python_coverage_test "Messaging/python" t_messages.py
......@@ -10,29 +10,5 @@ queue=$(< /dev/urandom tr -dc [:alnum:] | head -c16)
qpid-config add exchange topic $queue
# Run the unit test
# either with or without code coverage measurements,
# depending wheter coverage has been installed
if type "coverage" > /dev/null; then
#run test using python coverage tool
#erase previous results
coverage erase
#setup coverage config file
printf "[report]\nexclude_lines = \n if __name__ == .__main__.\n def main\n" > .coveragerc
coverage run --branch --include=*Messaging/python* t_service_message_handler.py $queue
RESULT=$?
if [ $RESULT -eq 0 ]; then
echo " *** Code coverage results *** "
coverage report -m
echo " *** End coverage results *** "
fi
exit $RESULT
else
#coverage not available
echo "Please run: 'pip install coverage' to enable code coverage reporting of the unit tests"
#run plain test script
python t_service_message_handler.py $queue
fi
source python-coverage.sh
python_coverage_test "Messaging/python" t_service_message_handler.py $queue
......@@ -5,9 +5,10 @@ lofar_package(PyCommon 1.0)
lofar_find_package(Python 2.6 REQUIRED)
include(PythonInstall)
#add_subdirectory(test)
add_subdirectory(test)
set(_py_files
dbcredentials.py
factory.py
util.py
datetimeutils.py)
......
#!/usr/bin/python
# Copyright (C) 2012-2015 ASTRON (Netherlands Institute for Radio Astronomy)
# P.O. Box 2, 7990 AA Dwingeloo, The Netherlands
#
# This file is part of the LOFAR software suite.
# The LOFAR software suite is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# The LOFAR software suite is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with the LOFAR software suite. If not, see <http://www.gnu.org/licenses/>.
# $Id$
from glob import glob
import os
import pwd
from ConfigParser import SafeConfigParser, NoSectionError, DuplicateSectionError
from optparse import OptionGroup
__all__ = ["Credentials", "DBCredentials", "options_group", "parse_options"]
# obtain the environment, and add USER and HOME if needed (since supervisord does not)
environ = os.environ
user_info = pwd.getpwuid(os.getuid())
environ.setdefault("HOME", user_info.pw_dir)
environ.setdefault("USER", user_info.pw_name)
def findfiles(pattern):
""" Returns a list of files matched by `pattern'.
The pattern can include environment variables using the
{VAR} notation.
"""
try:
return glob(pattern.format(**environ))
except KeyError:
return []
class Credentials:
def __init__(self):
# Flavour of database (postgres, mysql, oracle, sqlite)
self.type = "postgres"
# Connection information (port 0 = use default)
self.host = "localhost"
self.port = 0
# Authentication
self.user = environ["USER"]
self.password = ""
# Database selection
self.database = ""
def __str__(self):
return "type={type} addr={host}:{port} auth={user}:{password} db={database}".format(**self.__dict__)
def pg_connect_options(self):
"""
Returns a dict of options to provide to PyGreSQL's pg.connect function. Use:
conn = pg.connect(**dbcreds.pg_connect_options())
"""
return {
"host": self.host,
"port": self.port or -1,
"user": self.user,
"passwd": self.password,
"dbname": self.database,
}
class DBCredentials:
def __init__(self, filepatterns=None):
"""
Read database credentials from all configuration files matched by any of the patterns.
By default, the following files are read:
$LOFARROOT/etc/dbcredentials/*.ini
~/.lofar/dbcredentials/*.ini
The configuration files allow for any number of database sections:
[database:OTDB]
type = postgres # postgres, mysql, oracle, sqlite
host = localhost
port = 0 # 0 = use default port
user = paulus
password = boskabouter
database = LOFAR_4
These database credentials can subsequently be queried under their
symbolic name ("OTDB" in the example).
"""
if filepatterns is None:
filepatterns = [
"{LOFARROOT}/etc/dbcredentials/*.ini",
"{HOME}/.lofar/dbcredentials/*.ini",
]
self.config = SafeConfigParser()
self.files = sum([findfiles(p) for p in filepatterns],[])
self.config.read(self.files)
def get(self, database):
"""
Return credentials for a given database.
"""
# create default credentials
creds = Credentials()
# read configuration
try:
d = dict(self.config.items(self._section(database)))
except NoSectionError:
return creds
# parse and convert config information
if "host" in d: creds.host = d["host"]
if "port" in d: creds.port = int(d["port"] or 0)
if "user" in d: creds.user = d["user"]
if "password" in d: creds.password = d["password"]
if "database" in d: creds.database = d["database"]
return creds
def set(self, database, credentials):
"""
Add or overwrite credentials for a given database.
"""
section = self._section(database)
# create section if needed
try:
self.config.add_section(section)
except DuplicateSectionError:
pass
# set or override credentials
self.config.set(section, "type", credentials.type)
self.config.set(section, "host", credentials.host)
self.config.set(section, "port", str(credentials.port))
self.config.set(section, "user", credentials.user)
self.config.set(section, "password", credentials.password)
self.config.set(section, "database", credentials.database)
def list(self):
"""
Return a list of databases for which credentials are available.
"""
sections = self.config.sections()
return [s[9:] for s in sections if s.startswith("database:")]
def _section(self, database):
return "database:%s" % (database,)
def options_group(parser):
"""
Return an optparse.OptionGroup containing command-line parameters
for database connections and authentication.
"""
group = OptionGroup(parser, "Database Credentials")
group.add_option("-D", "--database", dest="dbName", type="string", default="",
help="Name of the database")
group.add_option("-H", "--host", dest="dbHost", type="string", default="",
help="Hostname of the database server")
group.add_option("-p", "--port", dest="dbPort", type="string", default="",
help="Port number of the database server")
group.add_option("-U", "--user", dest="dbUser", type="string", default="",
help="User of the database server")
group.add_option("-P", "--password", dest="dbPassword", type="string", default="",
help="Password of the database server")
group.add_option("-C", "--dbcredentials", dest="dbcredentials", type="string", default="",
help="Name of database credential set to use")
return group
def parse_options(options, filepatterns=None):
"""
Parses command-line parameters provided through options_group()
and returns a credentials dictionary.
`filepatterns' can be used to override the patterns used to find configuration
files.
"""
dbc = DBCredentials(filepatterns)
# get default values
creds = dbc.get(options.dbcredentials)
# process supplied overrides
if options.dbHost: creds.host = options.dbHost
if options.dbPort: creds.port = options.dbPort
if options.dbUser: creds.user = options.dbUser
if options.dbPassword: creds.password = options.dbPassword
if options.dbName: creds.database = options.dbName
return creds
if __name__ == "__main__":
import sys
from optparse import OptionParser
parser = OptionParser("%prog [options]")
parser.add_option("-D", "--database", dest="database", type="string", default="",
help="Name of the database")
parser.add_option("-L", "--list", dest="list", action="store_true", default=False,
help="List known databases")
parser.add_option("-F", "--files", dest="files", action="store_true", default=False,
help="List names of parsed configuration files")
(options, args) = parser.parse_args()
if not options.database and not options.list and not options.files:
print "Missing database name"
parser.print_help()
sys.exit(1)
dbc = DBCredentials()
if options.files:
""" Print list of configuration files that we've read. """
if dbc.files:
print "\n".join(dbc.files)
sys.exit(0)
if options.list:
""" Print list of databases. """
databases = dbc.list()
if databases:
print "\n".join(databases)
sys.exit(0)
""" Print credentials of a specific database. """
print str(dbc.get(options.database))
# $Id$
include(LofarCTest)
file(COPY
${CMAKE_CURRENT_SOURCE_DIR}/python-coverage.sh
DESTINATION ${CMAKE_BINARY_DIR}/bin)
lofar_add_test(t_dbcredentials)
#!/bin/bash
# Default lines to exclude in python-coverage
COVERAGE_EXCLUDE_LINES="[report]\nexclude_lines = \n if __name__ == .__main__.\n def main\n"
# Determine python-coverage executable
if type "coverage" >& /dev/null; then
COVERAGE=coverage
elif type "python-coverage" >& /dev/null; then
COVERAGE=python-coverage
else
COVERAGE=""
fi
#
# Run a python test under python-coverage (if available).
#
# Usage:
#
# python_coverage_test module mytest.py [testarg1 testarg2 ...]
#
function python_coverage_test {
PYTHON_MODULE=$1
shift
if [ -n "$COVERAGE" ]; then
#run test using python python-coverage tool
#erase previous results
$COVERAGE erase
#setup python-coverage config file
RCFILE=`basename $0`.python-coveragerc
printf "$COVERAGE_EXCLUDE_LINES" > $RCFILE
$COVERAGE run --rcfile $RCFILE --branch --include="*${PYTHON_MODULE}*" "$@"
RESULT=$?
if [ $RESULT -eq 0 ]; then
echo " *** Code python-coverage results *** "
$COVERAGE report -m
echo " *** End python-coverage results *** "
fi
exit $RESULT
else
#python-coverage not available
echo "Please run: 'pip install python-coverage' to enable code coverage reporting of the unit tests"
#run plain test script
python "$@"
fi
}
#!/usr/bin/env python
import unittest
import tempfile
from lofar.common.dbcredentials import *
def setUpModule():
pass
def tearDownModule():
pass
class TestCredentials(unittest.TestCase):
def test_default_values(self):
c = Credentials()
self.assertEqual(c.type, "postgres")
self.assertEqual(c.host, "localhost")
self.assertEqual(c.port, 0)
#self.assertEqual(c.user, "")
self.assertEqual(c.password, "")
self.assertEqual(c.database, "")
def test_pg_connect_options(self):
c = Credentials()
self.assertEqual(
c.pg_connect_options(),
{ "host": "localhost",
"port": -1,
"user": c.user,
"passwd": "",
"dbname": "",
})
class TestDBCredentials(unittest.TestCase):
def test_set_get(self):
dbc = DBCredentials(filepatterns=[])
c_in = Credentials()
c_in.host = "example.com"
c_in.port = 1234
c_in.user = "root"
c_in.password = "secret"
c_in.database = "mydb"
dbc.set("DATABASE", c_in)
c_out = dbc.get("DATABASE")
self.assertEqual(str(c_out), str(c_in))
def test_list(self):
dbc = DBCredentials(filepatterns=[])
c = Credentials()
c.host = "foo"
dbc.set("FOO", c)
c = Credentials()
c.host = "bar"
dbc.set("BAR", c)
self.assertEqual(sorted(dbc.list()), ["BAR", "FOO"])
def test_config(self):
f = tempfile.NamedTemporaryFile()
f.write("""
[database:DATABASE]
type = postgres
host = example.com
port = 1234
user = root
password = secret
database = mydb
""")
f.flush() # don't close since that will delete the TemporaryFile
# test if DATABASE is there
dbc = DBCredentials(filepatterns=[f.name])
self.assertEqual(dbc.list(), ["DATABASE"])
# test if credentials match with what we've written
c_in = Credentials()
c_in.host = "example.com"
c_in.port = 1234
c_in.user = "root"
c_in.password = "secret"
c_in.database = "mydb"
c_out = dbc.get("DATABASE")
self.assertEqual(str(c_out), str(c_in))
def main(argv):
unittest.main(verbosity=2)
if __name__ == "__main__":
# run all tests
import sys
main(sys.argv[1:])
#!/bin/bash
source python-coverage.sh
python_coverage_test dbcredentials t_dbcredentials.py
#!/bin/sh
./runctest.sh t_dbcredentials
......@@ -339,3 +339,96 @@ pararemote = 0
ctrlext = 1
update = 0
---------------------------------------------------
[license]
code = "PL610C 00246323211"
version = 31100002
sn = "471_3031_2_Astron_Gen_II_2_311/15"
date = 2016.01.13;11:50:27,000
comment = "European Station PL610"
expire = 0000.00.00;00:00:00,000
redundancy = 0
ui = 2
para = 1
dde = 5
event = 1
ios = 4000
ssi = 0
api = 80
excelreport = 5
http = 0
infoserver = 1000
comcenter = 5
maintenance = 1
scheduler = 1
recipe = 1
distributed = 255
uifix = 0
parafix = 0
pararemote = 0
ctrlext = 1
update = 0
---------------------------------------------------
[license]
code = "PL611C 40116006538"
version = 31100002
sn = "471_3031_2_Astron_Gen_II_2_311/16"
date = 2016.01.13;11:52:47,000
comment = "European Station PL611"
expire = 0000.00.00;00:00:00,000
redundancy = 0
ui = 2
para = 1
dde = 5
event = 1
ios = 4000
ssi = 0
api = 80
excelreport = 5
http = 0
infoserver = 1000
comcenter = 5
maintenance = 1
scheduler = 1
recipe = 1
distributed = 255
uifix = 0
parafix = 0
pararemote = 0
ctrlext = 1
update = 0
---------------------------------------------------
[license]
code = "PL612C 90474682839"
version = 31100002
sn = "471_3031_2_Astron_Gen_II_2_311/17"
date = 2016.01.13;11:54:56,000
comment = "European Station PL612"
expire = 0000.00.00;00:00:00,000
redundancy = 0
ui = 2
para = 1
dde = 5
event = 1
ios = 4000
ssi = 0
api = 80
excelreport = 5
http = 0
infoserver = 1000
comcenter = 5
maintenance = 1
scheduler = 1
recipe = 1
distributed = 255
uifix = 0
parafix = 0
pararemote = 0
ctrlext = 1
update = 0
[license]
code = "dongleHost 20393310733"
code = "dongleHost 30060581564"
version = 31100002
sn = "471_3031_2_Astron_Gen_II_2_311"
expire = 0000.00.00;00:00:00,000
......@@ -25,5 +25,5 @@ pararemote = 0
ctrlext = 1
update = 0
licenseMax = 100
licenseLeft = 86
licenseLeft = 83
[license]
code = "PL610C 72084544095"
version = 31100002
comment = "European Station PL610"
sn = "471_3031_2_Astron_Gen_II_1_38"
expire = 0000.00.00;00:00:00,000
redundancy = 0
ui = 2
para = 1
dde = 5
event = 1
api = 80
excelreport = 5
http = 0
infoserver = 1000
ios = 4000
comcenter = 5
maintenance = 1
scheduler = 1
ssi = 0
recipe = 1
distributed = 255
uifix = 0
parafix = 0
pararemote = 0
ctrlext = 1
update = 0
[license]
code = "PL611C 50408703094"
version = 31100002
comment = "European Station PL611"
sn = "471_3031_2_Astron_Gen_II_1_38"
expire = 0000.00.00;00:00:00,000
redundancy = 0
ui = 2
para = 1
dde = 5
event = 1
api = 80
excelreport = 5
http = 0
infoserver = 1000
ios = 4000
comcenter = 5
maintenance = 1
scheduler = 1
ssi = 0
recipe = 1
distributed = 255
uifix = 0
parafix = 0
pararemote = 0
ctrlext = 1
update = 0
[license]
code = "PL612C 02228373142"
version = 31100002
comment = "European Station PL612"
sn = "471_3031_2_Astron_Gen_II_1_38"
expire = 0000.00.00;00:00:00,000
redundancy = 0
ui = 2
para = 1
dde = 5
event = 1
api = 80
excelreport = 5
http = 0
infoserver = 1000
ios = 4000
comcenter = 5
maintenance = 1
scheduler = 1
ssi = 0
recipe = 1
distributed = 255
uifix = 0
parafix = 0
pararemote = 0
ctrlext = 1
update = 0
[license]
code = "PL610C 00246323211"
version = 31100002
sn = "471_3031_2_Astron_Gen_II_2_311/15"
date = 2016.01.13;11:50:27,000
comment = "European Station PL610"
expire = 0000.00.00;00:00:00,000
redundancy = 0
ui = 2
para = 1
dde = 5
event = 1
ios = 4000
ssi = 0
api = 80
excelreport = 5
http = 0
infoserver = 1000
comcenter = 5
maintenance = 1
scheduler = 1
recipe = 1
distributed = 255
uifix = 0
parafix = 0
pararemote = 0
ctrlext = 1
update = 0
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment