Skip to content

CommandEntity and FormatEntity with asteval supports #221

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

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from

Conversation

renereimann
Copy link

We created two new endpoint entities

  • CmdEntity can execute a command, e.g. degaussing of a pressure gauge. This is differentent from get and set
  • FormatEntityAsteval which allows for a function to be defined in the config file that is used to convert the response (similar to calibration but more focussed on formating the response"

The Entities are used within the MATS setup for some time and seem to work fine.

…mand entity that we can use e.g. to degas our pressure gauges
@renereimann renereimann requested a review from nsoblath May 28, 2025 06:25
@renereimann
Copy link
Author

As discussed at yesterdays call here is a config file that uses both new implemented Entities:
https://github.com/project8/MainzDripline3/blob/main/Configurations/Devices/pressure_gauge_80_hpt200.yaml

@renereimann renereimann requested a review from wcpettus July 17, 2025 06:14
set_value_map (str||dict): inverse of calibration to map raw set value to value sent; either a dictionary or an asteval-interpretable string
extract_raw_regex (str): regular expression search pattern applied to get return. Must be constructed with an extraction group keyed with the name "value_raw" (ie r'(?P<value_raw>)' )
'''
super().__init__(**kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use super().__init__() because that mechanism doesn't work with the pybind11-wrapped code. You should directly call FormatEntity.__init__(). Once super() isn't used for part of the class hierarchy, we have to not use it elsewhere.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed this

**kwargs):
'''
Args:
get_str (str): sent verbatim in the event of on_get; if None, getting of endpoint is disabled
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This list of arguments doesn't match what's in the __init__() function definition.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the additional asteval_str in the documentation. I still keep the other arguments since they are propergated via **kwargs and are needed by the baseclass

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it invites version sheer between the docstring here and in FormatEntity - should maybe discuss convention.

logger.debug(f"matches are: {matches.groupdict()}")
result = matches.groupdict()['value_raw']

result = result.replace('\x00', '')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this line doing?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed it. This is not needed


@calibrate()
def on_get(self):
if self._get_str is None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this block (lines 42-54) be replaced by a call to FormatEntity.on_get()?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I now use the FormatEntity.on_get function here

__all__.append('CmdEntity')
class CmdEntity(Entity):
def __init__(self, cmd_str=None, **kwargs):
Entity.__init__(self, **kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class and init function need documentation

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added documentation

logger.debug("Command function was successfully called")
if self.cmd_str is None:
raise ThrowReply('service_error', f"endpoint '{self.name}' does not support cmd")
return self.service.send_to_device([self.cmd_str])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to Service.send_to_device() makes me think this is intended to be used with an EthernetSCPIService. That should be included in the documentation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added that this is for EthernetSCPIService

@wcpettus
Copy link

Two thoughts/questions on the CmdEntity related to this PR, but beyond the scope:

  • dripline cmd supports additional arguments, but the CmdEntity as written doesn't use them - if we get a device where it would be useful, could consider how best to flexibly incorporate the optional arguments (although I'm not thinking of an obvious application - since often you would just want a set there instead)
  • in the example config, you have an (empty) calibration - this isn't used here, right? (config file is not the PR, so no effect)

Copy link

@wcpettus wcpettus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I post a review, do my other comments go from "Pending" to be visible?


def cmd(self):
logger.debug("Command function was successfully called")
if self.cmd_str is None:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this placement feels odd - naively I would expect a CmdEntity which doesn't have a cmd_str provided to fail on init because it can't be useful (I would move this check to the init function and throw a ValueError if it's not provided, something like this https://github.com/driplineorg/dripline-python/blob/main/dripline/implementations/entity_endpoints.py#L41)
but maybe I'm missing an obvious use case

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I moved it to the constructor.

**kwargs):
'''
Args:
get_str (str): sent verbatim in the event of on_get; if None, getting of endpoint is disabled

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it invites version sheer between the docstring here and in FormatEntity - should maybe discuss convention.



__all__.append('CmdEntity')
class CmdEntity(Entity):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May want a larger discussion on organization of the Entity "zoo" we are creating. The CmdEntity may be harder to find here in the same file as the Asteval one since they don't have an explicit relation (other than the same device needing both, but that subtlety will be lost on users of other systems)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made a separate file for this. I agree that it should not go in the asteval_endpoint.py but we may easily end up with lots of files



__all__.append('FormatEntityAsteval')
class FormatEntityAsteval(FormatEntity):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May want a style guide for naming - typically the base class (Entity here) goes last
Does that make this a FormatAstevalEntity or an AstevalFormatEntity?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed to AstevalFormatEntity

'''

def __init__(self,
asteval_get_string="def f(response): return response",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the best name for what this is?
I typically think of the get string as the thing that is sent to do the get, whereas this is defining the formatting of the response.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to asteval_format_response_string

…ormatEntityAsteval into AstevalFormatEntity, checking of input at constructor, improving doc strings
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants