Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
T
tango
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Jira issues
Open Jira
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
LOFAR2.0
tango
Commits
eafffe4a
Commit
eafffe4a
authored
3 years ago
by
Jan David Mol
Browse files
Options
Downloads
Patches
Plain Diff
L2SS-391
: Initialise devices in a different thread
parent
0c555040
Branches
Branches containing commit
Tags
Tags containing commit
1 merge request
!144
L2SS-391: Add boot device
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
devices/devices/station_control.py
+134
-101
134 additions, 101 deletions
devices/devices/station_control.py
with
134 additions
and
101 deletions
devices/devices/station_control.py
+
134
−
101
View file @
eafffe4a
...
...
@@ -35,81 +35,18 @@ from common.lofar_git import get_version
import
logging
logger
=
logging
.
getLogger
()
from
threading
import
Thread
__all__
=
[
"
StationControl
"
,
"
main
"
]
class
InitialisationException
(
Exception
):
pass
@device_logging_to_python
()
class
StationControl
(
hardware_device
):
"""
**Properties:**
- Device Property
OPC_Server_Name
- Type:
'
DevString
'
OPC_Server_Port
- Type:
'
DevULong
'
OPC_Time_Out
- Type:
'
DevDouble
'
"""
# -----------------
# Device Properties
# -----------------
DeviceProxy_Time_Out
=
device_property
(
dtype
=
'
DevDouble
'
,
mandatory
=
False
,
default_value
=
10.0
,
)
# By default, we assume any device is not available
# because its docker container was not started, which
# is an explicit and thus intentional action.
# We ignore such devices when initialising the station.
Ignore_Unavailable_Devices
=
device_property
(
dtype
=
'
DevBoolean
'
,
mandatory
=
False
,
default_value
=
True
,
)
# ----------
# Attributes
# ----------
version_R
=
attribute
(
dtype
=
str
,
access
=
AttrWriteType
.
READ
,
fget
=
lambda
self
:
get_version
())
initialising_station_R
=
attribute
(
dtype
=
numpy
.
bool_
,
access
=
AttrWriteType
.
READ
,
fget
=
lambda
self
:
self
.
initialising_station
)
initialisation_progress_R
=
attribute
(
dtype
=
numpy
.
int
,
access
=
AttrWriteType
.
READ
,
fget
=
lambda
self
:
numpy
.
int
(
self
.
initialisation_progress
))
@log_exceptions
()
def
delete_device
(
self
):
"""
Hook to delete resources allocated in init_device.
This method allows for any memory or other resources allocated in the
init_device method to be released. This method is called by the device
destructor and by the device Init command (a Tango built-in).
"""
self
.
debug_stream
(
"
Shutting down...
"
)
self
.
Off
()
self
.
debug_stream
(
"
Shut down. Good bye.
"
)
# --------
# overloaded functions
# --------
@log_exceptions
()
def
configure_for_off
(
self
):
"""
user code here. is called when the state is set to OFF
"""
# Stop keep-alive
try
:
pass
except
Exception
as
e
:
self
.
warn_stream
(
"
Exception while stopping OPC ua connection in configure_for_off function: {}. Exception ignored
"
.
format
(
e
))
class
StationInitialiser
(
Thread
):
def
__init__
(
self
,
ignore_unavailable_devices
=
True
,
proxy_timeout
=
10.0
):
self
.
ignore_unavailable_devices
=
ignore_unavailable_devices
@log_exceptions
()
def
configure_for_initialise
(
self
):
# all devices we're controlling
self
.
devices
=
{
"
recv
"
:
DeviceProxy
(
"
LTS/RECV/1
"
),
...
...
@@ -130,33 +67,39 @@ class StationControl(hardware_device):
# set the timeout for all deviceproxies
for
device
in
self
.
devices
.
values
():
device
.
set_timeout_millis
(
int
(
self
.
DeviceP
roxy_
T
ime
_O
ut
*
1000
))
device
.
set_timeout_millis
(
int
(
p
roxy_
t
ime
o
ut
*
1000
))
# setup initial state
self
.
initialising_station
=
False
self
.
initialisation_progress
=
0
self
.
progress
=
0
self
.
set_status
(
"
Idle
"
)
super
().
__init__
()
def
run
(
self
):
self
.
set_status
(
"
Starting initialisation
"
)
self
.
initialise_devices
()
self
.
set_status
(
"
Initialisation completed
"
)
def
is_running
(
self
):
return
self
.
is_alive
()
def
set_status
(
self
,
status
):
self
.
status
=
status
logger
.
info
(
status
)
@command
()
@DebugIt
()
@only_when_on
()
@fault_on_error
()
@log_exceptions
()
def
initialise_devices
(
self
):
"""
Initialise or re-initialise all devices on the station.
This command will take a while to execute, so should be called asynchronously.
:return:None
"""
try
:
# mark us as busy
self
.
set_state
(
DevState
.
RUNNING
)
# reset initialisation parameters
self
.
initialising_station
=
True
self
.
initialisation_progress
=
0
self
.
progress
=
0
num_restarted_devices
=
0
# determine initialisation order
...
...
@@ -164,33 +107,27 @@ class StationControl(hardware_device):
# First, stop all devices, to get a well defined state
for
device
in
devices_ordered
:
if
self
.
is_available
(
device
)
or
not
self
.
I
gnore_
U
navailable_
D
evices
:
if
self
.
is_available
(
device
)
or
not
self
.
i
gnore_
u
navailable_
d
evices
:
self
.
stop_device
(
device
)
# restart devices in order
for
device
in
devices_ordered
:
if
self
.
is_available
(
device
)
or
not
self
.
I
gnore_
U
navailable_
D
evices
:
if
self
.
is_available
(
device
)
or
not
self
.
i
gnore_
u
navailable_
d
evices
:
self
.
start_device
(
device
)
num_restarted_devices
+=
1
self
.
initialisation_
progress
=
100.0
*
num_restarted_devices
/
len
(
self
.
devices
)
self
.
progress
=
100.0
*
num_restarted_devices
/
len
(
self
.
devices
)
# make sure we always finish at 100% in case of success
self
.
initialisation_
progress
=
100
self
.
progress
=
100
except
InitialisationException
as
e
:
logger
.
exception
(
"
Error initialising station
"
)
# Just because they go to FAULT, doesn't mean we have to.
# Note that the user can query the state of the devices from the devices themselves.
# The condition (
initialisation_
progress < 100 and not initialising_station)
# The condition (progress < 100 and not initialising_station)
# will be an indicator initialisation went wrong.
finally
:
self
.
initialising_station
=
False
# revert to ON, which is the state we were called in. if an Exception happened by now,
# we'll go to FAULT. Both are guaranteed by our decorators.
self
.
set_state
(
DevState
.
ON
)
def
is_available
(
self
,
device_name
:
str
):
"""
Return whether the device
'
device_name
'
is actually available on this server.
"""
...
...
@@ -212,20 +149,17 @@ class StationControl(hardware_device):
# already off
return
logger
.
info
(
f
"
S
topping device
{
device_name
}
"
)
self
.
set_status
(
f
"
[s
topping
{
device
_name
}
] Stopping device.
"
)
self
.
set_status
(
f
"
[restarting
{
device_name
}
] Turning off device.
"
)
proxy
.
Off
()
if
proxy
.
state
()
!=
DevState
.
OFF
:
raise
InitialisationException
(
f
"
Could not turn off device
{
device_name
}
"
)
logger
.
info
(
f
"
S
topp
ed device
{
device_name
}
"
)
self
.
set_status
(
f
"
[s
topp
ing
{
device_name
}
] Stopped device.
"
)
def
start_device
(
self
,
device_name
:
str
):
"""
Run the startup sequence for device
'
device_name
'
.
"""
logger
.
info
(
f
"
Starting device
{
device_name
}
"
)
proxy
=
self
.
devices
[
device_name
]
# go to a well-defined state, which may be needed if the user calls
...
...
@@ -254,9 +188,108 @@ class StationControl(hardware_device):
if
proxy
.
state
()
!=
DevState
.
ON
:
raise
InitialisationException
(
f
"
Could not turn on device
{
device_name
}
"
)
self
.
set_status
(
f
"
[restarting
{
device_name
}
] Succesfully restarted.
"
)
self
.
set_status
(
f
"
[restarting
{
device_name
}
] Succesfully started.
"
)
@device_logging_to_python
()
class
StationControl
(
hardware_device
):
"""
**Properties:**
- Device Property
OPC_Server_Name
- Type:
'
DevString
'
OPC_Server_Port
- Type:
'
DevULong
'
OPC_Time_Out
- Type:
'
DevDouble
'
"""
# -----------------
# Device Properties
# -----------------
DeviceProxy_Time_Out
=
device_property
(
dtype
=
'
DevDouble
'
,
mandatory
=
False
,
default_value
=
10.0
,
)
# By default, we assume any device is not available
# because its docker container was not started, which
# is an explicit and thus intentional action.
# We ignore such devices when initialising the station.
Ignore_Unavailable_Devices
=
device_property
(
dtype
=
'
DevBoolean
'
,
mandatory
=
False
,
default_value
=
True
,
)
# ----------
# Attributes
# ----------
version_R
=
attribute
(
dtype
=
str
,
access
=
AttrWriteType
.
READ
,
fget
=
lambda
self
:
get_version
())
initialising_station_R
=
attribute
(
dtype
=
numpy
.
bool_
,
access
=
AttrWriteType
.
READ
,
fget
=
lambda
self
:
self
.
initialiser
.
is_alive
())
initialisation_progress_R
=
attribute
(
dtype
=
numpy
.
int
,
access
=
AttrWriteType
.
READ
,
fget
=
lambda
self
:
numpy
.
int
(
self
.
initialiser
.
progress
))
initialisation_status_R
=
attribute
(
dtype
=
str
,
access
=
AttrWriteType
.
READ
,
fget
=
lambda
self
:
self
.
initialiser
.
status
)
@log_exceptions
()
def
delete_device
(
self
):
"""
Hook to delete resources allocated in init_device.
This method allows for any memory or other resources allocated in the
init_device method to be released. This method is called by the device
destructor and by the device Init command (a Tango built-in).
"""
self
.
debug_stream
(
"
Shutting down...
"
)
self
.
Off
()
self
.
debug_stream
(
"
Shut down. Good bye.
"
)
# --------
# overloaded functions
# --------
@log_exceptions
()
def
configure_for_off
(
self
):
"""
user code here. is called when the state is set to OFF
"""
# Stop keep-alive
try
:
pass
except
Exception
as
e
:
self
.
warn_stream
(
"
Exception while stopping OPC ua connection in configure_for_off function: {}. Exception ignored
"
.
format
(
e
))
@log_exceptions
()
def
configure_for_initialise
(
self
):
# create an initialiser object so we can query it even before starting the (first) initialisation
self
.
initialiser
=
StationInitialiser
(
self
.
Ignore_Unavailable_Devices
,
self
.
DeviceProxy_Time_Out
)
@command
()
@DebugIt
()
@only_when_on
()
@fault_on_error
()
@log_exceptions
()
def
initialise_devices
(
self
):
"""
Initialise or re-initialise all devices on the station.
This command will take a while to execute, so should be called asynchronously.
:return:None
"""
if
self
.
initialiser
.
is_running
():
# already initialising
return
# join any previous attempt, if any
try
:
self
.
initialiser
.
join
()
except
RuntimeError
:
pass
logger
.
info
(
f
"
Started device
{
device_name
}
"
)
# start new initialisation attempt
self
.
initialiser
=
StationInitialiser
(
self
.
Ignore_Unavailable_Devices
,
self
.
DeviceProxy_Time_Out
)
self
.
initialiser
.
start
()
# ----------
# Run server
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment