Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

TransformationProfile and SystemOffsetProfile #5679

Merged
merged 20 commits into from
Jun 27, 2018

Conversation

triller-telekom
Copy link
Contributor

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]

@triller-telekom
Copy link
Contributor Author

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.
However, a user might not know this type or this type is changed in a future version of the binding and this will create confusing results. So we mighty only want to allow offsets without a unit if the binding only sends plain Number states without units.

@triller-telekom triller-telekom force-pushed the offsetProfile branch 4 times, most recently from d578c89 to dc2b836 Compare June 5, 2018 11:40
sjsf
sjsf previously requested changes Jun 5, 2018
@NonNullByDefault
public class SystemOffsetProfile implements StateProfile {

private static Logger logger = LoggerFactory.getLogger(SystemOffsetProfile.class);
Copy link
Contributor

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);
Copy link
Contributor

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.

Copy link
Contributor Author

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"?

Copy link
Contributor

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);
Copy link
Contributor

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);
Copy link
Contributor

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);
Copy link
Contributor

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.
Copy link
Contributor

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.
Copy link
Contributor

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.
Copy link
Contributor

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>"]}
Copy link
Contributor

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>"]}
Copy link
Contributor

Choose a reason for hiding this comment

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

see above

@triller-telekom triller-telekom changed the title WIP: TransformationProfile and SystemOffsetProfile TransformationProfile and SystemOffsetProfile Jun 7, 2018
@triller-telekom
Copy link
Contributor Author

I have removed the WIP because for backward compatibility I now allow offsets of type DecimalType on states of type QuantityType, but log a warning that the user should check his setup.

@triller-telekom triller-telekom changed the title TransformationProfile and SystemOffsetProfile WIP: TransformationProfile and SystemOffsetProfile Jun 15, 2018
@triller-telekom
Copy link
Contributor Author

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 "Window [MAP(window_esp.map):%s]" in their state descriptions for example. The question is whether the formatting of the state before the transformation (the %s) is useful to the profiles or not. I have implemented it in the TransformationProfile but i am not sure whether we only should transform the raw state without any possibility to format the state before it will be transformed.

I have also added configdescriptions for the transformation profiles.

WDYT?

@triller-telekom
Copy link
Contributor Author

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".

@triller-telekom triller-telekom changed the title WIP: TransformationProfile and SystemOffsetProfile TransformationProfile and SystemOffsetProfile Jun 19, 2018
@triller-telekom
Copy link
Contributor Author

This PR is now ready for review.

@triller-telekom
Copy link
Contributor Author

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 TransformationServices, but instead each TransformationService should provide its own factory and profile(s).

An update will follow.

@triller-telekom triller-telekom changed the title TransformationProfile and SystemOffsetProfile WIP: TransformationProfile and SystemOffsetProfile Jun 21, 2018
@htreu htreu mentioned this pull request Jun 21, 2018
5 tasks
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]>
Copy link
Contributor

@htreu htreu left a 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;

/**
*
Copy link
Contributor

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>
Copy link
Contributor

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) {
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 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");
Copy link
Contributor

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(
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 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();
Copy link
Contributor

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,
Copy link
Contributor

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,
Copy link
Contributor

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>
Copy link
Contributor

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.
Copy link
Contributor

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.

@triller-telekom triller-telekom changed the title WIP: TransformationProfile and SystemOffsetProfile TransformationProfile and SystemOffsetProfile Jun 27, 2018
Copy link
Contributor

@htreu htreu left a comment

Choose a reason for hiding this comment

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

LGTM, thanks.

@kaikreuzer kaikreuzer dismissed sjsf’s stale review June 27, 2018 09:26

comments have been addressed

@kaikreuzer kaikreuzer merged commit cf01b86 into eclipse-archived:master Jun 27, 2018
@triller-telekom triller-telekom deleted the offsetProfile branch June 27, 2018 10:35
@openhab-bot
Copy link
Contributor

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

triller-telekom added a commit to triller-telekom/smarthome that referenced this pull request Jul 12, 2018
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]>
maggu2810 pushed a commit that referenced this pull request Jul 12, 2018
…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]>
@htreu htreu added this to the 0.10.0 milestone Oct 30, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants