On Save and FIT Write Bug?

I write a FIT Summary string at the end of an activity in my Data Field.

I did have it in the onStop() interrupt, and it worked well. But an activity can have multiple stop/start events. So this was writting the summary string multiple times in the FIT file. I think only the last one was rendered in the Garmin Connect activity summary.

But I realized that onReset() was the better place to put this code. In the simulator, this runs when you SAVE or DISCARD an activity. Perfect. It writes once when you are really done with the activity.

But in a REAL device it doesn't actually get written or show up in Garmin Connect.

My theory is that a device buffers the write to be flushed sometime later. But by then the FIT file is closed.

Anyway, this is at least a simulator bug since it doesn't emulate a real device behavior. And/or a H/W bug (it should complete the write following the onReset() interrupt. Or, I'm not thinking about this correctly.

Easy fix - put that FIT write back into the onStop() function.

If this is a bug, I'll post in the bug forum - just not sure it is yet. Thoughts?

  • ok I'll leave my session setData() in onTimerStop() and see if there is ever a missing string. I bet you're right. thx!

    One thing I noticed is that you keep referring to things like onTimerStop() as "interrupts", which may be part of why you're uncertain whether it's ok call setData() in onTimerStop(). onTimerStop() is more of an "event" (or callback) than an "interrupt".

    My understanding of Monkey C/Connect IQ is that it's single threaded and event-based (kind of like javascript), not multi-threaded or interrupt based.

    So my understanding is that there's no need to worry that you'll try to call setData() in onTimerStop() and that it'll somehow be interrupted or pre-empted by the save/finalize FIT file action.

    That's why I would really try to avoid thinking of it in terms of things like "interrupts", "buffer" and "flush" because it may not be an accurate model of how things work.

  • I call it an interrupt because when you push the lap button (for example), it interrupts the normal execution flow and calls the onTimerLap() handler, then returns to the normal execution flow, right? But to your point, maybe that is confusing things.

    I call it a "flush" because as I understand F/W internals, we populate (set) an internal "write" buffer, and at some point the F/W decides it will flush that buffer by calling a write to the file.

    To Jim's point that this "flush or write" will occur after we hit STOP but before SAVE.... I don't think that is true. You've proven that hitting STOP (where the setData() is in the onTimerStop() function) does not actually cause the F/W to force a write, ever. You've performed several STOPs and didn't see it in the FIT file.

    So the trigger for the F/W to finally "flush" the SESSION write buffer to the FIT file must occur when the user hits SAVE. Then, the last setData() that wrote a string into the SESSION write buffer is saved to file by the F/W.

    ===

    All the evidence seems to point to that behavior. The terms interrupt, buffer, and flush might be confusing but this explains what I mean by those terms.

    ===

    If so, it seems the SAVE action causes the F/W to finally write the SESSION string to the FIT file. THEN it triggers the CIQ onTimerReset() interrupt. That is why when we setData() from onTimerReset(), the F/W ignores the string we placed in the buffer at that point. It already wrote it to the FIT file. They should have waited to flush the SESSION string to the FIT file until after it triggered the CIQ onTimerReset(). Then our setData() write in that function would end up in the file. But we can work around this by writing it every time STOP is called.

  • I call it an interrupt because when you push the lap button (for example), it interrupts the normal execution flow and calls the onTimerLap() handler, then returns to the normal execution flow, right? But to your point, maybe that is confusing things.

    I wouldn't call it an interrupt because *in the context of your app*, there's no normal execution flow that's being interrupted. It's even possible that in the overall context of the Garmin firmware, there's no interrupt.

    If you have a data field app with with onTimerLap(), compute() and onUpdate(), all of those functions are going to run synchronously. They don't interrupt each other. That's what I meant when I said CIQ is single threaded and event based.

    If you noticed, nowhere in the CIQ documentation are any of these things referred to as "interrupts".

    For an analogy of how this might work, see the javascript implementation in web browsers. Everything is single-threaded and event-based. (UI events are handled via callbacks and an event loop).

    I call it a "flush" because as I understand F/W internals, we populate (set) an internal "write" buffer, and at some point the F/W decides it will flush that buffer by calling a write to the file.

    Sure, you can think of it as a buffer, as long as you keep in mind it's a buffer of size 1. Usually that's not the case in other contexts. Notice how the document just refers to setData() as setting a (single) value.

    To Jim's point that this "flush or write" will occur after we hit STOP but before SAVE

    I don't think that's what he said or implied. That wouldn't make any sense, given the fact that session data is only ever written once and the firmware cannot predict the future (after pausing an activity, the user can unpause it, save it or discard it, and the firmware would have to know which one of those things you are going to do in advance, if it would write session data before save and also never write session data more than once.)

    If so, it seems the SAVE action causes the F/W to finally write the SESSION string to the FIT file. THEN it triggers the CIQ onTimerReset() interrupt.

    Yeah. Again, there's nothing in the documentation which implies what order this happens, unfortunately. Which is why the way it works may be unexpected, but I wouldn't call it a "bug".