Using the C++ SDK, how do I decode and re encode all messages except some Activity records which need correction

Has anyone worked with the C++ SDK enough to explain MesgListeners to me?

It appears that I can AddListener an Encode object which implements MesgListener to the MesgBroadcaster which I pass to Decode::Read.

Since Encode::OnMesg just contains Write(mesg), this causes all recognized decoded messages to be re encoded to the output file.

In addition, I would like Decode to call the OnMesg of a special RecordMesgListener for record messages rather than Encode::OnMesg so I can correct some records.

However, when I AddLIstener a stub RecordMesgListener to the MesgBroadcaster which just does Write(mesg) like Encode::OnMesg, I do not see identical output  as I would expect.

Instead, I get an output that has some additional (but not duplicates of all) records and other corruption and a bad CRC.

Why is this happening?

Here's my Recode function:

int Recode(Listener& listener, fit::MesgBroadcaster& mesgBroadcaster, char* filename)
{
    // Open file to decode
    fit::Decode decode;
    fstream inFile;

    inFile.open(filename, ios::in | ios::binary);

    if (!inFile.is_open())
    {
        printf("Error opening file %s\n", filename);
        return -1;
    }

    if (!decode.CheckIntegrity(inFile))
    {
        printf("FIT file integrity failed.\nAttempting to decode...\n");
    }

    // Open the file to encode
    std::fstream outFile;
    outFile.open("badFileRecode.fit", std::ios::in | std::ios::out | std::ios::binary | std::ios::trunc);

    if (!outFile.is_open())
    {
        printf("Error opening file badFileRecode.fit\n");
        return -1;
    }

    // Create a FIT Encode object
    fit::Encode encode(fit::ProtocolVersion::V20);

    try
    {
        // Write the FIT header to the output stream
        encode.Open(outFile);
        // Allow the RecordMesgListener to encode after fixes
        listener.encode = encode;
        // Other decoded messages are written by encode without changes
        mesgBroadcaster.AddListener((fit::MesgListener&)encode);


        decode.Read(inFile, mesgBroadcaster);
    }
    catch (const fit::RuntimeException& e)
    {
        printf("Exception decoding file: %s\n", e.what());
        return -1;
    }
    catch (...)
    {
        printf("Exception decoding file");
        return -1;
    }


    try {
        // Update the data size in the header and calculate the CRC
        if (!encode.Close())
        {
            printf("Error closing encode.\n");
            return -1;
        }

        // Close the file
        outFile.close();

        printf("Encoded FIT file badFileRecode.fit.\n");
        return 0;
    }
    catch (...)
    {
        throw std::exception("Exception encoding activity file");
    }
}

Here's the Listener specific to RecordMesg

class Listener
    : public fit::RecordMesgListener
{
public:
    fit::Encode encode;

    void OnMesg( fit::RecordMesg& record ) override
    {
        encode.Write(record);
    }
};

  • I don't see a way to edit my question.

    I see now that MesgBroadcaster::OnMesg calls generic mesgListeners first and then also calls message specific listeners.

    That doesn't explain why I don't get duplicates of all record messages.

    And, this ordering means that I would need to use a custom generic listener which checks that the message is NOT a record before it calls Write.

    That seems too ugly to be the intended usage.

  • I only took a quick look, but one suggestion is to place your repair "filter" in between the decoder and the encoder. The filter would be a generic mesg listener and would pass through the non-Record messages. Then as needed it wouldrepair the Record message before passing them on. The messages would go to the encoder in the same order that they were decoded.

    You can see a similar pattern here. The filter in this tool buffers all of the decoded messages, repairs things, and then broadcasts the messages to the encoder.
    github.com/.../repair

  • Thanks Ben,

    I'll give "filtering" with a generic listener a try and let you know.

    Also, that example could prove useful although my Java is a bit rusty.

  • Great, adding a single FilterListener to the MesgBroadcaster which I pass to Decode seems to decode and re encode all the recognized messages. For now, I'm not making the fix but just trying to get back what I read in. Strangely, FIT File Viewer tells me that my output file is "invalid" although the fields it displays look just like my test input file. So, there is still some tweaking to do. But, the FilterListener solves this problem.

    class FilterListener
        : public fit::MesgListener
    {
    public:
        fit::Encode encode;
        void OnMesg(fit::Mesg& mesg)
        {
            if (mesg.GetNum() == FIT_MESG_NUM_RECORD) {
                // TODO: Rewrite mesg to include fixed heartrate values
                encode.Write(mesg);
            }
            else {
                encode.Write(mesg);
            }
        }
    };