All Downloads are FREE. Search and download functionalities are using the official Maven repository.

plugins.ActivityFileValidationPlugin Maven / Gradle / Ivy

There is a newer version: 21.141.0
Show newest version
/////////////////////////////////////////////////////////////////////////////////////////////
// Copyright 2023 Garmin International, Inc.
// Licensed under the Flexible and Interoperable Data Transfer (FIT) Protocol License; you
// may not use this file except in compliance with the Flexible and Interoperable Data
// Transfer (FIT) Protocol License.
/////////////////////////////////////////////////////////////////////////////////////////////
// ****WARNING****  This file is auto-generated!  Do NOT edit this file.
// Profile Version = 21.120Release
// Tag = production/release/21.120.00-0-g2d77811
/////////////////////////////////////////////////////////////////////////////////////////////


package com.garmin.fit.plugins;

import com.garmin.fit.ActivityMesg;
import com.garmin.fit.DateTime;
import com.garmin.fit.DeviceIndex;
import com.garmin.fit.DeviceInfoMesg;
import com.garmin.fit.File;
import com.garmin.fit.FileIdMesg;
import com.garmin.fit.Fit;
import com.garmin.fit.FitListener;
import com.garmin.fit.FitMessages;
import com.garmin.fit.LapMesg;
import com.garmin.fit.Manufacturer;
import com.garmin.fit.Mesg;
import com.garmin.fit.MesgBroadcastPlugin;
import com.garmin.fit.RecordMesg;
import com.garmin.fit.SessionMesg;
import com.garmin.fit.plugins.ActivityFileValidationResult.Level;
import com.garmin.fit.plugins.ActivityFileValidationResult.Status;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class ActivityFileValidationPlugin implements MesgBroadcastPlugin {

    private static final int MIN_TIMEZONE_OFFSET_IN_SECONDS = -43200; // -12 hours
    private static final int MAX_TIMEZONE_OFFSET_IN_SECONDS = 50400; // +14 hours

    private final FitListener fitListener;
    private final ArrayList results;
    private int mesgCount;

    public ActivityFileValidationPlugin() {
        fitListener = new FitListener();
        results = new ArrayList<>();
        mesgCount = 0;
    }

    @Override
    public void onIncomingMesg(Mesg mesg) {
        fitListener.onMesg(mesg);
        mesgCount++;
    }

    @Override
    public void onBroadcast(List mesgs) {
        FitMessages fitMessages = fitListener.getFitMessages();

        results.clear();

        // FileId Message Checks
        results.add(checkFileIdMesgExists(fitMessages.getFileIdMesgs()));

        if (fitMessages.getFileIdMesgs().size() > 0) {
            results.add(checkFileIdMesgIsFirst(mesgs));
            results.add(checkFileIdMesgType(fitMessages.getFileIdMesgs().get(0)));
            results.add(checkFileIdMesgManufacturerId(fitMessages.getFileIdMesgs().get(0)));
            results.add(checkFileIdMesgTimeCreated(fitMessages.getFileIdMesgs().get(0)));
        }

        // Activity Message Checks
        results.add(checkActivityMesgExists(fitMessages.getActivityMesgs()));

        if (fitMessages.getActivityMesgs().size() > 0) {
            results.add(checkActivityMesgTimestamp(fitMessages.getActivityMesgs().get(0)));
            results.add(checkActivityMesgLocalTimeStamp(fitMessages.getActivityMesgs().get(0)));
            results.add(checkActivityMesgTotalTimerTime(fitMessages.getActivityMesgs().get(0), fitMessages.getSessionMesgs()));
            results.add(checkActivityMesgSessionCount(fitMessages.getActivityMesgs().get(0), fitMessages.getSessionMesgs().size()));
        }

        // Session Message Checks
        results.add(checkSessionMesgsExists(fitMessages.getSessionMesgs().size()));
        results.add(checkSessionMesgTimestamp(fitMessages.getSessionMesgs()));
        results.add(checkSessionMesgStartTime(fitMessages.getSessionMesgs()));
        results.add(checkSessionMesgTotalTimerTimeAndTotalElapsedTime(fitMessages.getSessionMesgs()));
        results.add(checkSessionMesgFirstLapIndexAndNumLapsAreSequentialAndAbut(fitMessages.getSessionMesgs(), fitMessages.getLapMesgs()));
        results.add(checkSessionMesgTotalTimerTime(fitMessages.getSessionMesgs(), fitMessages.getLapMesgs()));
        results.add(checkSessionMesgTotalElapsedTime(fitMessages.getSessionMesgs(), fitMessages.getLapMesgs()));
        results.add(checkSessionMesgSport(fitMessages.getSessionMesgs()));
        results.add(checkSessionMesgSubSport(fitMessages.getSessionMesgs()));
        results.add(checkSessionMesgsAreSequentialAndAbut(fitMessages.getSessionMesgs()));
        results.add(checkSessionMesgValidMesgIndexes(fitMessages.getSessionMesgs()));

        // Lap Message Checks
        results.add(checkLapMesgsExists(fitMessages.getLapMesgs().size()));
        results.add(checkLapMesgValidMesgIndexes(fitMessages.getLapMesgs()));
        results.add(checkLapMesgValidStartTimeAndTimeStamp(fitMessages.getSessionMesgs(), fitMessages.getLapMesgs()));
        results.add(checkLapMesgsAreSequentialAndAbut(fitMessages.getLapMesgs()));

        //Record Message Checks
        results.add(checkRecordMesgTimestampsAgainstSessionMesgTimes(fitMessages.getSessionMesgs(), fitMessages.getRecordMesgs()));
        results.add(checkRecordMesgsAreChronological(fitMessages.getRecordMesgs()));

        //Device Info Message Checks
        results.add(checkDeviceInfoMesgValidTimestamp(fitMessages.getDeviceInfoMesgs()));
        results.add(checkDeviceInfoMesgValidDeviceIndex(fitMessages.getDeviceInfoMesgs()));
        results.add(checkDeviceInfoMesgValidManufacturerIdExists(fitMessages.getDeviceInfoMesgs()));
    }

    /**
     * Repeat message validation by passing an empty message list to onBroadcast().
     * Some checks may be skipped or fail since the message list is empty. In normal
     * circumstances, this method does not need to called directly.
     */
    public void repeatValidation() {
        onBroadcast(new ArrayList());
    }

    public int getMesgCount() {
        return mesgCount;
    }

    public List getResults() {
        return Collections.unmodifiableList(results);
    }

    ActivityFileValidationResult checkFileIdMesgExists(List fileIdMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("FileId Message Exists", Level.REQUIRED);

        if (fileIdMesgs.size() > 0) {
            result.setStatus(Status.PASSED);
        } else {
            result.setStatus(Status.FAILED);
        }
        return result;
    }

    ActivityFileValidationResult checkFileIdMesgIsFirst(List mesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("FileId Message Is First", Level.REQUIRED);

        for (Mesg mesg : mesgs) {
            if (mesg.getName().equals("pad")) {
                continue;
            } else if (mesg.getName().equals("file_id")) {
                result.setStatus(Status.PASSED);
                break;
            } else {
                result.setStatus(Status.FAILED);
                break;
            }
        }
        return result;
    }

    ActivityFileValidationResult checkFileIdMesgType(FileIdMesg fileIdMesg) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("FileId Message Type Is Activity", Level.REQUIRED);

        if (fileIdMesg.getType() == File.ACTIVITY) {
            result.setStatus(Status.PASSED);
        } else {
            result.setStatus(Status.FAILED);
            result.setDescription("Expected Type: Activity, actual Type: " + fileIdMesg.getType() + ".");
        }
        return result;
    }

    ActivityFileValidationResult checkFileIdMesgManufacturerId(FileIdMesg fileIdMesg) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("FileId Message Manufacturer Id Exists", Level.REQUIRED);

        if (fileIdMesg.getManufacturer() == null) {
            result.setStatus(Status.FAILED);
            result.setDescription("Manufacturer Id is null.");
            return result;
        }

        if (Manufacturer.getStringFromValue(fileIdMesg.getManufacturer()).equals("")) {
            result.setStatus(Status.WARNING);
            result.setDescription("Unknown Manufacturer Id " + fileIdMesg.getManufacturer() + ".");
            return result;
        }

        result.setStatus(Status.PASSED);
        return result;
    }

    ActivityFileValidationResult checkFileIdMesgTimeCreated(FileIdMesg fileIdMesg) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("FileId Message Time Created Exists", Level.REQUIRED);

        if (fileIdMesg.getTimeCreated() == null) {
            result.setStatus(Status.FAILED);
            result.setDescription("Time Created is null.");
        } else {
            result.setStatus(Status.PASSED);
        }
        return result;
    }

    ActivityFileValidationResult checkActivityMesgExists(List activityMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Activity Message Exists", Level.REQUIRED);

        if (activityMesgs.size() > 0) {
            result.setStatus(Status.PASSED);
        } else {
            result.setStatus(Status.FAILED);
        }
        return result;
    }

    ActivityFileValidationResult checkActivityMesgTimestamp(ActivityMesg activityMesg) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Activity Message Timestamp Exists", Level.REQUIRED);

        if (activityMesg.getTimestamp() == null) {
            result.setStatus(Status.FAILED);
            result.setDescription("Timestamp is null.");
        } else {
            result.setStatus(Status.PASSED);
        }
        return result;
    }

    ActivityFileValidationResult checkActivityMesgLocalTimeStamp(ActivityMesg activityMesg) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Activity Message Local Timestamp is Valid", Level.REQUIRED);
        if (activityMesg.getLocalTimestamp() == null) {
            result.setStatus(Status.FAILED);
            result.setDescription("Local Timestamp is null.");
            return result;
        }

        Long localTimestamp = activityMesg.getLocalTimestamp();
        Long timestamp = activityMesg.getTimestamp().getTimestamp();
        long timezoneOffset = localTimestamp - timestamp;
        if ((timezoneOffset >= MIN_TIMEZONE_OFFSET_IN_SECONDS) && (timezoneOffset <= MAX_TIMEZONE_OFFSET_IN_SECONDS)) {
            result.setStatus(Status.PASSED);
        } else {
            result.setStatus(Status.FAILED);
            result.setDescription("Local Timestamp is not within the tolerated range of [-12,14] hours.");
        }
        return result;
    }

    ActivityFileValidationResult checkActivityMesgTotalTimerTime(ActivityMesg activityMesg, List sessionMesgs) {
        return checkValidFieldSums(activityMesg, sessionMesgs, "Activity", "Session", "total_timer_time");
    }

    ActivityFileValidationResult checkActivityMesgSessionCount(ActivityMesg activityMesg, int numSessions) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Activity Message Session Count Is Equal To Actual Session Count", Level.OPTIONAL);

        if (activityMesg.getNumSessions() == null) {
            result.setStatus(Status.WARNING);
            result.setDescription("Num Sessions is null.");
            return result;
        }

        if (activityMesg.getNumSessions() != numSessions) {
            result.setStatus(Status.WARNING);
            result.setDescription("Num Sessions does not match the actual number of Sessions messages found in the file.");
            return result;
        }

        result.setStatus(Status.PASSED);

        return result;
    }

    ActivityFileValidationResult checkSessionMesgsExists(int sessionMesgsSize) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Session Message Exists", Level.REQUIRED);

        if (sessionMesgsSize > 0) {
            result.setStatus(Status.PASSED);
        } else {
            result.setStatus(Status.FAILED);
        }
        return result;
    }

    ActivityFileValidationResult checkSessionMesgTimestamp(List sessionMesgs) {
        return checkMesgValidTimestamp(sessionMesgs, "Session");
    }

    ActivityFileValidationResult checkSessionMesgStartTime(List sessionMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Session Message Start Time Is Valid", Level.REQUIRED);

        if (sessionMesgs.size() == 0) {
            result.setStatus(Status.SKIPPED);
            return result;
        }

        boolean hasNullTimestamp = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getTimestamp() == null);

        boolean hasNullStartTime = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getStartTime() == null);

        if (hasNullTimestamp || hasNullStartTime) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Session messages contain a null Start Time and/or a null Timestamp.");
            return result;
        }

        for (SessionMesg sessionMesg : sessionMesgs) {
            if (sessionMesg.getTimestamp().before(sessionMesg.getStartTime())) {
                result.setStatus(Status.FAILED);
                result.setDescription("A Session message Start Time is greater than its Timestamp.");
                return result;
            }
        }

        result.setStatus(Status.PASSED);

        return result;
    }

    ActivityFileValidationResult checkSessionMesgTotalTimerTimeAndTotalElapsedTime(List sessionMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Session Message Total Timer Time and Total Elapsed Time are Valid", Level.REQUIRED);

        if (sessionMesgs.size() == 0) {
            result.setStatus(Status.SKIPPED);
            return result;
        }

        boolean hasNullTimerTime = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getTotalTimerTime() == null);

        boolean hasNullTotalElapsedTime = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getTotalElapsedTime() == null);

        if (hasNullTimerTime || hasNullTotalElapsedTime) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Session messages contain a null Start Time and/or a null Timestamp.");
            return result;
        }

        for (SessionMesg sessionMesg : sessionMesgs) {
            if (sessionMesg.getTotalElapsedTime() < sessionMesg.getTotalTimerTime()) {
                result.setStatus(Status.FAILED);
                result.setDescription("Session message Total Timer Time " + sessionMesg.getTotalTimerTime() + " > " + " Total Elapsed Time" + sessionMesg.getTotalElapsedTime() + ".");
                return result;
            }
        }

        result.setStatus(Status.PASSED);

        return result;
    }

    ActivityFileValidationResult checkSessionMesgFirstLapIndexAndNumLapsAreSequentialAndAbut(List sessionMesgs, List lapMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Session Message First Lap Index and Num Laps are Valid", Level.REQUIRED);

        if (sessionMesgs.size() == 0 || lapMesgs.size() == 0) {
            result.setStatus(Status.SKIPPED);
            result.setDescription("No Session messages exist and/or no Lap messages exist.");
            return result;
        }

        boolean hasNullFirstLapIndex = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getFirstLapIndex() == null);

        boolean hasNullNumLaps = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getNumLaps() == null);

        if (hasNullFirstLapIndex || hasNullNumLaps) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Session messages contain a null First Lap Index value and/or null Num Lap value.");
            return result;
        }

        int expectedFirstLapIndex = 0;

        for (SessionMesg sessionMesg : sessionMesgs) {

            if (sessionMesg.getFirstLapIndex() != expectedFirstLapIndex) {
                result.setStatus(Status.FAILED);
                result.setDescription("One or more Session messages contains an invlaid First Lap Index value.");
                return result;
            }

            expectedFirstLapIndex += sessionMesg.getNumLaps();
        }

        if (expectedFirstLapIndex != lapMesgs.size()) {
            result.setStatus(Status.FAILED);
            result.setDescription("Sum of Session messages Num Laps values is not equal to total number of Lap messages found in the file.");
            return result;
        }

        result.setStatus(Status.PASSED);

        return result;
    }

    ActivityFileValidationResult checkSessionMesgTotalTimerTime(List sessionMesgs, List lapMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Session Message Total Timer Time Is Equal To Sum of Lap Messages Total Timer Time", Level.REQUIRED);

        if (sessionMesgs.size() == 0 || lapMesgs.size() == 0) {
            result.setStatus(Status.SKIPPED);
            result.setDescription("No Session messages exist and/or no Lap messages exist.");
            return result;
        }

        boolean hasNullSessionTotalTimerTime = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getTotalTimerTime() == null);

        if (hasNullSessionTotalTimerTime) {
            result.setStatus(Status.FAILED);
            return result;
        }

        boolean hasNullFirstLapIndex = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getFirstLapIndex() == null);

        boolean hasNullNumLaps = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getNumLaps() == null);

        if (hasNullFirstLapIndex || hasNullNumLaps) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Session messages contain a null First Lap Index value and/or null Num Lap value.");
            return result;
        }

        for (SessionMesg session : sessionMesgs) {

            List laps = lapMesgs.stream()
                    .skip(session.getFirstLapIndex())
                    .limit(session.getNumLaps())
                    .collect(Collectors.toList());

            result = checkValidFieldSums(session, laps, "Session", "Lap", "total_timer_time");
            if (result.getStatus() == Status.FAILED) {
                return result;
            }
        }
        return result;
    }

    ActivityFileValidationResult checkSessionMesgTotalElapsedTime(List sessionMesgs, List lapMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Session Message Total Elapsed Time Is Equal To Sum of Lap Messages Total Elapsed Time", Level.REQUIRED);

        if (sessionMesgs.size() == 0 || lapMesgs.size() == 0) {
            result.setStatus(Status.SKIPPED);
            result.setDescription("No Session messages exist and/or no Lap messages exist.");
            return result;
        }

        boolean hasNullSessionTotalElapsedTime = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getTotalElapsedTime() == null);

        if (hasNullSessionTotalElapsedTime) {
            result.setStatus(Status.FAILED);
            return result;
        }

        boolean hasNullFirstLapIndex = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getFirstLapIndex() == null);

        boolean hasNullNumLaps = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getNumLaps() == null);

        if (hasNullFirstLapIndex || hasNullNumLaps) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Session messages contain a null First Lap Index value and/or null Num Lap value.");
            return result;
        }

        for (SessionMesg session : sessionMesgs) {

            List laps = lapMesgs.stream()
                    .skip(session.getFirstLapIndex())
                    .limit(session.getNumLaps())
                    .collect(Collectors.toList());

            result = checkValidFieldSums(session, laps, "Session", "Lap", "total_elapsed_time");
            if (result.getStatus() == Status.FAILED) {
                return result;
            }
        }
        return result;
    }

    ActivityFileValidationResult checkSessionMesgSport(List sessionMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Session Message Sport Exists", Level.REQUIRED);

        Status status = checkFieldValuesAreValid(sessionMesgs, "sport");

        if (status == Status.FAILED) {
            result.setDescription("One or more Session messages contain a null Sport value.");
        }

        result.setStatus(status);

        return result;
    }

    ActivityFileValidationResult checkSessionMesgSubSport(List sessionMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Session Message Sub Sport Exists", Level.OPTIONAL);

        Status status = checkSubSportValuesAreValid(sessionMesgs);

        if (status == Status.WARNING) {
            result.setDescription("One or more Session messages contain a null Sub Sport value. Set Sub Sport value to 'generic' if unknown.");
        }

        result.setStatus(status);

        return result;
    }

    ActivityFileValidationResult checkSessionMesgsAreSequentialAndAbut(List sessionMesgs) {
        return checkMesgsAreSequentialAndAbut(sessionMesgs, "Session");
    }

    ActivityFileValidationResult checkSessionMesgValidMesgIndexes(List sessionMesgs) {
        return checkValidMesgIndexes(sessionMesgs, "Session");
    }

    ActivityFileValidationResult checkLapMesgValidMesgIndexes(List lapMesgs) {
        return checkValidMesgIndexes(lapMesgs, "Lap");
    }

    ActivityFileValidationResult checkLapMesgsExists(int lapMesgsSize) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Lap Message Exists", Level.REQUIRED);

        if (lapMesgsSize > 0) {
            result.setStatus(Status.PASSED);
        } else {
            result.setStatus(Status.FAILED);
        }
        return result;
    }

    ActivityFileValidationResult checkLapMesgValidStartTimeAndTimeStamp(List sessionMesgs, List lapMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Lap Message Start Time and Timestamp are Valid", Level.REQUIRED);

        if (sessionMesgs.size() == 0 || lapMesgs.size() == 0) {
            result.setStatus(Status.SKIPPED);
            return result;
        }

        boolean hasNullStartTime = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getStartTime() == null);

        boolean hasNullTimestamp = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getTimestamp() == null);

        if (hasNullStartTime || hasNullTimestamp) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Session messages contain a null Start Time and/or null Timestamp.");
            return result;
        }

        boolean hasNullFirstLapIndex = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getFirstLapIndex() == null);

        boolean hasNullNumLaps = sessionMesgs.stream()
                .anyMatch(sessionMesg -> sessionMesg.getNumLaps() == null);

        if (hasNullFirstLapIndex || hasNullNumLaps) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Session messages contain a null First Lap Index value and/or a null Num Lap value.");
            return result;
        }

        boolean hasNullLapMesgStartTime = lapMesgs.stream()
                .anyMatch(lapMesg -> lapMesg.getStartTime() == null);

        boolean hasNullLapMesTimeStamp = lapMesgs.stream()
                .anyMatch(lapMesg -> lapMesg.getTimestamp() == null);

        if (hasNullLapMesgStartTime || hasNullLapMesTimeStamp) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Lap messages contain a null Start Time and/or a null Timestamp.");
            return result;
        }

        for (SessionMesg session : sessionMesgs) {
            List laps = lapMesgs.stream()
                    .skip(session.getFirstLapIndex())
                    .limit(session.getNumLaps())
                    .collect(Collectors.toList());

            for (LapMesg lap : laps) {
                boolean isStartTimeBefore = lap.getStartTime().before(session.getStartTime());
                boolean isTimestampAfter = lap.getTimestamp().after(session.getTimestamp());

                if (isStartTimeBefore || isTimestampAfter) {
                    result.setStatus(Status.FAILED);
                    result.setDescription("One or more Lap messages contain an incorrect Start Time and/or an incorrect Timestamp relative to the Session message.");
                    return result;
                }
            }
        }

        result.setStatus(Status.PASSED);

        return result;
    }

    ActivityFileValidationResult checkLapMesgsAreSequentialAndAbut(List lapMesgs) {
        return checkMesgsAreSequentialAndAbut(lapMesgs, "Lap");
    }

    ActivityFileValidationResult checkRecordMesgTimestampsAgainstSessionMesgTimes(List sessionMesgs, List recordMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Record Message Timestamps Fall Within Session Message Times", Level.REQUIRED);

        if (sessionMesgs.size() == 0 || recordMesgs.size() == 0) {
            result.setStatus(Status.SKIPPED);
            return result;
        }

        DateTime firstSessionMesgStartTime = sessionMesgs.get(0).getStartTime();
        DateTime lastSessionMesgTimeStamp = sessionMesgs.get(sessionMesgs.size() - 1).getTimestamp();

        if ((firstSessionMesgStartTime == null) || (lastSessionMesgTimeStamp == null)) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Session Messages contain a null Start Time and/or a null Timestamp.");
            return result;
        }

        DateTime firstRecordMesgTimestamp = recordMesgs.get(0).getTimestamp();
        DateTime lastRecordMesgTimestamp = recordMesgs.get(recordMesgs.size() - 1).getTimestamp();

        if ((firstRecordMesgTimestamp == null) || (lastRecordMesgTimestamp == null)) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Record messages contain a null Timestamp.");
            return result;
        }

        if (firstRecordMesgTimestamp.getTimestamp() - firstSessionMesgStartTime.getTimestamp() < -1) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Record message Timestamps is earlier than Session Start Time.");
            return result;
        }

        if (lastRecordMesgTimestamp.getTimestamp() - lastSessionMesgTimeStamp.getTimestamp() > 1) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Record message Timestamps is later than Session Timestamp.");
            return result;
        }

        result.setStatus(Status.PASSED);

        return result;
    }

    ActivityFileValidationResult checkRecordMesgsAreChronological(List recordMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Record Messages Are in Chronological Ascending Order", Level.REQUIRED);

        if (recordMesgs.size() == 0) {
            result.setStatus(Status.SKIPPED);
            return result;
        }

        boolean hasNullTimestamp = recordMesgs.stream()
                .anyMatch(recordMesg -> recordMesg.getTimestamp() == null);

        if (hasNullTimestamp) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Record messages contain a null Timestamp.");
            return result;
        }

        DateTime previousTimestamp = recordMesgs.get(0).getTimestamp();

        for (RecordMesg recordMesg : recordMesgs) {

            if (previousTimestamp.after(recordMesg.getTimestamp())) {
                result.setStatus(Status.FAILED);
                result.setDescription("One or more Record messages are not sequential.");
                return result;
            }
            previousTimestamp = recordMesg.getTimestamp();
        }

        result.setStatus(Status.PASSED);

        return result;
    }

    ActivityFileValidationResult checkDeviceInfoMesgValidTimestamp(List deviceInfoMesgs) {
        return checkMesgValidTimestamp(deviceInfoMesgs, "Device Info");
    }

    ActivityFileValidationResult checkDeviceInfoMesgValidDeviceIndex(List deviceInfoMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Device Info Message Device Index is Valid", Level.REQUIRED);

        if (deviceInfoMesgs.size() == 0) {
            result.setStatus(Status.SKIPPED);
            return result;
        }

        boolean hasNullDeviceIndex = deviceInfoMesgs.stream()
                .anyMatch(deviceInfoMesg -> deviceInfoMesg.getDeviceIndex() == null);

        if (hasNullDeviceIndex) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more Device Info messages contain a null Device Index value.");
            return result;
        }

        boolean hasCreator = deviceInfoMesgs.stream()
                .anyMatch(deviceInfoMesg -> deviceInfoMesg.getDeviceIndex().equals(DeviceIndex.CREATOR));

        if (!hasCreator) {
            result.setStatus(Status.FAILED);
            result.setDescription("At least one Device Info message Device Index field needs to be set to Creator.");
            return result;
        }

        result.setStatus(Status.PASSED);

        return result;
    }

    ActivityFileValidationResult checkDeviceInfoMesgValidManufacturerIdExists(List deviceInfoMesgs) {
        ActivityFileValidationResult result = new ActivityFileValidationResult("Device Info Message Manufacturer Id is Valid", Level.OPTIONAL);

        if (deviceInfoMesgs.size() == 0) {
            result.setStatus(Status.SKIPPED);
            return result;
        }

        boolean hasNullManufacturer = deviceInfoMesgs.stream()
                .anyMatch(deviceInfoMesg -> deviceInfoMesg.getManufacturer() == null);

        if (hasNullManufacturer) {
            result.setStatus(Status.WARNING);
            result.setDescription("One or more Device Info messages contain a null Manufacturer Id.");
            return result;
        }

        boolean hasInvalidManufacturer = deviceInfoMesgs.stream()
                .anyMatch(deviceInfoMesg -> Manufacturer.getStringFromValue(deviceInfoMesg.getManufacturer()).equals(""));

        if (hasInvalidManufacturer) {
            result.setStatus(Status.WARNING);
            result.setDescription("One or more Device Info messages contain an unknown Manufacturer Id.");
            return result;
        }

        result.setStatus(Status.PASSED);

        return result;
    }

    @SafeVarargs
    @SuppressWarnings("varargs")
    final boolean isEmpty(List... lists) {
        return Arrays.stream(lists).anyMatch(l -> l.size() == 0);
    }

    ActivityFileValidationResult checkValidMesgIndexes(List mesgs, String mesgName) {
        ActivityFileValidationResult result = new ActivityFileValidationResult(mesgName + " Message Valid Message Index", Level.REQUIRED);

        if (mesgs.size() == 0) {
            result.setStatus(Status.SKIPPED);
            return result;
        }

        for (int i = 0; i < mesgs.size(); i++) {
            Integer messageIndex = mesgs.get(i).getFieldIntegerValue(254, 0, Fit.SUBFIELD_INDEX_MAIN_FIELD);

            if (messageIndex == null) {
                result.setStatus(Status.FAILED);
                result.setDescription("One or more " + mesgName + " messages contain a null Message Index value.");
                return result;
            }

            if (messageIndex != i) {
                result.setStatus(Status.FAILED);
                result.setDescription("Message Indexes are not sequential.");
                return result;
            }
        }

        result.setStatus(Status.PASSED);

        return result;
    }

    ActivityFileValidationResult checkMesgValidTimestamp(List mesgs, String name) {
        ActivityFileValidationResult result = new ActivityFileValidationResult(name + " Message Timestamps are Valid", Level.REQUIRED);

        if (mesgs.size() == 0) {
            result.setStatus(Status.SKIPPED);
            return result;
        }

        boolean hasNullTimestamp = mesgs.stream()
                .anyMatch(mesg -> mesg.getFieldLongValue("timestamp") == null);

        if (hasNullTimestamp) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more " + name + " messages contain a null Timestamp.");
            return result;
        }

        result.setStatus(Status.PASSED);

        return result;
    }

    ActivityFileValidationResult checkValidFieldSums(Mesg mesg, List mesgs, String mesgName, String mesgListName, String fieldName) {
        ActivityFileValidationResult result = new ActivityFileValidationResult(mesgName + " Message " + fieldName + " is Equal to the Sum of " + mesgListName + " Messages " + fieldName + " Values", Level.REQUIRED);

        if (mesgs.size() == 0) {
            result.setStatus(Status.SKIPPED);
            return result;
        }

        if ((mesg.getFieldFloatValue(fieldName) == null)) {
            result.setStatus(Status.FAILED);
            result.setDescription(mesgName + " Message contains a null " + fieldName);
            return result;
        }

        boolean hasNullValue = mesgs.stream()
                .anyMatch(m -> m.getFieldFloatValue(fieldName) == null);

        if (hasNullValue) {
            result.setStatus(Status.FAILED);
            result.setDescription(mesgListName + "Message contains a null " + fieldName + " value.");
            return result;
        }

        double sumOfFieldValues = mesgs.stream()
                .mapToDouble(mesgList -> mesgList.getFieldFloatValue(fieldName))
                .sum();

        if (Math.abs(mesg.getFieldFloatValue(fieldName) - sumOfFieldValues) > 1) {
            result.setStatus(Status.FAILED);
            result.setDescription(mesgName + " message " + fieldName + " does not equal the sum of " + mesgListName + " messages " + fieldName + " values.");
            return result;
        }

        result.setStatus(Status.PASSED);

        return result;
    }

    ActivityFileValidationResult checkMesgsAreSequentialAndAbut(List mesgs, String mesgName) {
        ActivityFileValidationResult result = new ActivityFileValidationResult(mesgName + " Message Are Sequential and Abut", Level.REQUIRED);

        if (mesgs.size() <= 1) {
            result.setStatus(Status.SKIPPED);
            result.setDescription("Check requires two or more " + mesgName + " messages, found " + mesgs.size() + ".");
            return result;
        }

        boolean hasNullStartTime = mesgs.stream()
                .anyMatch(mesg -> mesg.getFieldLongValue("start_time") == null);

        boolean hasNullTotalElapsedTime = mesgs.stream()
                .anyMatch(mesg -> mesg.getFieldLongValue("total_elapsed_time") == null);

        if (hasNullStartTime || hasNullTotalElapsedTime) {
            result.setStatus(Status.FAILED);
            result.setDescription("One or more " + mesgName + " messages contain a null Start Time and/or a null Total Elapsed Time.");
            return result;
        }

        long previousEndTime = calculateEndTime(mesgs.get(0));

        for (Mesg mesg : mesgs.subList(1, mesgs.size())) {
            long startTime = mesg.getFieldLongValue("start_time");

            if (Math.abs(startTime - previousEndTime) > 2) {
                result.setStatus(Status.FAILED);
                result.setDescription(mesgName + " messages are not sequential, or do not abut.");
                return result;
            }

            previousEndTime = calculateEndTime(mesg);
        }

        result.setStatus(Status.PASSED);

        return result;
    }

    boolean anyMatchNull(List mesgs, String fieldName) {
        return mesgs.stream().anyMatch(mesg -> mesg.getFieldValue(fieldName) == null);
    }

    boolean allMatchNull(List mesgs, String fieldName) {
        return mesgs.stream().allMatch(mesg -> mesg.getFieldValue(fieldName) == null);
    }

    Status checkFieldValuesAreValid(List mesgs, String fieldName) {
        if (mesgs.size() == 0) {
            return Status.SKIPPED;
        }

        return anyMatchNull(mesgs, fieldName) ? Status.FAILED : Status.PASSED;
    }

    Status checkSportValuesAreValid(List mesgs) {
        Status status = checkFieldValuesAreValid(mesgs, "sport");
        return status == Status.FAILED ? Status.WARNING : status;
    }

    Status checkSubSportValuesAreValid(List mesgs) {
        Status status = checkFieldValuesAreValid(mesgs, "sub_sport");
        return status == Status.FAILED ? Status.WARNING : status;
    }

    Long calculateEndTime(Mesg mesg){
        long startTime = mesg.getFieldLongValue("start_time");
        long totalElapsedTime = mesg.getFieldLongValue("total_elapsed_time");
        return startTime + totalElapsedTime;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy