Skip to content

Commit

Permalink
Merge pull request #5 from dejarno/main
Browse files Browse the repository at this point in the history
Add support for SML files
  • Loading branch information
dejarno authored Mar 25, 2024
2 parents d33c821 + 9a897fd commit 6ce51e7
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/com/github/nradov/sdetofit/DivesSourceFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.xml.sax.SAXException;

import com.github.nradov.sdetofit.suunto.SuuntoSde;
import com.github.nradov.sdetofit.suunto.SuuntoSml;
import com.github.nradov.sdetofit.suunto.SuuntoXml;

/**
Expand All @@ -34,6 +35,8 @@ private static final class FileExtension {
/** XML file. */
final static String XML = ".xml";

/** SML file. */
final static String SML = ".sml";
}

/**
Expand All @@ -59,6 +62,8 @@ public static DivesSource create(final Path file)
return new SuuntoSde(file);
} else if (lowerCaseFile.endsWith(FileExtension.XML)) {
return new SuuntoXml(file);
} else if (lowerCaseFile.endsWith(FileExtension.SML)) {
return new SuuntoSml(file);
}
// TODO: add support for other file formats

Expand Down
181 changes: 181 additions & 0 deletions src/com/github/nradov/sdetofit/suunto/SuuntoSml.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package com.github.nradov.sdetofit.suunto;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.NavigableSet;
import java.util.TreeSet;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.garmin.fit.DateTime;
import com.garmin.fit.Manufacturer;
import com.github.nradov.sdetofit.Dive;
import com.github.nradov.sdetofit.DivesSource;
import com.github.nradov.sdetofit.Record;
import com.github.nradov.sdetofit.SdeToFit;

/**
* Single dive profile in Suunto SML format.
*/
public class SuuntoSml implements Dive, DivesSource {

private final DateTime start, end;
private final String productName;
private final long serialNumber;
private final long diveNumber;
private final float maxDepth, meanDepth;

private final Element suunto;

/* XML document root element name. */
private static final String DOCUMENT_ELEMENT_NAME = "sml";

public SuuntoSml(final Path file) throws ParserConfigurationException, SAXException, IOException {
this(new FileInputStream(file.toFile()));
}

public SuuntoSml(final InputStream is) throws ParserConfigurationException, SAXException, IOException {
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
final DocumentBuilder db = dbf.newDocumentBuilder();
final Document doc = db.parse(is);
suunto = doc.getDocumentElement();

if (!DOCUMENT_ELEMENT_NAME.equals(suunto.getTagName())) {
throw new IllegalArgumentException(
"Document element name is " + suunto.getLocalName() + " instead of " + DOCUMENT_ELEMENT_NAME);
}

final String date = suunto.getElementsByTagName("DateTime").item(0).getTextContent();
final LocalDateTime dateTime = LocalDateTime.parse(date);

final int dayOfMonth = dateTime.getDayOfMonth();
final int month = dateTime.getMonthValue()-1;
final int year = dateTime.getYear();
final int hourOfDay = dateTime.getHour();
final int minute = dateTime.getMinute();
final int second = dateTime.getSecond();


final Calendar startCalendar = new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second);
start = new DateTime((startCalendar.getTimeInMillis() - SdeToFit.OFFSET_MS) / 1000);
final int diveTimeSec = Integer.valueOf(suunto.getElementsByTagName("Duration").item(0).getTextContent().trim());

end = new DateTime(((startCalendar.getTimeInMillis() - SdeToFit.OFFSET_MS) / 1000) + diveTimeSec);

this.maxDepth = Float.parseFloat(suunto.getElementsByTagName("Max").item(0).getTextContent());
this.meanDepth = Float.parseFloat(suunto.getElementsByTagName("Avg").item(0).getTextContent());

// TODO: fix this, as far as I could find the total dive number is not kept in SML
this.diveNumber = Integer.valueOf(suunto.getElementsByTagName("NumberInSeries").item(0).getTextContent());

this.productName = suunto.getElementsByTagName("Name").item(0).getTextContent();
this.serialNumber = Long.valueOf(suunto.getElementsByTagName("SerialNumber").item(0).getTextContent());
populateRecords();
}

@Override
public DateTime getStartTime() {
return start;
}

@Override
public DateTime getEndTime() {
return end;
}

private List<Record> records = new ArrayList<Record>();;

private void populateRecords() {

final NodeList samples = suunto.getElementsByTagName("Sample");

final int sampleCount = samples.getLength()+1;
final int sampleInterval = Integer
.valueOf(suunto.getElementsByTagName("SampleInterval").item(0).getTextContent());
final int diveSeconds = sampleCount * sampleInterval;
final DateTime dateTime = new DateTime(start);

for (int i = 0; i < samples.getLength(); i++) {
final var sample = (Element) samples.item(i);
final int sampleTime = Integer.valueOf(sample.getElementsByTagName("Time").item(0).getTextContent());
if (sampleTime > diveSeconds) {
throw new IllegalStateException("sample at time " + sampleTime + " doesn't match header");
}

if (sample.getElementsByTagName("Depth").item(0) == null) {
continue;
}

final float depth = Float.valueOf(sample.getElementsByTagName("Depth").item(0).getTextContent());
final float temperature = Float.valueOf(sample.getElementsByTagName("Temperature").item(0).getTextContent()) - 273.15f;
final var record = new Record(dateTime, depth, (byte) temperature);
records.add(record);
dateTime.add(sampleInterval);
}
}

public List<Record> getRecords() {
return Collections.unmodifiableList(records);
}

@Override
public int compareTo(final Dive o) {
return getStartTime().compareTo(o.getStartTime());
}

@Override
public String getProductName() {
return productName;
}

@Override
public Integer getManufacturer() {
return Manufacturer.SUUNTO;
}

@Override
public long getSerialNumber() {
return serialNumber;
}

@Override
public float getAvgDepth() {
return meanDepth;
}

@Override
public float getMaxDepth() {
return maxDepth;
}

@Override
public float getBottomTime() {
return (float) (end.getTimestamp() - start.getTimestamp());
}

@Override
public long getDiveNumber() {
return diveNumber;
}

@Override
public NavigableSet<Dive> getDives() {
return new TreeSet<Dive>(Collections.singleton(this));
}

}

0 comments on commit 6ce51e7

Please sign in to comment.