unitsofmeasurement / uom-demos

Units of Measurement Demos
Other
21 stars 9 forks source link

How to create an RPM unit? #101

Open samodadela opened 3 years ago

samodadela commented 3 years ago

Trying to create a new Unit<Frequency> RPM:

    @Test
    void myRpmUnit() throws IOException {
        Unit<Frequency> RPM = AlternateUnit.of(ONE.divide(MINUTE), "rpm").asType(Frequency.class);
        Quantity<Frequency> quantity = Quantities.getQuantity(60, RPM);
        assertThat(quantity.to(HERTZ).getValue().doubleValue()).isEqualTo(1.0);
    }

Specifying alternate as "rpm" I get:

java.lang.IllegalArgumentException: The parent unit: 1/min is not an unscaled SI unit

The RPM unit should have the symbol "rpm" (or "[rpm]"?) instead of 1/min. This should work in serialization and deserialization.

Tried using:

SimpleUnitFormat.getInstance().label(RPM, "rpm");

... but this works only partially: using toString() it writes "rpm", but using Jackson serialization we still get 1/min.

Using indriya 2.0.4

keilw commented 3 years ago

First of all, please use Indriya 2.1.2 if you can, that's the latest version and some of the 2.0.x ones may behave slightly different especially converting due to underlying number systems and precisions. Whoever has time could help contribute further to the uom-guide with examples like this. Please note any of the Jackson libraries use systems-ucum which is relatively self-contained and based on the Unified Code for Units of Measure (UCUM) and as you can see, that does not contain RPM, so we cannot add something that standard does not even contain yet. I will transfer this ticket to uom-guide as an improvement suggestion.

keilw commented 3 years ago

This had to be created here, uom-guide is not compatible with these types of issues or the repository type, but similar to jakartaee-tutorial and jakartaee-tutorial-examples either here (probably makes more sense) or in a separate repository, there could also be concrete examples and demos for the guide book.

keilw commented 3 years ago

UCUM uses the official unit Hertz for frequency, so to handle it with Jackson Binding as it is now (intended to work with APIs in a consistent environment backed by the UCUM catalog) you may have to convert your RPM to HERTZ first. I believe you might like to use ProductUnit for declaring the RPM, that has fewer constraints, AlternateUnit is only for specific cases.

samodadela commented 3 years ago

As regards Indriya versions: I can't use Indriya 2.1.2 because of uom-lib-jackson:

        <dependency>
            <groupId>tech.uom.lib</groupId>
            <artifactId>uom-lib-jackson</artifactId>
            <version>2.0.1</version>
        </dependency>

This one requires:

        <dependency>
            <groupId>tech.units</groupId>
            <artifactId>indriya</artifactId>
            <version>2.0.4</version> <!-- exact version required by uom-lib-jackson 2.0.1 (latest as of 06.05.2020) -->
        </dependency>   

... this is per GH documentation for uom-lib-jackson. I can go as high as Indriya 2.1... above that I get:

java.lang.NoClassDefFoundError: tech/units/indriya/ComparableUnit

    at systems.uom.ucum.format.UCUMFormat.<init>(UCUMFormat.java:206)
    at systems.uom.ucum.format.UCUMFormat$Parsing.<init>(UCUMFormat.java:466)
    at systems.uom.ucum.format.UCUMFormat$Parsing.<clinit>(UCUMFormat.java:461)
    at systems.uom.ucum.format.UCUMFormat.getInstance(UCUMFormat.java:111)
    at tech.uom.lib.jackson.UnitJacksonModule$UnitJsonSerializer.serialize(UnitJacksonModule.java:89)
    at tech.uom.lib.jackson.UnitJacksonModule$UnitJsonSerializer.serialize(UnitJacksonModule.java:69)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
    at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:3058)
    at com.fasterxml.jackson.core.base.GeneratorBase.writeObject(GeneratorBase.java:388)

As regards using ProductUnit... this works but I'd like to change the unit name from "1/min' to 'rpm' if possible. So you are saying that Jackson Binding will use exclusively the UCUM catalog and that since there's no RPM definition, it is not possible to make it serialize RPM as 'rpm'?

keilw commented 3 years ago

That's a minimum version to build it, if your app uses 2.1.2 I am not aware you would get Indriya twice.

As for the UCUM representation I mentioned RPM does not exist in UCUM, that is something we cannot change, they stick to HERTZ. There is currently no way to introduce any new units to UCUM and it would go against what UCUM was created for.

Please file a ticket against https://github.com/unitsofmeasurement/uom-lib/issues (as a "bug" if you want) because normally as you see, there is no problem running it on top of 2.1.2 but we simplified some structures of Indriya between 2.0.4 and 2.1.0 or above (especially with MR1) so the missing ComparableUnit is a result of that. We'll provide a new set of UoM Libraries for that soon.

For the desire to use a different parser, please also file another ticket there (but without label or "new feature") because it is currently based on UCUM for a good reason and you cannot use UCUM with a unit like RPM you must first convert your RPM quantity to HERTZ from the UCUM catalog, then it works fine but it'll parse and print it as "Hz" for a good reason. I cannot promise anything regarding that enhancement request because we have limited resources and such new feature request either could be done by contributing PRs to the respective libraries and modules, e.g. we had a couple of contributions to http://www.uom.systems/ in the past as you can see. For the compatibility problem we'll release a new version soon.

samodadela commented 3 years ago

Thank you very much for your help. I'll wait for the new uom-lib ComparableUnit fix and then upgrade Indriya. As for the RPM issue I'll use Hz or 1/min for now. Hopefully there won't be more units like that.

A general note on (compatible) library versions. It would be nice to have a top-level UOM BOM that would pull in all the compatible versions from the different modules for a release.

keilw commented 3 years ago

We thought about that and uom-lib itself (almost e.g. projects like Eclipse MicroProfile, but not so complex ;-) has more than one version lifecycle, because Indriya among other artifacts uses uom-lib-common while some including the Jackson library depend on a particular version. There may be a better solution here in future to call UCUMFormat as UnitFormat via the SPI and be completely implementation-neutral, but we'd have to see, if it really works or there are problems like the ones you saw with different RI verisions. Currently tagging common differently from the rest works well enough.

Please feel free to suggest a BOM under https://github.com/unitsofmeasurement/uom-parent and its issue tracker, We already used that to control all the detail tasks for the Bintray replacement, so if a new repo was necessary don't worry but that seems the best place for "umbrella" topics.