Simple Datafield crashes on edge device

Hi,

I designed a very simple datafield which works very well on my F6X pro. Nevertheless, the datafield is designed for the edge devices as well. A user complaint about a crash at the very beginning. He told me that the device has no Bluetooth connection yet and then the datafield crashes. If the BT connection is established, the datafield works as well stable. 

The only time I am asking for an info field is in this code area:

	function compute(info) {
    	if (info has :currentSpeed) { // The current speed in meters per second (mps).
        	if (info.currentSpeed != null) {
           		speedValue = info.currentSpeed;
        	}
    	}    	
    	//speedValue = null;
		if (speedValue == null) {
	        speedValue = "---";	
		} else {
			if (splitDecimalPlaces) {
				speedWithFraction = getSplitSpeed(speedValue);
			} else {
				speedValue = getSpeed(speedValue);
			}
		}
	}

Even if I decomment the speedValue = null line the datafield works in my sim. Is there any other means I have to do for edge devices?

  • What do you see in the ciq_log when it crashes?

  • Hi Jim,

    nothing special:

    ---
    Error: Unhandled Exception
    Time: 2020-10-04T19:04:27Z
    Part-Number: 006-B3011-00
    Firmware-Version: '5.20'
    Language-Code: hrv
    ConnectIQ-Version: 3.1.8
    Store-Id: 3751fbbc-a94c-43b1-a4b3-9fbb42430a9f
    Store-Version: 10
    Filename: A9C63708
    Appname: Simle Speed Field
    Stack:
    - pc: 0x100002c3
    - pc: 0x100002d9
    - pc: 0x1000027b
    ---

  • Using the debug.xml you can translate the pc values back to the code. (or that will be done for you if you see it in ERA)

    see forums.garmin.com/.../so-you-have-a-ciq_log-file-but-all-you-see-is-pc-without-a-friendly-stack-trace---what-to-do

  • Wow, pretty cool. Thanks for sharing. Just a further question to your linked post: what is ERA?

    Saying this I was able to find the referenced code and it seems the problem is located here in compute(info):

        	//speedValue = null;
    		if (speedValue == null) {
    	        speedValue = "---";	
    		} else {
    			if (splitDecimalPlaces) {
    				speedWithFraction = getSplitSpeed(speedValue);
    			} else {
    				speedValue = getSpeed(speedValue);
    			}
    		}
    

    I tried before having this knowledge to set the speedValue (which is coming directly from the info field) to null manually. Setting speedValue to null makes no problem in the sim. 

    But having this knowledge from your linked post now tells me, that speedValue isn't null at the time of the crash but it don't seem to be a number either. If I set speedValue to a string manually I can force a crash as well in the sim. 

    Therefore my question: Could it be that the info.currentSpeed field is anything else except a float? The API doc says:

    var currentSpeed ⇒ Toybox.Lang.Float

    The current speed in meters per second (mps).

    Is it possible to cast speedValue anyhow to a float?

  • Here's a blog post about ERA: https://forums.garmin.com/developer/connect-iq/b/news-announcements/posts/exceptional-crash-logging

    If you use Eclipse, it's a selection in the Connect IQ menu.  With it, you can see the crash logs for your apps, where it takes the debug info (included in an iq) and does the pc to line number translation for you.

  • A simple replacement in the code did the trick and the crash is gone:

    // if (speedValue == null) {
    if (!(speedValue instanceof Toybox.Lang.Float)) {

  • I'm not sure that's your final solution.

    Looking at the code you originally submitted, I'd question why, for speed, you're not initialising "speedValue" to a known value right at the top of compute().  I presume it's defined at global or class level.  compute is called every second or so and the risk with those IF blocks you've got at the top is that "speedValue" has an unknown value when it gets to the second lot of IF blocks.  At the least I think you should have some ELSE conditions on the first IF blocks or initialise speedValue right at the top.  Also, what does getSpeed do, because that could be the other cause of the problem (by setting an invalid value that gets picked up on the next compute() call).

    This has always worked for me (Edge and Wearables):

    In compute():

    spd = (info.currentSpeed != null) ? info.currentSpeed * spdConstant : 0.0;

    spdConstant is set during initialisation (to convert to KM or Miles per hr):

    if (appSettings.paceUnits == null || appSettings.paceUnits == Sys.UNIT_METRIC) {

    spdConstant = 3.6;

    } else {

    spdConstant = 2.23694;

    }

  • speedValue ist to 0 in the class's init block. In principal my code does the same as yours: your lese condition (setting spd to 0.0) is my if condition (setting speedValue to ---). I don't want to set it 0.0 in this case since the speed is not 0.0 but unknown. If info.currentSpeed delivers a value which is not null but isn't a float either, your code would lead to the same crash as my code.

    The getSpeed() method computes in principal the same formula as you stated.

  • The instanceof check should fail if speedValue is null or if it is some type other than Float (Number, Long, Double, String, ...). If this indeed fixes the problem, it seems like it would be helpful to know what the type of speedValue is coming back as.

    Also, you should be able to decode the stack (as pointed out by ) and tell what line the app is crashing on. My guess is that it is not crashing in compute() because the depth of the stack shown is 3. If it were crashing in compute(), the depth would be 1.

  • if (splitDecimalPlaces) {
    	speedWithFraction = getSplitSpeed(speedValue);
    } else {
    	speedValue = getSpeed(speedValue);
    }

    It seems that the failure is happening in one of these two functions (getSplitSpeed or getSpeed). The documentation says that currentSpeed is only ever null or Float. I have checked, and those are the only two possible values for that field at the place where it is assigned.

    As mentioned above, you should be able to decode the stack addresses to figure out where the problem is. Another thing you could do would be to wrap that block of code in a try/catch block and see what the exception message (use System.println() and create a log file for the system to write the output to).