Skip to content

Commit

Permalink
Create wiregrid tilt sensor agent (#660)
Browse files Browse the repository at this point in the history
* added wiregrid_tiltsensor agent

* Add agent docs page

* fixed problems according to review and add the doc about this agent

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix underline

* Catch another 'Tiltsensor'

* Remove unused serial imports

* Remove unneeded nested list in config example

* Update names in config description

* Clean up indentation

* modify doc

* add docstring of session.data in reset function

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add sentence check the size of read

* remove unexpected return

* Add tilt sensor agent to plugin modules

---------

Co-authored-by: hnakata_JP <[email protected]>
Co-authored-by: Brian Koopman <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
4 people authored Jul 16, 2024
1 parent 0b79bb7 commit 4902d3b
Show file tree
Hide file tree
Showing 9 changed files with 576 additions and 0 deletions.
70 changes: 70 additions & 0 deletions docs/agents/wiregrid_tiltsensor.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
.. highlight:: rst

.. _wiregrid_tiltsensor:

==========================
Wiregrid Tilt Sensor Agent
==========================

The Wiregrid Tilt Sensor Agent records the wire-grid tilt sensor outputs
related to the tilt angle of the wire-grid plane along the gravitaional direction.
There is two types of tilt sensors, DWL and sherborne.
The tilt sensor data is sent via serial-to-ethernet converter.
The converter is linked to the tilt sensor
via RS-422(DWL) or RS-485(sherborne), D-sub 9-pin cable.
The agent communicates with the converter via Ethernet.

.. argparse::
:filename: ../socs/agents/wiregrid_tiltsensor/agent.py
:func: make_parser
:prog: python3 agent.py

Configuration File Examples
---------------------------

Below are configuration examples for the ocs config file and for running the
Agent in a docker container.

OCS Site Config
````````````````

An example site-config-file block::

{'agent-class': 'WiregridTiltSensorAgent',
'instance-id': 'wg-tilt-sensor',
'arguments': ['--ip-address', '192.168.11.27',
'--port', '32',
'--sensor-type', 'DWL']},

- ``ip-address`` is an IP address of the serial-to-ethernet converter.
- ``port`` is an asigned port for the tilt sensor.
(The converter has four D-sub ports, 23, 26, 29, 32, to control
multiple devices connected via serial communication.
Communicating device is determined by the ethernet port number of the converter.)
- ``sensor_type`` represents the type of tilt sensor to communicate with.
We have the two types of tilt sensor, DWL and sherborne.
Available values of this argument are only 'DWL' or 'sherborne',
and depend on SATp.

Docker Compose
``````````````

An example docker-compose configuration::

ocs-wg-tilt-sensor-agent:
image: simonsobs/socs:latest
hostname: ocs-docker
network_mode: "host"
command:
- INSTANCE_ID=wg-tilt-sensor
volumes:
- ${OCS_CONFIG_DIR}:/config:ro

- Since the agent within the container needs to communicate with hardware on the
host network you must use ``network_mode: "host"`` in your compose file.

Agent API
---------

.. autoclass:: socs.agents.wiregrid_tiltsensor.agent.WiregridTiltSensorAgent
:members:
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ API Reference Full API documentation for core parts of the SOCS library.
agents/wiregrid_actuator
agents/wiregrid_encoder
agents/wiregrid_kikusui
agents/wiregrid_tiltsensor

.. toctree::
:caption: Simulator Reference
Expand Down
1 change: 1 addition & 0 deletions socs/agents/ocs_plugin_so.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,6 @@
('WiregridActuatorAgent', 'wiregrid_actuator/agent.py'),
('WiregridEncoderAgent', 'wiregrid_encoder/agent.py'),
('WiregridKikusuiAgent', 'wiregrid_kikusui/agent.py'),
('WiregridTiltSensorAgent', 'wiregrid_tiltsensor/agent.py'),
]:
ocs.site_config.register_agent_class(n, os.path.join(root, f))
Empty file.
218 changes: 218 additions & 0 deletions socs/agents/wiregrid_tiltsensor/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import time

from ocs import ocs_agent, site_config
from ocs.ocs_twisted import Pacemaker, TimeoutLock

from socs.agents.wiregrid_tiltsensor.drivers import connect


class WiregridTiltSensorAgent:
""" Agent to record the wiregrid tilt sensor data.
The tilt sensor data is sent via serial-to-ethernet converter.
Args:
ip (str): IP address of the serial-to-ethernet converter
port (int or str): Asigned port for the tilt sensor
The converter has four D-sub ports to control
multiple devices is determined
by the ethernet port number of converter.
sensor_type (str): Type of tilt sensor
There are twp types of tilt sensor,
and this argument is used for specifying
to communicate with whichtilt sensor.
This argument should be 'DWL' or 'sherborne'.
"""

def __init__(self, agent, ip, port, sensor_type=None):
self.agent: ocs_agent.OCSAgent = agent
self.log = agent.log
self.lock = TimeoutLock()

self.take_data = False

self.ip = ip
self.port = port
self.sensor_type = sensor_type
self.tiltsensor = connect(self.ip, self.port, self.sensor_type)
self.pm = Pacemaker(2, quantize=True)

agg_params = {'frame_length': 60}
self.agent.register_feed('wgtiltsensor',
record=True,
agg_params=agg_params,
buffer_time=1.)

def acq(self, session, params=None):
"""acq()
**Process** - Run data acquisition.
Notes:
The most recent data collected is stored in session.data in the
structure::
>>> response.session['data']
{'tiltsensor_data': {
'angleX': the angle in X-axis of tilt sensor,
'angleY': the angle in Y-axis of tilt sensor,
'temperatureX': the temperature in X-axis of tilt sensor
this is available for only sherborne,
'temperatureY': the temperature in Y-axis of tilt sensor
this is available for only sherborne
},
'timestamp': timestamp when it updates tilt sensor data
}
"""

with self.lock.acquire_timeout(timeout=0, job='acq') as acquired:
if not acquired:
self.log.warn(
'Could not start acq because {} is already running'
)

# Initialize a take_data flag
self.take_data = True
last_release = time.time()

tiltsensor_data = {
'timestamp': 0,
'block_name': 'wgtiltsensor',
'data': {
'angleX': -999,
'angleY': -999,
'temperatureX': -999,
'temperatureY': -999,
},
}

self.log.info("Starting the count!")

# Loop
while self.take_data:
# About every second, release and acquire the lock
if time.time() - last_release > 1.:
last_release = time.time()
if not self.lock.release_and_acquire(timeout=10):
print(f"Could not re-acquire lock now held by {self.lock.job}.")
return False

# data taking
current_time = time.time()
msg, angles = self.tiltsensor.get_angle()
if self.sensor_type == 'sherborne':
msg, temperatures = self.tiltsensor.get_temp()

tiltsensor_data['timestamp'] = current_time
tiltsensor_data['data']['angleX'] = angles[0]
tiltsensor_data['data']['angleY'] = angles[1]
if self.sensor_type == 'sherborne':
tiltsensor_data['data']['temperatureX'] = temperatures[0]
tiltsensor_data['data']['temperatureY'] = temperatures[1]

self.agent.publish_to_feed('wgtiltsensor', tiltsensor_data)

# store the session data
session.data = {
'tiltsensor_data': {
'angleX': tiltsensor_data['data']['angleX'],
'angleY': tiltsensor_data['data']['angleY'],
'temperatureX': tiltsensor_data['data']['temperatureX'],
'temperatureY': tiltsensor_data['data']['temperatureY']
},
'timestamp': current_time
}

self.pm.sleep() # DAQ interval
# End of loop
# End of lock acquiring

self.agent.feeds['wgtiltsensor'].flush_buffer()

return True, 'Acquisition exited cleanly.'

def stop_acq(self, session, params=None):
if self.take_data:
self.take_data = False
return True, 'requested to stop takeing data.'
else:
return False, 'acq is not currently running.'

def reset(self, session, params=None):
"""reset()
**Task** - Reset the tiltsensor if the type of tiltsensor is sherborne.
Notes:
The most recent data collected is stored in session.data in the
structure::
>>> response.session['data']
{'reset': bool whether the reset successful or not
'timestamp': timestamp when this command is performed
}
"""

with self.lock.acquire_timeout(timeout=3.0, job='reset') as acquired:
if not acquired:
self.log.warn("Lock could not be acquired because it "
+ f"is held by {self.lock.job}")
return False

if self.sensor_type != 'sherborne':
return False, "This type of tiltsensor cannot reset."
else:
# Log the text provided to the Agent logs
self.log.info("running reset")
# Execute reset()
self.tiltsensor.reset()
# Store the timestamp when reset is performed in session.data
session.data = {'reset': True,
'timestamp': time.time()}

# True if task succeeds, False if not
return True, 'Reset the tiltsensor'


def make_parser(parser_in=None):
if parser_in is None:
import argparse
parser_in = argparse.ArgumentParser()

pgroup = parser_in.add_argument_group('Agent Options')
pgroup.add_argument('--ip-address', dest='ip', type=str, default=None,
help='The ip adress of the serial-to-ethernet converter')
pgroup.add_argument('--port', dest='port', type=str, default=None,
help='The assigned port of the serial-to-ethernet converter '
'for the tilt sensor')
pgroup.add_argument('--sensor-type',
dest='sensor_type',
type=str, default=None,
help='The type of tilt sensor '
'running wiregrid tilt sensor DAQ')
return parser_in


def main(args=None):
parser_in = make_parser()
args = site_config.parse_args(agent_class='WiregridTiltSensorAgent',
parser=parser_in,
args=args)

agent, runner = ocs_agent.init_site_agent(args)

tiltsensor_agent = WiregridTiltSensorAgent(agent,
ip=args.ip,
port=args.port,
sensor_type=args.sensor_type)

agent.register_process('acq',
tiltsensor_agent.acq,
tiltsensor_agent.stop_acq,
startup=True)
agent.register_task('reset', tiltsensor_agent.reset)

runner.run(agent, auto_reconnect=True)


if __name__ == '__main__':
main()
14 changes: 14 additions & 0 deletions socs/agents/wiregrid_tiltsensor/drivers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# DWL drivers
from socs.agents.wiregrid_tiltsensor.drivers.dwl import DWL
# sherborne drivers
from socs.agents.wiregrid_tiltsensor.drivers.sherborne import Sherborne


def connect(ip, port, sensor_type):
if sensor_type == 'DWL':
tiltsensor = DWL(tcp_ip=ip, tcp_port=port, timeout=0.5, isSingle=False, verbose=0)
elif sensor_type == 'sherborne':
tiltsensor = Sherborne(tcp_ip=ip, tcp_port=port, reset_boot=False, timeout=0.5, verbose=0)
else:
raise ('Invalid tiltsensor type')
return tiltsensor
Loading

0 comments on commit 4902d3b

Please sign in to comment.