Conversion form HEX string to Float32 and Signed Int16

  • Platform information:

    • Hardware: CPUArchitecture/RAM/storage
    • OS: Windows 7 64bit
    • Java Runtime Environment: Zulu8
    • openHAB version: 2.5
  • Issue of the topic: I am currently implementing into my openHAB configuration industrial flowmeter IFM SM8100 (SM8100 - Magnetic-inductive flow meter - ifm electronic). I am getting data with http binding using JSONPATH trasformation. The transformed data are a hex string of 16 bytes like “42C3DC29FFDE060D”. First 8 bytes represent Totalizer in Float32 format. Next 4 bytes stand for Flow (signed int16) and last 4 bytes represent Temperature (signed int16). Unfortunately both cases - HEX-to-Float32 and HEX-to-Int16 - are missing in the great tutorial Type Conversions of @rlkoshak.
    I’m not a java programmer and did some research. I found simple and effective solution for both convertions, but only HEX-to-Float32 works at the moment. Here are the code parts.

  • Items:

String Port2 "Port2 Process Data [%s]" {http="<[iolinlkmasterPort2:1000:JSONPATH($.data.value)]"}

Number:Volume Port2_Totalizer "Totalizer Volume: [%.2f L]" <cistern> 

Number:VolumetricFlowRate Port2_Flow "Flow Rate: [%.1f L/min]" <flow> 

Number:Temperature Port2_Temperature "Temperature [%.1f °C]" <temperature>
  • Sitemap:
sitemap IFM label="IFM Devices" {

Text item=Port2 label="Port2 Raw Process Data [%s]"

Text item=Port2_Totalizer

Text item=Port2_Flow

Text item=Port2_Temperature

}
  • Rules:
import org.apache.commons.lang.StringUtils

rule "Port2 Temperature" 

when     

    Item Port2 received update or

    System started

then

    val Port2AsString = Port2.state.toString

    //Right 4 bytes represent Temperature

    val Port2_Temp_Bytes = StringUtils.right(Port2AsString, 4)

    //Left 8 Bytes represent Totalizer

    val Port2_Totalizer_Bytes = StringUtils.left(Port2AsString, 8)

    //Next 4 Bytes to right contain Flow

    val Port2_Flow_Bytes = StringUtils.substring(Port2AsString,8,12)

    logInfo(">>> Port2 Temperature Raw<<<", Port2AsString)

    logInfo(">>> Port2 Temperature Bytes<<<", Port2_Temp_Bytes)

    logInfo(">>> Port2 Flow Bytes<<<", Port2_Flow_Bytes)

    logInfo(">>> Port2 Totalizer Bytes<<<", Port2_Totalizer_Bytes)

    //Conversion HEX-to-Int16

    var MyTemp = (short) Integer.parseInt(Port2_Temp_Bytes, 16) as Number

    var MyFlow = (short) Integer.parseInt(Port2_Flow_Bytes, 16) as Number

    //Conversion HEX-to-Float32

    var i = Long.parseLong(Port2_Totalizer_Bytes, 16) as Number

    var MyTotalizer = Float.intBitsToFloat(i.intValue()) as Number

    //

    Port2_Temperature.postUpdate(MyTemp)

    Port2_Flow.postUpdate(MyFlow)

    Port2_Totalizer.postUpdate(MyTotalizer)

end
  • Services configuration:
#IFM

iolinlkmasterPort2.url=http://192.168.0.200/iolinkmaster/port%5B2%5D/iolinkdevice/pdin/getdata

iolinlkmasterPort2.updateInterval=1000

Evidently var MyTemp = (short) Integer.parseInt(Port2_Temp_Bytes, 16) as Number is not working as Visual Studio shows message “Type mismatch: cannot convert from Class to String”. What should I change to get a correct result? I tested the original code:

   short s = (short) Integer.parseInt(myString,16);
    System.out.println(s);

in “Online Java Compiler - Online Java Editor - Online Java IDE - Java Coding Online - Practice Java Online - Execute Java Online - Compile Java Online - Run Java Online” and it worked fine.

Why parse to an int only to force it to a short? Use Short.parseShort in the first place and no conversion is necessary.

I’m pretty sure the as Number is going to cause some problems. parseInt returns a primitive int. Number is a type for an Object. You can’t just say “I know you are a primitive but now I want to use you like an Object.” If you really want the result to be a Number Object use getShort instead of parseShort. That will return a Short Object which implements Number so you can cast it to as Number if you want to.

Look Rich @rlkoshak , Short.parseShort does not accept values higher than 0x8000. In my case I get negative values as well because temperatures can be negative and flow can have opposite direction. In my example “42C3DC29FFDE060D” the flow value is negative and represented by “FFDE”. So Short.parseShort goes out of the permitted range. Is it the same range with getShort?

Most likely. short is a 16 bit signed twos compliment integer. Java doesn’t have unsigned shorts.

Thanks for the tip Rich @rlkoshak , the solution appeared to be the following:

var MyFlow = Integer.parseInt(Port2_Flow_Bytes, 16).shortValue() as Number

I reckon it might be useful to add this two conversions in your tutorial for stupid guys like me.

You still do not need the as Number and in some cases that will cause problems. shortValue() returns a primitive short, not an Object of type Number. You can’t just cast it to become one.

A lot of times though Rules DSL will convert numbers to BigDecimal behind the scenes which might be happening here and is why you don’t see a problem. But don’t count on that always working in all cases.

Sorry Rich @rlkoshak , when I took off as Number type it stopped working: with negative value always gives 0.