Issue of the topic: I am trying to use Units of Measure (UoM) in Rules. I am able to apply the UOM to the variables I create with just a number, but have been unable to apply the UoM to a calculated variable.
The rule works fine w/o UoM, but it is awkward because I have to remember that the calculated results is in meters, and I would like to be able to leave the UOM conversion to the computer.
Here is the Rules code w/o UoM:
val Number radius=75 //
val Number FarFromHomeRadius= 482803 //300 mi in meters
val Number EmilyFarFromMidwayRadius=32187 // 20 mi in meters
// val PointType home_location = new PointType(new DecimalType(30.093643), new DecimalType(-95.337313)) //Spring Home 30.095020701729204, -95.3372609550871
val PointType home_location = new PointType(new DecimalType(42.535647135832804), new DecimalType(-115.47038000151313))
val String Home = "Midway"
rule "George iPhone Home"
when Item GeorgeiPhoneIPhone8_Location changed
then
val PointType Gphone_location = GeorgeiPhoneIPhone8_Location.state as PointType
val Number Gdistance = Gphone_location.distanceFrom(home_location).intValue()
GeorgeDistanceFromHome.postUpdate(Gdistance) //Temporary while learning
//logInfo("George Distance", GeorgeDistanceFromHome.state)
if ( Gdistance <= radius && George_iPhone_Home.state != ON ) {
George_iPhone_Home.postUpdate(ON)
logInfo("iPhone Home", "George's iPhone is at " + Home + " home.")
}
else {
if (Gdistance > radius && George_iPhone_Home.state != OFF ) {
George_iPhone_Home.postUpdate(OFF)
logInfo("iPhone Home", "George's iPhone is away from " + Home + "home.")
}
if ((Gdistance > FarFromHomeRadius) && (George_Recently_Present_But_Now_Absent.state != ON)) {George_Recently_Present_But_Now_Absent.postUpdate(OFF)}
}
end
Now I try to add UoM:
val Number radius=75 | m
val Number FarFromHomeRadius= 300 | mi
val Number EmilyFarFromMidwayRadius= 20 | mi
so far so good. But I have been unable to figure out how to add UoM to the calculate distance:
val Number Gdistance = Gphone_location.distanceFrom(home_location).intValue()
Just adding | m at the end yields āno viable alternativeā in VSCode. I have tried various conversions and castings, but I donāt really know what Iām doing, and nothing worked.
I searched through the community:
I would appreciate assistance in figuring out how to make this work.
I would for a start, stop casting things as Number. While a Number can hold a Quantity (value & units) it is not a fully featured Quantity type with auto conversions etc.
So just var fred = 22.5 | mm
and let the interpreter sort it out.
Doesnāt distanceFrom() return a Quantity anyway?
Thanks for the suggestions. I tried removing the casting.
val radius=75 | m //reduced from 100 to 75. See if we get any false flags, and if it goes off at Stirlinds
val FarFromHomeRadius= 300 | mi //300 mi in meters
val Number EmilyFarFromMidwayRadius= 20 | mi // 20 mi in meters
val PointType home_location = new PointType(new DecimalType(40.535647135832804), new DecimalType(-114.47038000151313)) //Midway Home 45.535647135832804, -111.47038000151313
val String Home = "Midway"
rule "George iPhone Home"
when Item GeorgeiPhoneIPhone8_Location changed
then
val PointType Gphone_location = GeorgeiPhoneIPhone8_Location.state as PointType
val Gdistance = Gphone_location.distanceFrom(home_location)
//GeorgeDistanceFromHome.postUpdate(Gdistance) //Temporary while learning
//logInfo("George Distance", GeorgeDistanceFromHome.state)
if ( Gdistance <= radius && George_iPhone_Home.state != ON ) {
George_iPhone_Home.postUpdate(ON)
logInfo("iPhone Home", "George's iPhone is at " + Home + " home.")
}
else {
if (Gdistance > radius && George_iPhone_Home.state != OFF ) {
George_iPhone_Home.postUpdate(OFF)
logInfo("iPhone Home", "George's iPhone is away from " + Home + "home.")
}
if ((Gdistance > FarFromHomeRadius) && (George_Recently_Present_But_Now_Absent.state != ON)) {George_Recently_Present_But_Now_Absent.postUpdate(OFF)}
}
end
I get the following message in VSCode
Ambiguous binary operation.
The operator declarations
operator_greaterThan(Type, Number) in NumberExtensions and
operator_greaterThan(Number, Number) in NumberExtensions
both match.
This is in reference to a comparison:
if (Gdistance > radius && George_iPhone_Home.state != OFF ) {
So either the radius needs to be returned to be just a number with an assumed units of m or the distanceFrom needs to be converted to a QuantityType.
To do the latter itās as simple as
new QuantityType(Gphone_location.distanceFrom(home_location).toString + " m")
It would be nice though if distanceFrom did return a QuantityType, though that would probably break a bunch of peopleās rules. Maybe an alternative function could be addedā¦
val Gdistance = new QuantityType(Gphone_location.distanceFrom(home_location).toString + " m")
I get this message in VSCode. Iām sure the problem is that I donāt know what Iām doing. If only it were FORTRAN, Iām sure I would know what to do. In other words Iām probably your grandfatherās age, but still trying to learn new things.
Bounds mismatch: The type argument <Object> is not a valid substitute for the bounded type parameter <T extends Quantity<T>> of the constructor QuantityType<T>(String)
I suspect that rossko57 and I are both significantly older than you would guess.
That error is surprising. The JavaDocs for QuantityType implies that supplying a type is not required.
But this is a relatively unused part of OH and maybe the Language Service Processor (the part of OH that checks the syntax of Rules DSL) is simply confused. Did you try to run it?
We could supply the type but the JavaDocs really indicate thatās not necessary.
new QuantityType<javax.measure.quantity.Length>(Gphone_location.distanceFrom(...
As you suspected, the rule appears to be working fine notwithstanding the LSP message. It there somewhere I should report that, or should I just be happy that yāall helped me solve my problem and move on. I went ahead and added the <javax.measure.quantity.Length> which stopped the LSP message. I donāt want to get into the habit of ignoring messages because I donāt know enough to know which ones matter, and I might miss something that is significant.
var banana = new QuantityType("23.0 mm")
logInfo("test", "banana {}", banana) // log banana 23.0 mm
// VSCode opaquely complains about Bounds Mismatch
// that was a dirty generic Quantity anyway
// this would be more 'correct', properly typed
var grape = new QuantityType<Length>("2.0 km")
logInfo("test", "grape {}", grape) // log grape 2.0 km
// VSCode complains 'Length cannot be resolved ...'
// which is at least more easily understood (and ignored)
// by extension then we can do this of course
var apple = 3.1417
var orange = new QuantityType<Length>(apple.toString + "mm" )
logInfo("test", "orange {}", orange) // log orange 3.1417 mm
// and tolerate the VSC complaint
There should also be a non-string-parsing variation like new QuantityType<Length>( Number, Unit )
but I canāt crack the secret to that in DSL, going to be SIUnit.Millimtre or something like.
This should all be commonplace, but as Rich says it doesnāt crop up very often in practice, I suppose constants are more often used than tacking units on a numeric variable.
Thanks. I posted a link on the GitHub site you referenced. Not a big deal now that I understand, but if they are updating things, something to keep in mind.