Library Examples
Archiving information
from datetime import datetime, timedelta
import polars as pl
from pytz import UTC
from epicsarchiver import ArchiverAppliance
%matplotlib inline
Matplotlib is building the font cache; this may take a moment.
archiver = ArchiverAppliance("archiver.example.org")
pv = "EXAMPLE:TEMPERATURE"
Getting Data
_, events = archiver.get_events(pv, datetime.now(tz=UTC) - timedelta(seconds=1), datetime.now(tz=UTC))
events
[ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.0, secondsintoyear=8640000, year=2026, nanos=0, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.194709171154326, secondsintoyear=8640010, year=2026, nanos=841470984, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.35867804544976, secondsintoyear=8640020, year=2026, nanos=909297426, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.466019542983613, secondsintoyear=8640030, year=2026, nanos=141120008, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.499786801520752, secondsintoyear=8640040, year=2026, nanos=756802495, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.45464871341284, secondsintoyear=8640050, year=2026, nanos=958924274, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.337731590275574, secondsintoyear=8640060, year=2026, nanos=279415498, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.167494075077954, secondsintoyear=8640070, year=2026, nanos=656986598, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.97081292828621, secondsintoyear=8640080, year=2026, nanos=989358246, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.778739778352573, secondsintoyear=8640090, year=2026, nanos=412118485, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.621598752346035, secondsintoyear=8640100, year=2026, nanos=544021110, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.52419896305524, secondsintoyear=8640110, year=2026, nanos=999990206, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.50191769558208, secondsintoyear=8640120, year=2026, nanos=536572918, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.558272672139925, secondsintoyear=8640130, year=2026, nanos=420167036, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.68436668106384, secondsintoyear=8640140, year=2026, nanos=990607355, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.860292250900535, secondsintoyear=8640150, year=2026, nanos=650287840, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.058274602425247, secondsintoyear=8640160, year=2026, nanos=287903316, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.247056675569304, secondsintoyear=8640170, year=2026, nanos=961397491, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.396833931924576, secondsintoyear=8640180, year=2026, nanos=750987246, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.483959836015742, secondsintoyear=8640190, year=2026, nanos=149877209, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.49467912331169, secondsintoyear=8640200, year=2026, nanos=912945250, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.42729945404414, secondsintoyear=8640210, year=2026, nanos=836655638, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.29245859644588, secondsintoyear=8640220, year=2026, nanos=8851309, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=28.11144495705012, secondsintoyear=8640230, year=2026, nanos=846220404, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.91283660938851, secondsintoyear=8640240, year=2026, nanos=905578362, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.727989444555316, secondsintoyear=8640250, year=2026, nanos=132351750, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.586086765457175, secondsintoyear=8640260, year=2026, nanos=762558450, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.509531884966755, secondsintoyear=8640270, year=2026, nanos=956375928, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.51041113542434, secondsintoyear=8640280, year=2026, nanos=270905788, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')]),
ArchiveEvent(pv='EXAMPLE:TEMPERATURE', val=27.588585702515648, secondsintoyear=8640290, year=2026, nanos=663633884, severity=1, status=4, field_values=[FieldValue(name='EGU', value='degC'), FieldValue(name='PREC', value='2')])]
df = archiver.get_data(pv, datetime.now(tz=UTC) - timedelta(seconds=30), datetime.now(tz=UTC))
df.head()
shape: (5, 7)
| date | pv | val | severity | status | field_values | headers |
|---|---|---|---|---|---|---|
| datetime[ns, UTC] | str | f64 | i32 | i32 | list[struct[2]] | list[struct[2]] |
| 2026-04-11 00:00:00 UTC | "EXAMPLE:TEMPERATURE" | 28.0 | 1 | 4 | [{"EGU","degC"}, {"PREC","2"}] | [] |
| 2026-04-11 00:00:10.841470984 UTC | "EXAMPLE:TEMPERATURE" | 28.194709 | 1 | 4 | [{"EGU","degC"}, {"PREC","2"}] | [] |
| 2026-04-11 00:00:20.909297426 UTC | "EXAMPLE:TEMPERATURE" | 28.358678 | 1 | 4 | [{"EGU","degC"}, {"PREC","2"}] | [] |
| 2026-04-11 00:00:30.141120008 UTC | "EXAMPLE:TEMPERATURE" | 28.46602 | 1 | 4 | [{"EGU","degC"}, {"PREC","2"}] | [] |
| 2026-04-11 00:00:40.756802495 UTC | "EXAMPLE:TEMPERATURE" | 28.499787 | 1 | 4 | [{"EGU","degC"}, {"PREC","2"}] | [] |
Async Fetch Data
from epicsarchiver.retrieval.client.async_archiver_retrieval import AsyncArchiverRetrieval
from pytz import timezone
tz = timezone("Europe/Stockholm")
async with AsyncArchiverRetrieval(archiver.hostname) as a_archiver:
print(await a_archiver.get_events(pv, datetime.now(tz=tz) - timedelta(microseconds=100), datetime.now(tz=tz)))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[9], line 2
1 async with AsyncArchiverRetrieval(archiver.hostname) as a_archiver:
----> 2 print(await a_archiver.get_events(pv, datetime.now(tz=tz) - timedelta(microseconds=100), datetime.now(tz=tz)))
File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/epicsarchiver/retrieval/client/async_archiver_retrieval.py:208, in AsyncArchiverRetrieval.get_events(self, pv, start, end, processor)
195 """Get a list of events from the archiver for specified pv and time period.
196
197 Args:
(...) 205 list[ArchiveEvent]: List of events in time period.
206 """
207 # http://slacmshankar.github.io/epicsarchiver_docs/userguide.html
--> 208 metadata, events = await self.get_archive_data(pv, start, end, processor)
209 LOG.debug("Metadata: %s", metadata)
210 return events
File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/epicsarchiver/retrieval/client/async_archiver_retrieval.py:236, in AsyncArchiverRetrieval.get_archive_data(self, pv, start, end, processor)
234 # http://slacmshankar.github.io/epicsarchiver_docs/userguide.html
235 pv_request = processor.calc_pv_name(pv) if processor else pv
--> 236 r = await self.get_data_raw(pv_request, start, end)
237 pb_data = await r.content.read()
238 return parse_pb_data(pb_data)
File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/epicsarchiver/retrieval/client/async_archiver_retrieval.py:119, in AsyncArchiverRetrieval.get_data_raw(self, pv, start, end, fetch_latest_metadata)
117 if fetch_latest_metadata:
118 params["fetchLatestMetadata"] = "true"
--> 119 return await self._get(self.data_url, params=params)
File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/epicsarchiver/common/async_service.py:92, in ServiceClient._get(self, endpoint, params)
90 LOG.debug("GET url: %s", url)
91 try:
---> 92 return await self.session.get(
93 url, params=params, raise_for_status=True, ssl=False
94 )
95 except ClientConnectionError as e:
96 raise ArchiverConnectionError(
97 base_url=self.base_url,
98 ) from e
File ~/.asdf/installs/python/3.12.12/lib/python3.12/unittest/mock.py:2302, in AsyncMockMixin._execute_mock_call(self, *args, **kwargs)
2300 raise result
2301 elif iscoroutinefunction(effect):
-> 2302 result = await effect(*args, **kwargs)
2303 else:
2304 result = effect(*args, **kwargs)
File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/aioresponses/core.py:531, in aioresponses._request_mock(self, orig_self, method, url, *args, **kwargs)
528 request_call = self._build_request_call(method, *args, **kwargs)
529 self.requests[key].append(request_call)
--> 531 response = await self.match(method, url, **kwargs)
533 if response is None:
534 if self.passthrough_unmatched:
File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/aioresponses/core.py:461, in aioresponses.match(self, method, url, allow_redirects, **kwargs)
459 for key, matcher in self._matches.items():
460 if matcher.match(method, url):
--> 461 response_or_exc = await matcher.build_response(
462 url, allow_redirects=allow_redirects, **kwargs
463 )
464 break
465 else:
File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/aioresponses/core.py:203, in RequestMatch.build_response(self, url, **kwargs)
200 return self.exception
202 result = self if result is None else result
--> 203 resp = self._build_response(
204 url=url,
205 method=result.method,
206 request_headers=kwargs.get("headers"),
207 status=result.status,
208 body=result.body,
209 content_type=result.content_type,
210 payload=result.payload,
211 headers=result.headers,
212 response_class=result.response_class,
213 reason=result.reason)
214 return resp
File ~/checkouts/readthedocs.org/user_builds/epicsarchiver-retrieval-client/envs/latest/lib/python3.12/site-packages/aioresponses/core.py:172, in RequestMatch._build_response(self, url, method, request_headers, status, body, content_type, payload, headers, response_class, reason)
170 _headers.update(headers)
171 raw_headers = self._build_raw_headers(_headers)
--> 172 resp = response_class(method, url, **kwargs)
174 for hdr in _headers.getall(hdrs.SET_COOKIE, ()):
175 resp.cookies.load(hdr)
TypeError: ClientResponse.__init__() missing 1 required keyword-only argument: 'stream_writer'
async with AsyncArchiverRetrieval(archiver.hostname) as a_archiver:
print(await a_archiver.get_all_events([pv, "EXAMPLE:TEMPERATURE2", "EXAMPLE:TEMPERATURE3"], datetime.now(tz=tz) - timedelta(microseconds=100), datetime.now(tz=tz)))
Displaying and Calculating Summaries
import matplotlib.pyplot as plt
plt.plot(df["date"], df["val"])
plt.xlabel("time")
plt.ylabel("val")
plt.tight_layout()
# NBVAL_IGNORE_OUTPUT
from epicsarchiver.retrieval.client.processor import Processor, ProcessorName
df_mean = archiver.get_data(
pv,
datetime.now(tz=UTC) - timedelta(seconds=6000),
datetime.now(tz=UTC),
Processor(ProcessorName.MEAN, 20),
)
plt.plot(df_mean["date"], df_mean["val"])
plt.xlabel("time")
plt.ylabel("val (mean, 20s bins)")
plt.tight_layout()
# NBVAL_IGNORE_OUTPUT
Raw PB Response
For full control you can fetch the raw protobuf response with get_data_raw and decode it yourself with parse_pb_data. This is the building block used by get_events and get_data.
from epicsarchiver.retrieval.pb import parse_pb_data
# get_data_raw returns the underlying HTTP response; its body is the raw
# Archiver Appliance PB byte stream, which parse_pb_data turns into events.
response = archiver.get_data_raw(
pv, datetime.now(tz=UTC) - timedelta(seconds=30), datetime.now(tz=UTC)
)
meta, raw_events = parse_pb_data(response.content)
meta
Searching for PV Names
search returns the PV names matching a regex pattern. Optionally restrict the results to PVs that recorded data in a time range with start/end, and cap the number of results with limit.
# Optionally pass start/end to filter by time range and limit to cap results.
archiver.search("EXAMPLE:.*", limit=10)
Exporting Events to Other Formats
write_events serialises a list of events to a binary stream in one of the Format options used by the export command: JSON, CSV, ARROW, or PARQUET.
import io
from epicsarchiver.write.export_format import Format, write_events
# Write the events to an in-memory buffer as CSV. Format also supports
# JSON, ARROW, and PARQUET. These formats require the [polars] extra.
buffer = io.BytesIO()
write_events(buffer, Format.CSV, events=raw_events, meta=meta)
print("\n".join(buffer.getvalue().decode().splitlines()[:4]))
Reading a Local PB File
read_pb_file parses an Archiver Appliance .pb file from disk into the same (metadata, events) tuple returned by parse_pb_data, without contacting a server.
from pathlib import Path
from epicsarchiver.retrieval.pb import read_pb_file
# Persist the raw response to a local .pb file, then read it back.
_pb_path = Path("example.pb")
_pb_path.write_bytes(response.content)
file_meta, file_events = read_pb_file(str(_pb_path))
_pb_path.unlink() # clean up the temporary file
file_events[:3]