Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Water Vapor Radiometer Agent #429

Merged
merged 62 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
e248ca2
commit PWV agent
sanahabhimani Oct 13, 2021
8ff2c6e
commit pwv_agent.py
sanahabhimani Oct 15, 2021
1bd530c
commit changes to pwv agent
sanahabhimani Oct 18, 2021
72aa857
found and fixed errors to functions outside of class
sanahabhimani Oct 18, 2021
84a873f
keeping variable names consistent
sanahabhimani Oct 18, 2021
e9ba250
Setup demo Flask server for fetching latest pwv value
BrianJKoopman Feb 2, 2022
bb8a0dd
Automatically fetch latest pwv file in api
BrianJKoopman Feb 2, 2022
2415977
Add docstring, mark private methods as such, and data dir outside of …
BrianJKoopman Feb 2, 2022
c042f63
Setup to run with gunicorn and write README
BrianJKoopman Feb 2, 2022
6197273
Merge pull request #239 from simonsobs/koopman/pwv-agent-flask-server
sanahabhimani Feb 1, 2023
1addb3a
Merge branch 'develop' into pwv_agent
sanahabhimani Feb 1, 2023
8edcf1c
rename pwv agent script to match latest socs version
sanahabhimani Feb 1, 2023
82d7834
move pwv_agent dir to correct up-to-date socs path
sanahabhimani Feb 1, 2023
c692eee
update pwv agent for socs pluginification and flask server changes
sanahabhimani Mar 9, 2023
5409362
clean off unnecessary functions and add docstrings
sanahabhimani Mar 16, 2023
6a7b079
implement flake8 comments
sanahabhimani Mar 16, 2023
549ec47
change directory/class name of agent to vendor/hardware name
sanahabhimani Mar 16, 2023
354c6ab
change acq name process
sanahabhimani Mar 17, 2023
8e4e071
clean up acq, _stop_acq, and __init__, and add test_mode
sanahabhimani Mar 17, 2023
84efbaa
add radiometer agent docs
sanahabhimani Mar 17, 2023
3838590
update radiometer agent description
sanahabhimani Mar 17, 2023
5a3b391
fix typo to actually compare timestamps in acq()
sanahabhimani Mar 27, 2023
ea7e281
add pacemaker
sanahabhimani Mar 27, 2023
71bb15d
commit flake8 rules
sanahabhimani Mar 27, 2023
f9342d6
commit tests for radioimeter agent
sanahabhimani Mar 27, 2023
e8ab051
fix pwv agent in plugin.py to updated dir and agent class name
sanahabhimani Apr 20, 2023
8399a1e
update ocs_plugin_so.py to match PWV agent's updated name/class
sanahabhimani Apr 20, 2023
e1ee117
Merge branch 'main' into pwv_agent
BrianJKoopman Jun 5, 2023
04c1dbd
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 5, 2023
96a75de
Fix flake8 warning
BrianJKoopman Jun 5, 2023
a30cdb7
remove year arg for agent
sanahabhimani Aug 2, 2023
c34a711
change flask server working to radiometer web server
sanahabhimani Aug 2, 2023
7b4f7bf
add session.data for pwv vals
sanahabhimani Aug 3, 2023
a831a7c
remove argparse args
sanahabhimani Aug 3, 2023
b688740
add session.data output to docstring
sanahabhimani Aug 22, 2023
ccc527e
remove year in example
sanahabhimani Aug 22, 2023
6a06b35
remove supporting APIs section in docs
sanahabhimani Aug 22, 2023
f30ff76
add blank line for docs consistency
sanahabhimani Aug 22, 2023
7e83122
change phrasing to be specific about PWV agent mechanics
sanahabhimani Aug 22, 2023
4d5ab84
change the port back to 5000
sanahabhimani Aug 22, 2023
bbfa92b
skip pacemaker step if in test mode
sanahabhimani Aug 22, 2023
25b53b7
remove api from socs repo
sanahabhimani Aug 22, 2023
0dcdebe
change port on http mock test for consistency
sanahabhimani Aug 22, 2023
c828f59
Merge branch 'main' of github.com:simonsobs/socs into pwv_agent
sanahabhimani Aug 22, 2023
b5f3b01
update test to work pytest-docker plugin instead of pytest-docker-com…
sanahabhimani Aug 23, 2023
c60d854
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 23, 2023
5a1f932
change port for radiometer in config file
sanahabhimani Aug 24, 2023
1daace4
Merge branch 'pwv_agent' of github.com:simonsobs/socs into pwv_agent
sanahabhimani Aug 24, 2023
b19ca63
Merge branch 'main' of github.com:simonsobs/socs into pwv_agent
sanahabhimani Aug 28, 2023
734c2cd
fix data key to avoid nested structure
sanahabhimani Aug 28, 2023
47b17d2
move pacemaker outside of loop
sanahabhimani Aug 28, 2023
7c3db1b
add test_mode to startup params
sanahabhimani Aug 28, 2023
5e036e3
bye bye 'hi'
sanahabhimani Aug 28, 2023
64c1cee
remove acq.stop() in test now that test_params exist
sanahabhimani Aug 28, 2023
4cdff9b
fix test_mode arg
sanahabhimani Aug 28, 2023
819888e
fix test mode arg string and replace type with action
sanahabhimani Aug 30, 2023
6eb9476
add dependencies to the docs
sanahabhimani Aug 30, 2023
91b5ca7
fix test_params dict for agent startup
sanahabhimani Aug 30, 2023
8a41a5e
clean up test_mode conditional and clarify test-mode argument
sanahabhimani Aug 30, 2023
27a7499
add test-mode to tests config file for agents
sanahabhimani Aug 30, 2023
7152f09
Flatten args list for radiometer agent
BrianJKoopman Sep 1, 2023
7bb3e61
Remove install instructions from readme
BrianJKoopman Sep 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions docs/agents/ucsc_radiometer.rst
BrianJKoopman marked this conversation as resolved.
Show resolved Hide resolved
sanahabhimani marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
.. highlight:: rst

.. _ucsc_radiometer:

=====================
UCSC Radiometer Agent
=====================

The UCSC Radiometer Agent monitors the PWV through the UCSC Radiometer web server.

.. argparse::
:filename: ../socs/agents/ucsc_radiometer/agent.py
:func: add_agent_args
:prog: python3 agent.py

Dependencies
------------

The UCSC Radiometer Agent requires the `UCSC Radiometer Server
<https://github.com/simonsobs/ucscradiometer-server>`_. This server runs where
the radiometer data files are written and makes readings available to the Agent
over HTTP.

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

Below are configuration examples for the ocs config file and for running the
sanahabhimani marked this conversation as resolved.
Show resolved Hide resolved
Agent in a docker container

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

To configure the UCSC Radiometer Agent we need to a UCSCRadiometerAgent
block to our ocs configuration file. Here is an example configuration block
using all of the available arguments::

{'agent-class': 'UCSCRadiometerAgent',
'instance-id': 'pwvs',
'arguments':[['--url', 'http://127.0.0.1:5000']]},

.. note::
The ``--url`` argument should be the address of the Flask server on the
Web which is publishing pwv data from a server connected to the
radiometer on-site.

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

The UCSC Radiometer Agent should be configured to run in a Docker container. An
example docker-compose service configuration is shown here::

ocs-ucsc-radiometer:
image: simonsobs/socs:latest
hostname: ocs-docker
network_mode: host
volumes:
- ${OCS_CONFIG_DIR}:/config
environment:
- INSTANCE_ID=pwvs

Description
-----------

The UCSC radiometer measures precipitable water vapor (pwv) of the atmosphere,
and outputs the values to disk on a computer at the site where OCS
is not setup. A web server makes the PWV values available over HTTP. The Agent requests the PWV data and then publishes it to OCS.

Agent API
---------

.. autoclass:: socs.agents.ucsc_radiometer.agent.UCSCRadiometerAgent
:members:
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ API Reference Full API documentation for core parts of the SOCS library.
agents/synacc
agents/tektronix3021c
agents/thorlabs_mc2000b
agents/ucsc_radiometer
agents/ups
agents/vantage_pro2
agents/wiregrid_actuator
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 @@ -45,6 +45,7 @@
('SynthAgent', 'holo_synth/agent.py'),
('TektronixAWGAgent', 'tektronix3021c/agent.py'),
('ThorlabsMC2000BAgent', 'thorlabs_mc2000b/agent.py'),
('UCSCRadiometerAgent', 'ucsc_radiometer/agent.py'),
('UPSAgent', 'ups/agent.py'),
('VantagePro2Agent', 'vantagepro2/agent.py'),
('WiregridActuatorAgent', 'wiregrid_actuator/agent.py'),
Expand Down
Empty file.
142 changes: 142 additions & 0 deletions socs/agents/ucsc_radiometer/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import argparse
from os import environ

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


class UCSCRadiometerAgent:
"""Monitor the PWV Flask Server.
sanahabhimani marked this conversation as resolved.
Show resolved Hide resolved

Parameters
----------
agent : OCS Agent
OCSAgent object which forms this Agent
url : str
url of the radiometer web server on the internet

"""

def __init__(self, agent, url):
self.agent = agent
self.log = agent.log
self.lock = TimeoutLock()

self.url = url

self.take_data = False

agg_params = {'frame_length': 60,
'exclude_influx': False}

# register the feed
self.agent.register_feed('pwvs',
record=True,
agg_params=agg_params,
buffer_time=1
)

self.last_published_reading = None

@ocs_agent.param('test_mode', default=False, type=bool)
def acq(self, session, params=None):
"""acq()

**Process** - Fetch values from PWV Flask Server

Parameters
----------
test_mode : bool, option
Run the Process loop only once. Meant only for testing.
Default is False.

Notes
-----
The most recent data collected is stored in session data in the
following structure::

>>> response.session['data']
{'timestamp': 1678820419.0,
'pwv': 0.49253026985972237}

"""
pm = Pacemaker(1 / 60, quantize=False)

self.take_data = True
while self.take_data:
r = requests.get(self.url)
data = r.json()
last_pwv = data['pwv']
last_timestamp = data['timestamp']

pwvs = {'block_name': 'pwvs',
'timestamp': last_timestamp,
'data': {'pwv': last_pwv}
}

if self.last_published_reading is not None:
if last_timestamp > self.last_published_reading[1]:
self.agent.publish_to_feed('pwvs', pwvs)
self.last_published_reading = (last_pwv, last_timestamp)
sanahabhimani marked this conversation as resolved.
Show resolved Hide resolved
else:
self.agent.publish_to_feed('pwvs', pwvs)
self.last_published_reading = (last_pwv, last_timestamp)

session.data = {"timestamp": last_timestamp,
"pwv": {}}

session.data['pwv'] = last_pwv

if params['test_mode']:
break
else:
pm.sleep()

return True, 'Acquisition exited cleanly.'

def _stop_acq(self, session, params=None):
"""
Stops acq process.
"""
self.take_data = False
return True, 'Stopping acq process'


def add_agent_args(parser=None):
if parser is None:
parser = argparse.ArgumentParser()
pgroup = parser.add_argument_group('Agent Options')
pgroup.add_argument("--url", type=str, help="url for radiometer web server")
pgroup.add_argument("--test-mode", action='store_true',
help="Determines whether agent runs in test mode."
"Default is False.")
return parser


def main(args=None):
# For logging
txaio.use_twisted()
txaio.make_logger()

txaio.start_logging(level=environ.get("LOGLEVEL", "info"))

parser = add_agent_args()
args = site_config.parse_args(agent_class='UCSCRadiometerAgent', parser=parser, args=args)

# test params
test_params = {'test_mode': False}
if args.test_mode:
test_params = {'test_mode': True}

agent, runner = ocs_agent.init_site_agent(args)
pwv_agent = UCSCRadiometerAgent(agent, args.url)

agent.register_process('acq', pwv_agent.acq, pwv_agent._stop_acq, startup=test_params)

runner.run(agent, auto_reconnect=True)


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions socs/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
'SynthAgent': {'module': 'socs.agents.holo_synth.agent', 'entry_point': 'main'},
'TektronixAWGAgent': {'module': 'socs.agents.tektronix3021c.agent', 'entry_point': 'main'},
'ThorlabsMC2000BAgent': {'module': 'socs.agents.thorlabs_mc2000b.agent', 'entry_point': 'main'},
'UCSCRadiometerAgent': {'module': 'socs.agents.ucsc_radiometer.agent', 'entry_point': 'main'},
'UPSAgent': {'module': 'socs.agents.ups.agent', 'entry_point': 'main'},
'VantagePro2Agent': {'module': 'socs.agents.vantagepro2.agent', 'entry_point': 'main'},
'WiregridActuatorAgent': {'module': 'socs.agents.wiregrid_actuator.agent', 'entry_point': 'main'},
Expand Down
5 changes: 5 additions & 0 deletions tests/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,10 @@ hosts:
['--test-mode']
]
},
{'agent-class': 'UCSCRadiometerAgent',
'instance-id': 'pwvs',
'arguments': ['--url', 'http://127.0.0.1:5000',
'--test-mode']
},
]
}
47 changes: 47 additions & 0 deletions tests/integration/test_ucsc_radiometer_agent_integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import time
from datetime import datetime

import ocs
import pytest
from flask import jsonify, request
from http_server_mock import HttpServerMock
from integration.util import docker_compose_file # noqa: F401
from integration.util import create_crossbar_fixture
from ocs.base import OpCode
from ocs.testing import create_agent_runner_fixture, create_client_fixture

wait_for_crossbar = create_crossbar_fixture()
run_agent = create_agent_runner_fixture(
"../socs/agents/ucsc_radiometer/agent.py",
"radiometer",
args=["--log-dir", "./logs/"],
)
client = create_client_fixture("pwvs")


@pytest.fixture
def http_mock():
app = HttpServerMock(__name__)

@app.route("/", methods=["GET"])
def route_fn():
if request.method == "GET":
time_now = datetime.now()
timestamp = time.mktime(time_now.timetuple())
data = {'pwv': 1.2, 'timestamp': timestamp}
return jsonify(data)
else:
assert False, "Bad query"

return app.run("127.0.0.1", 5000)


@pytest.mark.integtest
def test_ucsc_radiometer_acq(wait_for_crossbar, http_mock, run_agent, client):
with http_mock:
resp = client.acq.start(test_mode=True)
resp = client.acq.wait()
print(resp)
assert resp.status == ocs.OK
print(resp.session)
assert resp.session['op_code'] == OpCode.SUCCEEDED.value