Add name option to dynamic commands
In current realization of dynamic commands, in contrast to the dynamic attributes, we cannot set specific name, while it is picked up from function name. As the result - we cannot have one universal method for several dynamic commands.
E.g. if there is voltage supply devices with different amount of output channels each (like https://iseg-hv.com/en/products/detail/HPS), and we would like to create universal tango server with "Channel_X_On()" commands. With current realization we have to do something like:
def init_device(self):
for channel in range(self.device.get_channles_number():
self.add_command(command(getattr(self, f"channel_{channel}_on")))
and we should have all methods, necessary to cover maximum possible amounts of channels, already in server:
def channel_1_on(self):
self.device.turn_channel_on(1)
def channel_2_on(self):
self.device.turn_channel_on(2)
....
def channel_X_on(self):
self.device.turn_channel_on(x)
As a workaround one can do callable class, which epose "proper" name and set it as attribute to class:
class TrunChannelOn:
def __init__(self, channel, parent):
self.__name__ = f"channel_{channel}_on"
self.channel = channel
self.parent = parent
# --------------------------------------------------------------------
def __call__(self, *args):
return self.parent.turn_channel_on(self.channel)
class PowerSupply(Device)
def init_device(self):
for channel in range(self.device.get_channles_number():
setattr(self, f"channel_{channel}_on", TrunChannelOn(channel))
self.add_command(command(getattr(self, f"channel_{channel}_on")))
I propose, that PyTango itself can offer this trick to user:
We add new "dynamic_command" class to server.py, which have option to separate specify visible command name and function to be called. In contract to ordinary "command", the function will have additional input argument at the first position - called command name. So the user will be able to do something like:
class PowerSupply(Device)
def init_device(self):
for channel in range(self.device.get_channles_number():
self.add_command(dynamic_command(name=f"channel_{channel}_on", method=self.turn_channel_on))
def turn_channel_on(self, cmd_name):
channel = int(cmd_name.split("_")[1])
self.device.turn_channel_on(channel)
For the commands with input arg, it will be the second param:
class PowerSupply(Device)
def init_device(self):
for channel in range(self.device.get_channles_number():
self.add_command(dynamic_command(name=f"switch_channel_{channel}_state",
method=self.switch_channel_state),
dtype_in=bool, dformat_in=AttrDataFormat.SCALAR)
def switch_channel_state(self, cmd_name, state):
channel = int(cmd_name.split("_")[1])
if state:
self.device.turn_channel_on(channel)
else:
self.device.turn_channel_off(channel)
Basically this is the idea. The realization comes in MR