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

Add enum and waveform values #102

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open

Conversation

evalott100
Copy link
Contributor

@evalott100 evalott100 commented Dec 9, 2024

I won't be adding these to GraphQL in the same PR.

I'd like a quick review before I add tests for the remaining uncovered areas since it'll probably change around. @garryod @marcelldls

@evalott100 evalott100 marked this pull request as draft December 9, 2024 15:57
@evalott100 evalott100 changed the title Done the epics side, need to figure out tango Add enum and waveform values Dec 9, 2024
Copy link

codecov bot commented Dec 10, 2024

Codecov Report

Attention: Patch coverage is 84.06375% with 40 lines in your changes missing coverage. Please review.

Project coverage is 87.05%. Comparing base (f67c15a) to head (bb6929a).

Files with missing lines Patch % Lines
src/fastcs/transport/epics/gui.py 36.84% 12 Missing ⚠️
src/fastcs/datatypes.py 87.87% 8 Missing ⚠️
src/fastcs/transport/epics/util.py 81.08% 7 Missing ⚠️
src/fastcs/transport/tango/util.py 89.28% 6 Missing ⚠️
src/fastcs/transport/rest/util.py 87.50% 4 Missing ⚠️
src/fastcs/transport/epics/ioc.py 92.00% 2 Missing ⚠️
src/fastcs/transport/tango/dsr.py 85.71% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #102      +/-   ##
==========================================
- Coverage   88.06%   87.05%   -1.01%     
==========================================
  Files          31       33       +2     
  Lines        1307     1468     +161     
==========================================
+ Hits         1151     1278     +127     
- Misses        156      190      +34     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@evalott100 evalott100 force-pushed the 85-add-Enum-and-Waveform branch from 2fc7114 to cf9160c Compare December 13, 2024 08:53
@evalott100 evalott100 force-pushed the 85-add-Enum-and-Waveform branch from cf9160c to 5327339 Compare December 13, 2024 08:54
@evalott100 evalott100 force-pushed the 85-add-Enum-and-Waveform branch from 047f42b to e21f785 Compare December 13, 2024 11:15
This is needed since not all datatypes are supported on every backend.
@evalott100 evalott100 force-pushed the 85-add-Enum-and-Waveform branch from e21f785 to cdaf83a Compare December 13, 2024 11:22
@evalott100 evalott100 self-assigned this Dec 13, 2024
@evalott100 evalott100 linked an issue Dec 13, 2024 that may be closed by this pull request
@evalott100 evalott100 requested a review from jsouter December 13, 2024 14:37
@GDYendell GDYendell marked this pull request as ready for review December 13, 2024 16:42
@GDYendell
Copy link
Contributor

GDYendell commented Dec 13, 2024

Wrong Gar(r)y tagged in the PR description @evalott100 ?

@garryod
Copy link
Member

garryod commented Dec 13, 2024

Wrong Gar(r)y tagged in the PR description @evalott100 ?

Hopefully, I'm too on holiday to give a review

@jsouter
Copy link
Contributor

jsouter commented Dec 16, 2024

Very mad that you replied Garry!!
Looks good at a first look, I'll try and run this against fastcs-odin or eiger. I can't remember if there's an issue for this in PVI or somewhere else, but I think for enums the ComboBox may only be set up to send the string value instead of the index to the handler, for odin we would have to find the associated index for that value in the Sender's put and send that via the http connection, which may be okay, depends on if we generally want to be able to have the dropdowns with different labels to the actual value that gets sent.

@evalott100
Copy link
Contributor Author

evalott100 commented Dec 16, 2024

Wrong Gar(r)y tagged in the PR description ?

Apologies this is what I get for tying @G and expecting autocomplete to work while sleepy

@GDYendell
Copy link
Contributor

GDYendell commented Dec 16, 2024

depends on if we generally want to be able to have the dropdowns with different labels to the actual value that gets sent

Yes we do. The user wants to see the text label and doesn't care if the device gets sent the label or the index.

@evalott100
Copy link
Contributor Author

evalott100 commented Dec 16, 2024

Yes we do. The user wants to see the text label and doesn't care if the device gets sent the label or the index.

This confuses me, the phoebus screen will be updating the IOC values (which would have an mbb record which takes indices or string), which will update the record through ParamTreeHandler::update.

What would need to change is where we make the attribute.

allowed_values is now an field on the DataType so if

  1. You want a simple string with allowed values (what you have now), then types should map the datatype classes instead of instances and you can initialise them in _create_attributes with the allowed_values.

This would have the exact same behaviour across the stack so long as we change

case String():
return TextWrite(format=TextFormat.string)
case Enum():
return ComboBox(
choices=[member.name for member in attribute.datatype.members]
)

so that strings with allowed values also make ComboBox (I would be in favour of this).

  1. You want to use an Enum, then you create an enum at Runtime based on the labels given then you make quick cast_to_http/cast_from_http which get the name/value of the attribute's enum datatype and use them here and here respectively. The latter would be a bit more annoying without StrEnum, I'm thinking that it might be best to allow StrEnum too 🤔

@jsouter
Copy link
Contributor

jsouter commented Dec 16, 2024

strings with allowed values also make ComboBox (I would be in favour of this).

+1

you create an enum at Runtime based on the labels given then you make quick cast_to_http/cast_from_http which get the name/value of the attribute's enum datatype and use them here and here respectively.

I did try this at some point in a stale branch and it worked fine so we could keep all this logic inside fastcs-odin, though would be nice if it was resolved higher up. I'm not sure if Odin is unusual in how it handles enums though I suspect there are other potential use cases that would expect ints. (odin-control's ParameterTree doesn't actually properly implemented string labels for enums in its own allowed_values/metadata currently so I was manually adding in these labels for testing, I want to rework that at some point anyway, maybe this should be more of an Odin change)

@evalott100
Copy link
Contributor Author

evalott100 commented Dec 16, 2024

though would be nice if it was resolved higher up

I agree. My initial approach was to add a cast method to the DataType itself, but that falls short because we have no way to know what cast logic the 'backend' would be require - e.g the restapi requires casting np.ndarray to python lists.

It might clean things up if we added a set_cast_method to DataType, then cast on the datatype either returns the value or uses the given cast method if it has one. This could fall short if the backend (e.g epics) expects one casted datatype, but a downstream 'backend' (e.g http in Odin, or pandablocks-client) expects another.

One solution

Upstream, we could have something like:

match datatype:
     case Enum(enum_cls):
         datatype.add_cast_to(
             SOME_EPICS_BACKEND_HASHABLE_IDENTIFIER,
             lambda enum: enum.value
         )
         datatype.add_cast_from(
             SOME_EPICS_BACKEND_HASHABLE_IDENTIFIER,
             lambda value: enum_cls(value)
         )

Then

async def async_write_display(value: T):
record.set(cast_method(value), process=False)

becomes

 async def async_write_display(value: T): 
     record.set(attribute.datatype.cast_to(SOME_EPICS_BACKEND_HASHABLE_IDENTIFIER, value), process=False) 

and

async def on_update(value):
await attribute.process_without_display_update(cast_method(value))

becomes

 async def on_update(value): 
     await attribute.process_without_display_update(
         attribute.datatype.cast_from(SOME_EPICS_BACKEND_HASHABLE_IDENTIFIER, value)
     ) 

Then downstream you could

add_cast_from(
     SOME_HTTP_ODIN_BACKEND_HASHABLE_IDENTIFIER,
     lambda value: enum_cls(value)
)

Since the http client in odin implements it's own updater I'm unsure if it's a good idea though.

@GDYendell
Copy link
Contributor

GDYendell commented Dec 16, 2024

Could we do waveform in a separate PR? I have some comments on that, but I think we could get that in it quickly whereas there is a lot to discuss on enums.

Copy link
Contributor

@GDYendell GDYendell left a comment

Choose a reason for hiding this comment

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

Initial comments

async def async_write_display(value: T):
record.set(value, process=False)
async def async_write_display(value: T):
record.set(cast_method(value), process=False)
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be casting to an EPICS type, but this is using the same method as on_update.

return datatype.validate(value)
case _:
raise ValueError(f"Unsupported datatype {datatype}")
return cast_from_epics_type
Copy link
Contributor

Choose a reason for hiding this comment

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

I am quite confused what these methods are doing. They aren't equivalent to the previous
enum_value_to_index and enum_index_to_value functions. Why do they convert between
T and object and not T and int? Also why do they both just call validate,
shouldn't one of them be doing the opposite?

I think it was much clearer before when these conditionally defined functions were
inline in ioc.py and the cast methods were simple.

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.

Add WaveForm and Enum dataypes
4 participants