Problem with Attention module and VivoActive4

I have a datafield that uses the Attention module, it plays various tones to alert the user to certain periods of time having elapsed.  I use a The code to play the tones is protected by a check and I use a Dictionary to link the tone to play to the type of alert.  The code looks like this:

// The tones associated with each alert type
const AlertToneDict as Dictionary<AlertTypeEnum, Attention.Tone> = {
	ThirtyMin => Attention.TONE_INTERVAL_ALERT,
    FiveMin   => Attention.TONE_ALERT_LO,
    OneMin    => Attention.TONE_ALERT_HI,
    TimesUp   => Attention.TONE_CANARY,
    PointLost => Attention.TONE_LOUD_BEEP,
    TimedOut  => Attention.TONE_FAILURE	 
} as Dictionary<AlertTypeEnum, Attention.Tone> ;


// Play the alert for this event
function playMBOAlert(eventType as AlertTypeEnum) as Void {
    if (Attention has :playTone) {
		var tone = AlertToneDict[eventType] as Attention.Tone;
        Attention.playTone(tone) ;
    }
}

However I'm getting ERA Crash reports for the VivoActive4 device and when I run the datafield for this device in the simulator I can reproduce the issue:

Error: Symbol Not Found Error
Details: Could not find symbol 'TONE_INTERVAL_ALERT'
Stack: 
  - <init>() at C:\GarminDevelopment\repos\MBO\MBOCountdown\source\MBOCountdownCommon.mc:23 0x10000837 
  - 
Native code0x80000000 

So it looks to me as if the Tone constants aren't defined for the VivoActive4. 

I'm thinking of replacing the Dictionary with a function that gets called inside the check for tone support (or maybe just dropping VivoActive4 support) but is there a better way to handle this. 

It also looks as if jungles might be a way to do this - factor all the tone related code into it's own file and provide a stub that's included for devices that don't support the tones.

  • The va4 doesn't support tones.  None of the va or venu devices do.  You want to use something like a has check for playTone.

  • The problem is that the error is coming from a declaration and I can't put a has :playTone check around it.  The code that actually tries to play the tone already includes this check but I don't see how to put a guard around the declaration of a const.

  • A simple way to do this is to use the values instead of the Attention.TONE_* names.

    So instead of Attention.TONE_START, use the number 1 for example

  • It might be a simple way to solve the issue but it just feels slightly dirty Nerd I've spent a lot of my life yelling at developers (well asking politely really) about the evil of magic numbers and the benefits of a well named constant.

  • The numbers won't change in this case, you might save a few bytes of memory, and it's not as much work to maintain as if you used jungle.  This is one of the few cases where I'll use numbers to simplify things, and you can always have the TONE_* names as a comment for readability

  • Well, as you said, you could replace the dictionary with a function. The function could just return the whole dictionary, or you could implement the same logic with a bunch of if-statements (which you'll find will save a lot of memory compared to a dictionary, which is very expensive.)

    In general you'll find that writing code the "right way" in Monkey C is very memory-intensive (compared to the meager amount of available memory for devices). This is more of an issue for older devices, but it's still relevant imo, especially since the Vivoactive 4 only has 32 KB for data fields, which is the same as old devices such as 735XT.

    The problem is that the error is coming from a declaration and I can't put a has :playTone check around it.  The code that actually tries to play the tone already includes this check but I don't see how to put a guard around the declaration of a const.

    Does this work?

    const AlertToneDict as Dictionary<AlertTypeEnum, Attention.Tone> =
        (Attention has: playTone)
        ?
        {
            ThirtyMin => Attention.TONE_INTERVAL_ALERT,
            FiveMin   => Attention.TONE_ALERT_LO,
            OneMin    => Attention.TONE_ALERT_HI,
            TimesUp   => Attention.TONE_CANARY,
            PointLost => Attention.TONE_LOUD_BEEP,
            TimedOut  => Attention.TONE_FAILURE
        } as Dictionary<AlertTypeEnum, Attention.Tone>
        : {};

  • In the end I've removed the dictionary and replaced it with a case statement in the playMBOAlert function that was the only place that used the dictionary.  The references to Attention are now safely within a has: playTone check and it compiles and runs on VivoActive4.  Testing and re-release time.

  • Cool. Just FYI, a series of if-statements takes up less memory (in code) than the equivalent case statement in Monkey C.