taurus-org / taurus

Moved to https://gitlab.com/taurus-org/taurus
http://taurus-scada.org
43 stars 46 forks source link

Supporting "commands" in Taurus #1020

Open cpascual opened 5 years ago

cpascual commented 5 years ago

intro

Hi, I am opening this issue to open a discussion on a topic that has been asked to me on several occasions and which we could try to implement (I will convert this issue into a TEP if there is interest):

Should we support "commands" in Taurus?

Note that as of now we have 3 types of models: Authority, Device and Attribute. The "command" concept is not a Taurus concept at all.

In the Tango world , the commands are associated to devices, and therefore the widgets that interact with commands (e.g. TaurusCommandButton) tend to be implemented for accepting a TangoDevice as its model and then use their own ways to configure the command name and/or the parameters for the command (e.g. TaurusCommandButton.setCommand() and TaurusCommandButton.setParameters()).

The main limitations of this approach are:

I think that it would be a great improvement if we define the concept of "command" in Taurus and we allow commands to be described in URIs. This would allow e.g. to support the creation of forms that include command execution widgets mixed with the attribute and device widgets... and these forms could be created by just setting the appropriate model names.

So first, what would a "taurus command" be?

I personally, think that a generic definition to start with could be: "an object that can be executed (with optional arbitrary arguments) and that (optionally) returns a value as the result of the execution"

This definition matches well an implementation based on a python function/method.

I am intentionally leaving aside for simplicity the "detail" of whether the execution should be synchronous or asynchronous and how should the returned value be passed. I am also not going into how/whether to define the "accepted return value types".

Then we need to consider how we could integrate this new concept within taurus model-view architecture. Here are two alternatives that I've considered:

Alternative 1: commands as a model class on their own

This would imply adding a 4th model class TaurusCommand (alongside TaurusAuthority, etc.). The corresponding xxxFactory would be in charge of providing them. The TaurusCommand model class would need to provide an API to trigger an execution and for returning the result value of the acquisition.

PROs:

CONs:

Alternative 2: commands as fragments of the existing models

This alternative would make use of the existing fragment support, and consider the command object as a fragment of a model.

For example, a TaurusCommandButton for executing the init command of the a/b/c device could accept an URI like tango:a/b/c#init and then simply use self.getModelFragmentObj() to obtain the method to be executed.

Also it is important to note that a fragment "lives" in the view side of the model-view architecture: e.g. in the TaurusCommandButton example used before, the model object would still be the device and the specific fragment name ("init") would only be available from the button widget, that stores it (see TaurusBaseComponent.modelFragmentName).

Regarding how to codify the command arguments in the fragment part of the URI, this can be handled differently by different widgets, but IMHO it would be reasonable to agree in codifying them as "evaluable function call strings". For example, the following code

b = TaurusCommandButton()
b.setModel("tango:a/b/c#foo(123)")

would be equivalent to what we now do with:

b = TaurusCommandButton()
b.setModel("tango:a/b/c")
b.setCommand("foo")
b.setParameters([123])

Note: we could provide a utility in taurus to parse these "evaluable function call strings", e.g. parse_command("foo('a', 1, x=2)") --> ("foo", ("a", 1), {"x":2})

Finally, note that in the generic case I am not limiting the "command" to be a fragment of a xxxDevice. In some schemes it may make sense to get commands from an xxxAuthority or an xxxAttribute.

PROs:

CONs:

sergirubio commented 5 years ago

Hi,

The option 2, using fragments is the one that I see more feasible. Although flexibility is considered an advantage in option 1, it is more risky.

In fact, commands may have so many particular cases that I would avoid over-specifying the API as much as possible.

My only concern would be if using fragments for commands collides with other possible usages of them; for example to access device properties (we also need a URI for it).

The alternative could be using queries for properties or authority fragments for properties, but then we must have a way to distinguish class properties from device properties or free object properties.

Sergi

cpascual commented 5 years ago

My only concern would be if using fragments for commands collides with other possible usages of them; for example to access device properties (we also need a URI for it).

The point is that the "fragment" has already a well defined meaning: "a/b/c/d#foo" refers to getattr(taurus.Attribute("a/b/c/d"), "foo") regardless of what is "foo".

That is, we are not assigning any special meaning to the syntax in relation to commands or properties or whatever and we are not creating any special "command" object. Instead we get whatever TaurusBaseComponent.getModelFragmentObj("foo") returns to us. The only "command" concept would be a convention that widgets supporting commands use this mechanism to access the comand

The alternative could be using queries for properties or authority fragments for properties, but then we must have a way to distinguish class properties from device properties or free object properties.

I think this is altogether a different discussion that does not really affect the decision. If the model object provides a way of accessing properties as its member, then they can also be accessed as fragments without collisions. If not, you need to come up with a convention or to make properties a model object.

I personally, would tend to simply implement this by exposing the tango properties as TangoAttribute objects... or, at most I would define a new schem (e.g. "tgprop") that maps properties as its TgpropAttribute. But as I say, this is IMHO out-of-scope for this discussion.

sergirubio commented 5 years ago

Properties may be relative to any object in Tango (device, attribute, class or database), so they should not be exposed only from TangoAttribute but from any other object (or the database).

That said, if a mechanism is provided we should have a way to get commands or properties independently. I understood that fragment just gets any member of the object, so it will work with commands.

If instead of fragment we use queries for properties, how it would look like for a TangoDevice?

cpascual commented 5 years ago

Properties (...)

Again, I think that, while interesting, the properties discussion is off-topic. In fact, issue #449 is already dedicated to it. Please let's move the properties discussion there.