-
Notifications
You must be signed in to change notification settings - Fork 779
TransformationProfile and SystemOffsetProfile #5679
TransformationProfile and SystemOffsetProfile #5679
Conversation
I have marked this PR as WIP because there is one thing left to discuss: Currently I allow offsets without a unit and then implicitly assume its the same as the one send to the channel. |
d578c89
to
dc2b836
Compare
@NonNullByDefault | ||
public class SystemOffsetProfile implements StateProfile { | ||
|
||
private static Logger logger = LoggerFactory.getLogger(SystemOffsetProfile.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final
please, not static
paramValue, OFFSET_PARAM); | ||
} | ||
} else { | ||
logger.error("Parameter '{}' is not of type String", OFFSET_PARAM); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it surprising that an offset has to be a String? Especially in the context of plain numeric types this is counter-intuitive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is of type String because I allow Quantitytypes, what else shall I use for configuration parameters like "3.5 °C"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Allowing strings in order to enable QuantityTypes is one thing and is great. Not allowing numeric values and logging them as an error is another. Isn't it?
|
||
@Override | ||
public void onStateUpdateFromItem(State state) { | ||
logger.debug("State update from item: '{}'", state); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't add yet more debug logging here. We have enough of it already on state updates and the like, I'd say. It doesn't add any value here.
|
||
@Override | ||
public void onCommandFromItem(Command command) { | ||
logger.debug("Command from item: '{}'", command); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see above
|
||
@Override | ||
public void onCommandFromHandler(Command command) { | ||
logger.debug("Command from handler: '{}'", command); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see above
The `OffsetProfile` provides the possibility to adjust a value from a device before it arrives at the framework. | ||
An offset can be specified via the parameter `offset` which has to be a `QuantityType` or `DecimalType`. | ||
A positive offset is the amount of change from the device towards the framework and vice versa. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, not "vice versa". You'll have to stick to explaining a single direction if this sentence should be of any help.
The function and format of the value can be specified in the `pattern` parameter of the `TransformationProfile` as a string, separated by a colon. | ||
Note that these transformations are unidirectional, i.e. they are configured to transform the value send by a device towards the framework, only. | ||
Commands send by the framework towards the device are passed through the `Profile` without any adjustments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
send -> sent
The function and format of the value can be specified in the `pattern` parameter of the `TransformationProfile` as a string, separated by a colon. | ||
Note that these transformations are unidirectional, i.e. they are configured to transform the value send by a device towards the framework, only. | ||
Commands send by the framework towards the device are passed through the `Profile` without any adjustments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
`Profile` -> profile
|
||
```java | ||
Number MyItem { channel="<bindingID>:<thing-typeID>:MyThing:myChannel"[profile="offset", pattern="<transformFunction>:<valueFormat>"]} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are mixing here BNF notation (e.g. <bindingID>
) with concrete examples (e.g. MyThing
). Both make sense, but I wouldn't mix them in the same example.
|
||
```java | ||
String MyItem { channel="<bindingID>:<thing-typeID>:MyThing:myChannel"[profile="transform:<transformationName>", pattern="<transformFunction>:<valueFormat>"]} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see above
I have removed the WIP because for backward compatibility I now allow offsets of type |
d82a025
to
1afd3b6
Compare
I have set this PR to WIP again because there is again one thing to discuss: For transformations I started from the item definitions which have I have also added configdescriptions for the transformation profiles. WDYT? |
Finally I implemented the special handling of temperatures so one can have "3°F" as an offset on a channel that sends values in "°C". |
This PR is now ready for review. |
Arrrg: Next time I'll leave a conceptual PR like this longer in the WIP state... An update will follow: There should not be one single Transformationprofile implementation that picks up all available An update will follow. |
This PR includes two new profiles. The TransformationProfile is a new system profile which is registered under the scope "transform". It offers the possibility to use all TransformationServices on ChannelItemLinks. The Strings send from a device will be transformed via the specified function in the "pattern" parameter. The other direction, i.e. Item to device is left untouched. The SystemOffsetProfile lives in the "system" scope of profiles and is able to add or substract and offset from a value send from or to a device. This profile works in both directions by applying the opposite offset for the opposite direction. QuantityTypes are supported as well, i.e. one can put an offset of "1°C" on a channel that sends Fahrenheit. In addition there is a new "ChattyThing" fort he magic binding which sends random values on a Number channel and random Strings on a StringType channel based on a configurable interval. For profile testing this turned out to be quite useful. Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
…rofiles. Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
2bae966
to
f838438
Compare
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @triller-telekom looks good overall. Since a lot of the transformation services look similar the comments about logging apply to all of them.
Also, please review your JavaDoc formatter settings since some changes are only from JavaDoc formatting.
import org.mockito.ArgumentCaptor; | ||
|
||
/** | ||
* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a simple description.
<config-description uri="profile:system:offset"> | ||
<parameter name="offset" type="text" required="true"> | ||
<label>Offset</label> | ||
<description>Offset (plain number or number with unit) to be applied on the state towards the item. If positive the negative offset will be applied in the reverse direction.</description> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would drop If positive
and only go with The negative offset ....
. Its not a constraint.
BigDecimal bd = (BigDecimal) paramValue; | ||
try { | ||
offset = new QuantityType<>(bd.toString()); | ||
} catch (IllegalArgumentException e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should always succeed, is it necessary to try/catch here?
if (finalOffset == null) { | ||
logger.error( | ||
"Offset not configured correctly, please make sure it is of type QuantityType, e.g. \"3\", \"-1.4\", \"3.2°C\". Using offset 0 now."); | ||
return new QuantityType<Dimensionless>("0"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error log says "using offset 0 now" but instead "0" is returned.
private Type applyOffset(Type state, boolean towardsItem) { | ||
QuantityType finalOffset = offset; | ||
if (finalOffset == null) { | ||
logger.error( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should only happen when the constructor failed somehow. In this case an error is already logged. I suggest logging warn here.
list.add(quantity); | ||
list.add(offset.quantity); | ||
|
||
final Quantity<T> sum = list.stream().reduce(QuantityFunctions.sum(unit)).get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might also be Arrays.asList(quantity, offset.quantity).stream().reduce(QuantityFunctions.sum(unit)).get();
function = (String) paramFunction; | ||
sourceFormat = (String) paramSource; | ||
} else { | ||
logger.warn("Parameter '{}' and '{}' have to be Strings. Profile will be inactive.", FUNCTION_PARAM, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we log error
here during construction time and warn
later on in onCommandFromHandler
and onStateUpdateFromHandler
?
try { | ||
result = TransformationHelper.transform(service, function, sourceFormat, state.toFullString()); | ||
} catch (TransformationException e) { | ||
logger.debug("Could not transform state '{}' with function '{}' and format '{}'", state, function, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better log this as a warning? is a transformation supposed to work every time? Or is it the default behaviour to not work for every incoming command/state?
<config-description uri="profile:transform:XSLT"> | ||
<parameter name="function" type="text" required="true"> | ||
<label>XSL Filename</label> | ||
<description>XSL Filename in transform folder, containing the transformation expression.</description> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
filename
or better file name
.
String <itemName> { channel="<channelUID>"[profile="transform:XSLT", function="<xsltExpression>", sourceFormat="<valueFormat>"]} | ||
``` | ||
|
||
The XSLT expression to be executed has to be set in the `function` parameter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the config description the function parameter is supposed to hold a filename. Here it should be the XSLT expression...
edit: the XsltTransformationService expects it to be the file name.
Signed-off-by: Stefan Triller <[email protected]>
Signed-off-by: Stefan Triller <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks.
This pull request has been mentioned on openHAB Community. There might be relevant details there: https://community.openhab.org/t/transformationservice-of-type-scale-is-unavailable/47236/9 |
Because the TransformationHelper throws a TransformationException to the caller since PR eclipse-archived#5679, it is now possible to provide a meaningful warning where the transformation is started, i.e. provide the item name. Fixes eclipse-archived#5120 Signed-off-by: Stefan Triller <[email protected]>
…ls (#5900) Because the TransformationHelper throws a TransformationException to the caller since PR #5679, it is now possible to provide a meaningful warning where the transformation is started, i.e. provide the item name. Fixes #5120 Signed-off-by: Stefan Triller <[email protected]>
This PR includes two new profiles.
The TransformationProfile is a new system profile which is registered
under the scope "transform". It offers the possibility to use all
TransformationServices on ChannelItemLinks. The Strings send from a device
will be transformed via the specified function in the "pattern" parameter.
The other direction, i.e. Item to device is left untouched.
The SystemOffsetProfile lives in the "system" scope of profiles and is able
to add or substract and offset from a value send from or to a device. This
profile works in both directions by applying the opposite offset for the
opposite direction. QuantityTypes are supported as well, i.e. one can put
an offset of "1°C" on a channel that sends Fahrenheit.
In addition there is a new "ChattyThing" fort he magic binding which sends
random values on a Number channel and random Strings on a StringType channel
based on a configurable interval. For profile testing this turned out to be
quite useful.
Signed-off-by: Stefan Triller [email protected]