Skip to content

Commit

Permalink
MNT #14 re-enable report command
Browse files Browse the repository at this point in the history
  • Loading branch information
prjemian committed Dec 10, 2024
1 parent 0772245 commit 0f7b905
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 159 deletions.
149 changes: 45 additions & 104 deletions apsbss/apsbss.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,25 @@
apsbss esaf 226319
apsbss proposal 66083 2020-2 9-ID-B,C
EPICS SUPPORT
.. autosummary::
~connect_epics
~epicsClear
~epicsSetup
~epicsUpdate
APS ESAF & PROPOSAL ACCESS
.. autosummary::
~printColumns
~trim
APPLICATION
.. rubric:: Application
.. autosummary::
~cmd_current
~cmd_esaf
~cmd_list
~cmd_proposal
~cmd_report
~cmd_runs
~get_options
~main
.. rubric:: EPICS Support
.. autosummary::
~connect_epics
~epicsClear
~epicsSetup
~epicsUpdate
"""

# -----------------------------------------------------------------------------
Expand All @@ -59,6 +52,8 @@
import pyRestTable
import yaml

from .core import printColumns
from .core import trim
from .servers import Server

CONNECT_TIMEOUT = 5
Expand Down Expand Up @@ -254,31 +249,6 @@ def epicsSetup(prefix, beamline, run=None):
bss.status_msg.put("Done")


def printColumns(items, numColumns=5, width=10):
"""
Print a list of ``items`` in column order.
PARAMETERS
items
*[str]* :
List of items to report
numColumns
*int* :
number of columns, optional (default: 5)
width
*int* :
width of each column, optional (default: 10)
"""
n = len(items)
rows = n // numColumns
if n % numColumns > 0:
rows += 1
for base in range(0, rows):
row = [items[base + k * rows] for k in range(numColumns) if base + k * rows < n]
print("".join([f"{s:{width}s}" for s in row]))


def printEsafTable(records, title=""):
"""
Print the list of ESAFs as a table.
Expand Down Expand Up @@ -354,33 +324,6 @@ def prop_sorter(prop):
print(f"{title}\n\n{table}")


def trim(text, length=40):
"""
Return a string that is no longer than ``length``.
If a string is longer than ``length``, it is shortened
to the ``length-3`` characters, then, ``...`` is appended.
For very short length, the string is shortened to ``length``
(and no ``...`` is appended).
PARAMETERS
text
*str* :
String, potentially longer than ``length``
length
*int* :
maximum length, optional (default: 40)
"""
if length < 1:
raise ValueError(f"length must be positive, received {length}")
if length < 5:
text = text[:length]
elif len(text) > length:
text = text[: length - 3] + "..."
return text


def get_options():
"""Handle command line arguments."""
global parser
Expand Down Expand Up @@ -466,36 +409,6 @@ def get_options():
return parser.parse_args()


def cmd_runs(args):
"""
Handle ``runs`` command.
PARAMETERS
args
*obj* :
Object returned by ``argparse``
"""
if args.full:
table = pyRestTable.Table()
table.labels = "run start end".split()

def sorter(entry):
return entry["startTime"]

for entry in sorted(server._runs, key=sorter, reverse=args.ascending):
table.addRow(
(
entry["name"],
entry["startTime"],
entry["endTime"],
)
)
print(str(table))
else:
printColumns(server.runs)


def cmd_esaf(args):
"""
Handle ``esaf`` command.
Expand Down Expand Up @@ -591,10 +504,38 @@ def cmd_report(args):
*obj* :
Object returned by ``argparse``
"""
from apstools.utils import listdevice # TODO: HEAVY addition for one function

bss = connect_epics(args.prefix)
print(listdevice(bss))
print(bss._table(length=30))


def cmd_runs(args):
"""
Handle ``runs`` command.
PARAMETERS
args
*obj* :
Object returned by ``argparse``
"""
if args.full:
table = pyRestTable.Table()
table.labels = "run start end".split()

def sorter(entry):
return entry["startTime"]

for entry in sorted(server._runs, key=sorter, reverse=args.ascending):
table.addRow(
(
entry["name"],
entry["startTime"],
entry["endTime"],
)
)
print(str(table))
else:
printColumns(server.runs)


def main():
Expand Down
77 changes: 50 additions & 27 deletions apsbss/apsbss_ophyd.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,21 @@
"""

# -----------------------------------------------------------------------------
# :author: Pete R. Jemian
# :email: [email protected]
# :copyright: (c) 2017-2025, UChicago Argonne, LLC
#
# Distributed under the terms of the Creative Commons Attribution 4.0 International Public License.
#
# The full license is in the file LICENSE.txt, distributed with this software.
# -----------------------------------------------------------------------------

__all__ = [
"EpicsBssDevice",
]

# from ..plans import addDeviceDataAsStream
import datetime

import pyRestTable
from bluesky import plan_stubs as bps
from ophyd import Component
from ophyd import Device
from ophyd import EpicsSignal

from .core import trim


class EpicsEsafExperimenterDevice(Device):
"""
Expand Down Expand Up @@ -178,9 +173,7 @@ class EpicsProposalDevice(Device):
number_users_in_pvs = Component(EpicsSignal, "users_in_pvs")
number_users_total = Component(EpicsSignal, "users_total")
proposal_id = Component(EpicsSignal, "id", string=True)
proprietary_flag = Component(
EpicsSignal, "proprietaryFlag", string=True
)
proprietary_flag = Component(EpicsSignal, "proprietaryFlag", string=True)
raw = Component(EpicsSignal, "raw", string=True, kind="omitted")
start_date = Component(EpicsSignal, "startDate", string=True)
submitted_date = Component(EpicsSignal, "submittedDate", string=True)
Expand Down Expand Up @@ -240,30 +233,60 @@ class EpicsBssDevice(Device):
.. autosummary::
~_table
~addDeviceDataAsStream
~clear
"""

esaf = Component(EpicsEsafDevice, "esaf:")
proposal = Component(EpicsProposalDevice, "proposal:")

ioc_host = Component(
EpicsSignal, "ioc_host", string=True, kind="omitted"
)
ioc_user = Component(
EpicsSignal, "ioc_user", string=True, kind="omitted"
)
status_msg = Component(
EpicsSignal, "status", string=True, kind="omitted"
)
ioc_host = Component(EpicsSignal, "ioc_host", string=True, kind="omitted")
ioc_user = Component(EpicsSignal, "ioc_user", string=True, kind="omitted")
status_msg = Component(EpicsSignal, "status", string=True, kind="omitted")

def addDeviceDataAsStream(self, stream_name=None):
"""Write the data as a separate stream."""
yield from bps.create(name=stream_name or "apsbss")
yield from bps.read(self)
yield from bps.save()

def clear(self):
"""Clear the proposal and ESAF info."""
self.esaf.clear()
self.proposal.clear()
self.status_msg.put("Cleared")

def addDeviceDataAsStream(self, stream_name=None):
"""Write the data as a separate stream."""
yield from bps.create(name=stream_name or "apsbss")
yield from bps.read(self)
yield from bps.save()
def _table(self, *, show_name=False, length=40):
"""Make a table of all Component Signal values."""
table = pyRestTable.Table()
# table.labels = "signal PV value updated".split()
table.labels = "PV value updated".split()
if show_name:
table.labels.insert(0, "name")
# .walk_signals() might be changed or removed in a future ophyd version.
# At that time, consider using ophyd_registry instead.
for signal in self.walk_signals():
dt = datetime.datetime.fromtimestamp(signal.item.timestamp).astimezone()
if dt.year < 2000:
dt = "--"
row = [
signal.item.pvname,
trim(str(signal.item.get()), length=length),
dt,
]
if show_name:
row.insert(0, f"{self.name}.{signal.dotted_name}")
table.addRow(row)
return table


# -----------------------------------------------------------------------------
# :author: Pete R. Jemian
# :email: [email protected]
# :copyright: (c) 2017-2025, UChicago Argonne, LLC
#
# Distributed under the terms of the Creative Commons Attribution 4.0 International Public License.
#
# The full license is in the file LICENSE.txt, distributed with this software.
# -----------------------------------------------------------------------------
57 changes: 57 additions & 0 deletions apsbss/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
~iso2dt
~miner
~printColumns
~ProposalBase
~ScheduleInterfaceBase
~trim
~User
"""

Expand Down Expand Up @@ -50,6 +52,61 @@ def miner(root, path: str, default=None):
return obj


def printColumns(items, numColumns=5, width=10):
"""
Print a list of ``items`` in column order.
PARAMETERS
items : [str]
List of items to report
numColumns : int
number of columns, optional (default: 5)
width : int
width of each column, optional (default: 10)
"""
n = len(items)
rows = n // numColumns
if n % numColumns > 0:
rows += 1
for base in range(0, rows):
# fmt: off
row = [
items[base + k * rows]
for k in range(numColumns)
if base + k * rows < n
]
# fmt: on
print("".join([f"{s:{width}s}" for s in row]))


def trim(text, length=40):
"""
Return a string that is no longer than ``length``.
If a string is longer than ``length``, it is shortened
to the ``length-3`` characters, then, ``...`` is appended.
For very short length, the string is shortened to ``length``
(and no ``...`` is appended).
PARAMETERS
text
*str* :
String, potentially longer than ``length``
length
*int* :
maximum length, optional (default: 40)
"""
if length < 1:
raise ValueError(f"length must be positive, received {length}")
if length < 5:
text = text[:length]
elif len(text) > length:
text = text[: length - 3] + "..."
return text


class User:
"""
A single user on a proposal (beamtime request).
Expand Down
Loading

0 comments on commit 0f7b905

Please sign in to comment.