kopia lustrzana https://github.com/bertrik/ttnhabbridge
Merge f1d99de180
into 858aa956ab
commit
7d05938c3c
|
@ -19,11 +19,12 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
|||
import nl.sikken.bertrik.hab.DecodeException;
|
||||
import nl.sikken.bertrik.hab.EPayloadEncoding;
|
||||
import nl.sikken.bertrik.hab.ExpiringCache;
|
||||
import nl.sikken.bertrik.hab.HabReceiver;
|
||||
import nl.sikken.bertrik.hab.Location;
|
||||
import nl.sikken.bertrik.hab.PayloadDecoder;
|
||||
import nl.sikken.bertrik.hab.Sentence;
|
||||
import nl.sikken.bertrik.hab.habitat.HabReceiver;
|
||||
import nl.sikken.bertrik.hab.habitat.HabitatUploader;
|
||||
import nl.sikken.bertrik.hab.habitat.Location;
|
||||
import nl.sikken.bertrik.hab.amateurSondehub.AmateurSondehubUploader;
|
||||
import nl.sikken.bertrik.hab.lorawan.HeliumUplinkMessage;
|
||||
import nl.sikken.bertrik.hab.lorawan.LoraWanUplinkMessage;
|
||||
import nl.sikken.bertrik.hab.lorawan.LoraWanUplinkMessage.GatewayInfo;
|
||||
|
@ -41,6 +42,8 @@ public final class TtnHabBridge {
|
|||
|
||||
private final List<MqttListener> mqttListeners = new ArrayList<>();
|
||||
private final HabitatUploader habUploader;
|
||||
private final AmateurSondehubUploader amateurSondehubUploader;
|
||||
|
||||
private final PayloadDecoder decoder;
|
||||
private final ExpiringCache gwCache;
|
||||
|
||||
|
@ -90,6 +93,8 @@ public final class TtnHabBridge {
|
|||
.add(new MqttListener(this::handleMessage, config.getHeliumConfig(), HeliumUplinkMessage.class));
|
||||
}
|
||||
this.habUploader = HabitatUploader.create(config.getHabitatConfig());
|
||||
this.amateurSondehubUploader = AmateurSondehubUploader.create(config.getAmateurSondehubConfig());
|
||||
|
||||
this.decoder = new PayloadDecoder(EPayloadEncoding.parse(config.getPayloadEncoding()));
|
||||
this.gwCache = new ExpiringCache(Duration.ofSeconds(config.getGwCacheExpirationTime()));
|
||||
}
|
||||
|
@ -104,6 +109,7 @@ public final class TtnHabBridge {
|
|||
|
||||
// start sub-modules
|
||||
habUploader.start();
|
||||
amateurSondehubUploader.start();
|
||||
for (MqttListener listener : mqttListeners) {
|
||||
listener.start();
|
||||
}
|
||||
|
@ -119,6 +125,7 @@ public final class TtnHabBridge {
|
|||
try {
|
||||
Sentence sentence = decoder.decode(message);
|
||||
String line = sentence.format();
|
||||
String amateurSondehubJsonStr = sentence.amateurSondehubFormat();
|
||||
|
||||
// collect list of listeners
|
||||
List<HabReceiver> receivers = new ArrayList<>();
|
||||
|
@ -137,6 +144,12 @@ public final class TtnHabBridge {
|
|||
|
||||
// send payload telemetry data
|
||||
habUploader.schedulePayloadTelemetryUpload(line, receivers, now);
|
||||
|
||||
|
||||
// send data to amateurSondehub
|
||||
amateurSondehubUploader.schedulePayloadTelemetryUpload(amateurSondehubJsonStr, receivers, now);
|
||||
|
||||
|
||||
} catch (DecodeException e) {
|
||||
LOG.warn("Payload decoding exception: {}", e.getMessage());
|
||||
} catch (Exception e) {
|
||||
|
@ -156,6 +169,7 @@ public final class TtnHabBridge {
|
|||
listener.stop();
|
||||
}
|
||||
habUploader.stop();
|
||||
amateurSondehubUploader.stop();
|
||||
LOG.info("Stopped TTN HAB bridge application");
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package nl.sikken.bertrik;
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import nl.sikken.bertrik.hab.amateurSondehub.AmateurSondehubConfig;
|
||||
import nl.sikken.bertrik.hab.habitat.HabitatConfig;
|
||||
import nl.sikken.bertrik.hab.lorawan.MqttConfig;
|
||||
|
||||
|
@ -20,6 +21,9 @@ final class TtnHabBridgeConfig {
|
|||
@JsonProperty("habitat")
|
||||
private final HabitatConfig habitatConfig = new HabitatConfig();
|
||||
|
||||
@JsonProperty("amateurSondehub")
|
||||
private final AmateurSondehubConfig amateurSondehubConfig = new AmateurSondehubConfig();
|
||||
|
||||
@JsonProperty("gwCacheExpirationTime")
|
||||
private final int gwCacheExpirationTime = 600; // seconds
|
||||
|
||||
|
@ -34,6 +38,10 @@ final class TtnHabBridgeConfig {
|
|||
return heliumConfig;
|
||||
}
|
||||
|
||||
public AmateurSondehubConfig getAmateurSondehubConfig() {
|
||||
return amateurSondehubConfig;
|
||||
}
|
||||
|
||||
public HabitatConfig getHabitatConfig() {
|
||||
return habitatConfig;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package nl.sikken.bertrik.hab.habitat;
|
||||
package nl.sikken.bertrik.hab;
|
||||
|
||||
/**
|
||||
* Object describing a HAB receiver.
|
|
@ -1,4 +1,4 @@
|
|||
package nl.sikken.bertrik.hab.habitat;
|
||||
package nl.sikken.bertrik.hab;
|
||||
|
||||
/**
|
||||
* Representation of a HAB receiver location.
|
|
@ -87,11 +87,11 @@ public final class PayloadDecoder {
|
|||
double altitude = sodaq.getAltitude();
|
||||
Instant instant = Instant.ofEpochSecond(sodaq.getTimeStamp());
|
||||
Sentence sentence = new Sentence(callSign, counter, instant);
|
||||
sentence.addField(String.format(Locale.ROOT, "%.6f", latitude));
|
||||
sentence.addField(String.format(Locale.ROOT, "%.6f", longitude));
|
||||
sentence.addField(String.format(Locale.ROOT, "%.1f", altitude));
|
||||
sentence.addField(String.format(Locale.ROOT, "%.0f", sodaq.getBoardTemp()));
|
||||
sentence.addField(String.format(Locale.ROOT, "%.2f", sodaq.getBattVoltage()));
|
||||
sentence.addField("lat", String.format(Locale.ROOT, "%.6f", latitude));
|
||||
sentence.addField("lon", String.format(Locale.ROOT, "%.6f", longitude));
|
||||
sentence.addField("alt", String.format(Locale.ROOT, "%.1f", altitude));
|
||||
sentence.addField("temp", String.format(Locale.ROOT, "%.0f", sodaq.getBoardTemp()));
|
||||
sentence.addField("batt", String.format(Locale.ROOT, "%.2f", sodaq.getBattVoltage()));
|
||||
return sentence;
|
||||
} catch (BufferUnderflowException e) {
|
||||
throw new DecodeException("Error decoding sodaqone", e);
|
||||
|
@ -117,15 +117,15 @@ public final class PayloadDecoder {
|
|||
double longitude = parseDouble(fields.get("lon"));
|
||||
double altitude = parseDouble(fields.get("gpsalt"));
|
||||
Sentence sentence = new Sentence(callSign, counter, time);
|
||||
sentence.addField(String.format(Locale.ROOT, "%.6f", latitude));
|
||||
sentence.addField(String.format(Locale.ROOT, "%.6f", longitude));
|
||||
sentence.addField(String.format(Locale.ROOT, "%.1f", altitude));
|
||||
sentence.addField("lat", String.format(Locale.ROOT, "%.6f", latitude));
|
||||
sentence.addField("lon", String.format(Locale.ROOT, "%.6f", longitude));
|
||||
sentence.addField("alt", String.format(Locale.ROOT, "%.1f", altitude));
|
||||
|
||||
if (fields.containsKey("temp") && fields.containsKey("vcc")) {
|
||||
Double temp = parseDouble(fields.get("temp"));
|
||||
Double vcc = parseDouble(fields.get("vcc"));
|
||||
sentence.addField(String.format(Locale.ROOT, "%.1f", temp));
|
||||
sentence.addField(String.format(Locale.ROOT, "%.3f", vcc));
|
||||
sentence.addField("temp", String.format(Locale.ROOT, "%.1f", temp));
|
||||
sentence.addField("batt", String.format(Locale.ROOT, "%.3f", vcc));
|
||||
}
|
||||
return sentence;
|
||||
} catch (RuntimeException e) {
|
||||
|
@ -167,7 +167,7 @@ public final class PayloadDecoder {
|
|||
// add all items, in the order they appear in the cayenne message
|
||||
for (CayenneItem item : cayenne.getItems()) {
|
||||
for (String s : item.format()) {
|
||||
sentence.addField(s);
|
||||
sentence.addField("", s);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,9 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
|
||||
/**
|
||||
|
@ -22,6 +25,9 @@ public final class Sentence {
|
|||
|
||||
private final CrcCcitt16 crc16 = new CrcCcitt16();
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ObjectNode json = mapper.createObjectNode();
|
||||
|
||||
private final List<String> fields = new ArrayList<>();
|
||||
|
||||
/**
|
||||
|
@ -42,8 +48,9 @@ public final class Sentence {
|
|||
*
|
||||
* @param value the pre-formatted value
|
||||
*/
|
||||
public void addField(String value) {
|
||||
fields.add(value);
|
||||
public void addField(String fieldName, String fieldValueStr) {
|
||||
json.put(fieldName, fieldValueStr);
|
||||
fields.add(fieldValueStr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,6 +80,23 @@ public final class Sentence {
|
|||
return String.format(Locale.ROOT, "$$%s*%04X\n", basic, crcValue);
|
||||
}
|
||||
|
||||
public String amateurSondehubFormat() {
|
||||
// add mandatory fields
|
||||
|
||||
json.put("software_name", "ttnhabbridge");
|
||||
json.put("software_version", "0.0.1");
|
||||
json.put("uploader_callsign", "foobar");
|
||||
|
||||
json.put("modulation", "LoRaWAN");
|
||||
|
||||
json.put("time_received", this.time.toString());
|
||||
json.put("datetime", this.time.toString());
|
||||
json.put("payload_callsign", this.callSign);
|
||||
|
||||
|
||||
return json.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return format();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package nl.sikken.bertrik.hab.habitat;
|
||||
package nl.sikken.bertrik.hab;
|
||||
|
||||
import java.util.Locale;
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package nl.sikken.bertrik.hab.amateurSondehub;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class AmateurSondehubConfig {
|
||||
|
||||
@JsonProperty("url")
|
||||
private final String url;
|
||||
|
||||
@JsonProperty("timeout")
|
||||
private final int timeout;
|
||||
|
||||
public AmateurSondehubConfig() {
|
||||
this("https://api.v2.sondehub.org", 60);
|
||||
}
|
||||
|
||||
public AmateurSondehubConfig(String url, int timeout) {
|
||||
this.url = url;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public int getTimeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package nl.sikken.bertrik.hab.amateurSondehub;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nl.sikken.bertrik.hab.HabReceiver;
|
||||
import nl.sikken.bertrik.hab.UploadResult;
|
||||
import okhttp3.OkHttpClient;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.jackson.JacksonConverterFactory;
|
||||
import retrofit2.converter.scalars.ScalarsConverterFactory;
|
||||
|
||||
/**
|
||||
* AmateurSondehub uploader.
|
||||
*
|
||||
* Exchanges data with the amateurSondehub system. Call to ScheduleXXX methods
|
||||
* are non-blocking. All actions run on a single background thread for
|
||||
* simplicity.
|
||||
*/
|
||||
public final class AmateurSondehubUploader {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AmateurSondehubUploader.class);
|
||||
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
private final IAmateurSondehubRestApi restClient;
|
||||
|
||||
/**
|
||||
* Creates a new amateurSondehub uploader.
|
||||
*
|
||||
* @param config the configuration
|
||||
* @return the amateurSondehub uploader
|
||||
*/
|
||||
public static AmateurSondehubUploader create(AmateurSondehubConfig config) {
|
||||
LOG.info("Creating new amateurSondehub REST client with timeout {} for {}", config.getTimeout(),
|
||||
config.getUrl());
|
||||
Duration timeout = Duration.ofSeconds(config.getTimeout());
|
||||
OkHttpClient client = new OkHttpClient().newBuilder().callTimeout(timeout).build();
|
||||
Retrofit retrofit = new Retrofit.Builder().baseUrl(config.getUrl())
|
||||
.addConverterFactory(ScalarsConverterFactory.create())
|
||||
.addConverterFactory(JacksonConverterFactory.create()).client(client).build();
|
||||
IAmateurSondehubRestApi restClient = retrofit.create(IAmateurSondehubRestApi.class);
|
||||
return new AmateurSondehubUploader(restClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param restClient the REST client used for uploading
|
||||
*/
|
||||
AmateurSondehubUploader(IAmateurSondehubRestApi restClient) {
|
||||
this.restClient = restClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the uploader process.
|
||||
*/
|
||||
public void start() {
|
||||
LOG.info("Starting amateurSondehub uploader");
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the uploader process.
|
||||
*/
|
||||
public void stop() {
|
||||
LOG.info("Stopping amateurSondehub uploader");
|
||||
executor.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a new sentence to be sent to the HAB network.
|
||||
*
|
||||
* @param sentence the ASCII sentence
|
||||
* @param receivers list of listener that received this sentence
|
||||
* @param instant the current date/time
|
||||
*/
|
||||
public void schedulePayloadTelemetryUpload(String sentence, List<HabReceiver> receivers, Instant instant) {
|
||||
LOG.info("Uploading for {} receivers: {}", receivers.size(), sentence.trim());
|
||||
|
||||
// todo: add receiver to sentence
|
||||
// submit it to our processing thread
|
||||
executor.execute(() -> uploadPayloadTelemetry(sentence));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual payload telemetry upload as a REST-like call towards
|
||||
* amateurSondehub.
|
||||
*
|
||||
* @param json the JSON payload
|
||||
*/
|
||||
private void uploadPayloadTelemetry(String json) {
|
||||
LOG.info("Upload payload telemetry: {}", json);
|
||||
try {
|
||||
Response<UploadResult> response = restClient.uploadDocument(json).execute();
|
||||
if (response.isSuccessful()) {
|
||||
LOG.info("Result payload telemetry: {}", response.body());
|
||||
} else {
|
||||
LOG.warn("Result payload telemetry: {}", response.message());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Caught IOException: {}", e.getMessage());
|
||||
} catch (Exception e) {
|
||||
LOG.error("Caught Exception: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package nl.sikken.bertrik.hab.amateurSondehub;
|
||||
|
||||
import nl.sikken.bertrik.hab.UploadResult;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.PUT;
|
||||
|
||||
/**
|
||||
* Interface definition for payload telemetry and listener telemetry towards
|
||||
* AmateurSondehub.
|
||||
*/
|
||||
public interface IAmateurSondehubRestApi {
|
||||
@PUT("/amateur/telemetry")
|
||||
Call<UploadResult> uploadDocument(@Body String document);
|
||||
|
||||
}
|
|
@ -18,9 +18,11 @@ import javax.xml.bind.DatatypeConverter;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nl.sikken.bertrik.hab.HabReceiver;
|
||||
import nl.sikken.bertrik.hab.UploadResult;
|
||||
import nl.sikken.bertrik.hab.habitat.docs.ListenerInformationDoc;
|
||||
import nl.sikken.bertrik.hab.habitat.docs.ListenerTelemetryDoc;
|
||||
import nl.sikken.bertrik.hab.habitat.docs.PayloadTelemetryDoc;
|
||||
import nl.sikken.bertrik.hab.habitat.docs.HabitatPayloadTelemetryDoc;
|
||||
import okhttp3.OkHttpClient;
|
||||
import retrofit2.Response;
|
||||
import retrofit2.Retrofit;
|
||||
|
@ -107,7 +109,7 @@ public final class HabitatUploader {
|
|||
|
||||
for (HabReceiver receiver : receivers) {
|
||||
// create Json
|
||||
PayloadTelemetryDoc doc = new PayloadTelemetryDoc(instant, bytes);
|
||||
HabitatPayloadTelemetryDoc doc = new HabitatPayloadTelemetryDoc(instant, bytes);
|
||||
doc.addCallSign(receiver.getCallsign());
|
||||
String json = doc.format();
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.sikken.bertrik.hab.habitat;
|
||||
|
||||
import nl.sikken.bertrik.hab.UploadResult;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.GET;
|
||||
|
|
|
@ -15,7 +15,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
|
|||
*
|
||||
* SEE http://habitat.habhub.org/jse/#schemas/payload_telemetry.json
|
||||
*/
|
||||
public final class PayloadTelemetryDoc {
|
||||
public final class HabitatPayloadTelemetryDoc {
|
||||
|
||||
private final DateTimeFormatter dateFormat = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
|
||||
|
||||
|
@ -30,7 +30,7 @@ public final class PayloadTelemetryDoc {
|
|||
* @param instant the creation/upload date/time
|
||||
* @param rawBytes the raw telemetry string as bytes
|
||||
*/
|
||||
public PayloadTelemetryDoc(Instant instant, byte[] rawBytes) {
|
||||
public HabitatPayloadTelemetryDoc(Instant instant, byte[] rawBytes) {
|
||||
this.dateCreated = OffsetDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
this.dateUploaded = OffsetDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
this.rawBytes = rawBytes.clone();
|
|
@ -6,7 +6,7 @@ import java.util.Locale;
|
|||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import nl.sikken.bertrik.hab.habitat.HabReceiver;
|
||||
import nl.sikken.bertrik.hab.HabReceiver;
|
||||
|
||||
/**
|
||||
* Listener information doc.
|
||||
|
|
|
@ -5,7 +5,7 @@ import java.time.Instant;
|
|||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import nl.sikken.bertrik.hab.habitat.HabReceiver;
|
||||
import nl.sikken.bertrik.hab.HabReceiver;
|
||||
|
||||
/**
|
||||
* Listener telemetry doc.
|
||||
|
|
|
@ -7,7 +7,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import nl.sikken.bertrik.hab.habitat.Location;
|
||||
import nl.sikken.bertrik.hab.Location;
|
||||
|
||||
/**
|
||||
* LoRaWAN uplink message, stack independent, containing all information needed
|
||||
|
|
|
@ -17,22 +17,38 @@ public final class SentenceTest {
|
|||
public void testSentence() {
|
||||
Instant instant = Instant.ofEpochSecond(0);
|
||||
Sentence sentence = new Sentence("CALL", 1, instant);
|
||||
sentence.addField("3.45");
|
||||
sentence.addField("6.78");
|
||||
sentence.addField("9.0");
|
||||
sentence.addField("lon", "3.45");
|
||||
sentence.addField("lat", "6.78");
|
||||
sentence.addField("alt", "9.0");
|
||||
String s = sentence.format();
|
||||
|
||||
Assert.assertEquals("$$CALL,1,00:00:00,3.45,6.78,9.0*906C\n", s);
|
||||
Assert.assertNotNull(sentence.toString());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verifies basic AmateurSonde json formatting.
|
||||
*/
|
||||
@Test
|
||||
public void testSentenceJson() {
|
||||
Instant instant = Instant.ofEpochMilli(1652126056001L);
|
||||
Sentence sentence = new Sentence("CALL", 1, instant);
|
||||
sentence.addField("lon", "3.45");
|
||||
sentence.addField("lat", "6.78");
|
||||
sentence.addField("alt", "9.0");
|
||||
String s = sentence.amateurSondehubFormat();
|
||||
Assert.assertEquals("{\"lon\":\"3.45\",\"lat\":\"6.78\",\"alt\":\"9.0\",\"software_name\":\"ttnhabbridge\",\"software_version\":\"0.0.1\",\"uploader_callsign\":\"foobar\",\"modulation\":\"LoRaWAN\",\"time_received\":\"2022-05-09T19:54:16.001Z\",\"datetime\":\"2022-05-09T19:54:16.001Z\",\"payload_callsign\":\"CALL\"}", s);
|
||||
Assert.assertNotNull(sentence.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that extra fields are formatted too.
|
||||
*/
|
||||
@Test
|
||||
public void testSentenceExtras() {
|
||||
Sentence sentence = new Sentence("CALL", 1, Instant.now());
|
||||
sentence.addField("hello");
|
||||
sentence.addField("extra" ,"hello");
|
||||
String s = sentence.format();
|
||||
|
||||
Assert.assertTrue(s.contains("hello"));
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package nl.sikken.bertrik.hab.amateurSondehub;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import nl.sikken.bertrik.hab.HabReceiver;
|
||||
import nl.sikken.bertrik.hab.UploadResult;
|
||||
import nl.sikken.bertrik.hab.Location;
|
||||
import nl.sikken.bertrik.hab.Sentence;
|
||||
import retrofit2.mock.Calls;
|
||||
|
||||
/**
|
||||
* Unit tests for AmateurSondehubUploader.
|
||||
*/
|
||||
public final class AmateurSondehubTest {
|
||||
|
||||
private static final Location LOCATION = new Location(52.0162, 4.4735, 5.0);
|
||||
|
||||
/**
|
||||
* Verifies creation of REST client.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateRestClient() {
|
||||
AmateurSondehubConfig config = new AmateurSondehubConfig();
|
||||
Assert.assertNotNull(AmateurSondehubUploader.create(config));
|
||||
}
|
||||
|
||||
/**
|
||||
* Happy flow scenario for payload upload.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void testUploadPayload() throws IOException {
|
||||
// create a mocked rest client
|
||||
IAmateurSondehubRestApi restClient = Mockito.mock(IAmateurSondehubRestApi.class);
|
||||
Mockito.when(restClient.uploadDocument(Mockito.anyString()))
|
||||
.thenReturn(Calls.response(new UploadResult(true, "id", "rev")));
|
||||
|
||||
AmateurSondehubUploader uploader = new AmateurSondehubUploader(restClient);
|
||||
|
||||
// verify upload using the uploader
|
||||
uploader.start();
|
||||
try {
|
||||
HabReceiver receiver = new HabReceiver("BERTRIK", LOCATION);
|
||||
Instant instant = Instant.now();
|
||||
Sentence sentence = new Sentence("NOTAFLIGHT", 1, instant);
|
||||
sentence.addField("location", "52.0182307,4.695772,1000");
|
||||
|
||||
uploader.schedulePayloadTelemetryUpload(sentence.format(), Arrays.asList(receiver), instant);
|
||||
Mockito.verify(restClient, Mockito.timeout(3000).times(1)).uploadDocument(Mockito.anyString());
|
||||
} finally {
|
||||
uploader.stop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -9,7 +9,10 @@ import org.junit.Ignore;
|
|||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import nl.sikken.bertrik.hab.HabReceiver;
|
||||
import nl.sikken.bertrik.hab.Location;
|
||||
import nl.sikken.bertrik.hab.Sentence;
|
||||
import nl.sikken.bertrik.hab.UploadResult;
|
||||
import retrofit2.mock.Calls;
|
||||
|
||||
/**
|
||||
|
@ -49,7 +52,7 @@ public final class HabitatUploaderTest {
|
|||
HabReceiver receiver = new HabReceiver("BERTRIK", LOCATION);
|
||||
Instant instant = Instant.now();
|
||||
Sentence sentence = new Sentence("NOTAFLIGHT", 1, instant);
|
||||
sentence.addField("52.0182307,4.695772,1000");
|
||||
sentence.addField("location", "52.0182307,4.695772,1000");
|
||||
|
||||
uploader.schedulePayloadTelemetryUpload(sentence.format(), Arrays.asList(receiver), instant);
|
||||
Mockito.verify(restClient, Mockito.timeout(3000).times(1)).updateListener(Mockito.anyString(),
|
||||
|
@ -102,7 +105,7 @@ public final class HabitatUploaderTest {
|
|||
try {
|
||||
Instant instant = Instant.now();
|
||||
Sentence sentence = new Sentence("NOTAFLIGHT", 1, instant);
|
||||
sentence.addField("52.0182307,4.695772,1000");
|
||||
sentence.addField("location", "52.0182307,4.695772,1000");
|
||||
HabReceiver receiver = new HabReceiver("BERTRIK", null);
|
||||
uploader.schedulePayloadTelemetryUpload(sentence.format(), Arrays.asList(receiver), instant);
|
||||
Thread.sleep(3000);
|
||||
|
|
|
@ -3,6 +3,8 @@ package nl.sikken.bertrik.hab.habitat;
|
|||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
|
||||
import nl.sikken.bertrik.hab.HabReceiver;
|
||||
import nl.sikken.bertrik.hab.Location;
|
||||
import nl.sikken.bertrik.hab.Sentence;
|
||||
|
||||
public final class RunMultiReceiverTest {
|
||||
|
|
|
@ -5,8 +5,8 @@ import java.time.Instant;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import nl.sikken.bertrik.hab.habitat.HabReceiver;
|
||||
import nl.sikken.bertrik.hab.habitat.Location;
|
||||
import nl.sikken.bertrik.hab.HabReceiver;
|
||||
import nl.sikken.bertrik.hab.Location;
|
||||
|
||||
/**
|
||||
* Unit tests for ListenerInfoDoc
|
||||
|
|
|
@ -5,8 +5,8 @@ import java.time.Instant;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import nl.sikken.bertrik.hab.habitat.HabReceiver;
|
||||
import nl.sikken.bertrik.hab.habitat.Location;
|
||||
import nl.sikken.bertrik.hab.HabReceiver;
|
||||
import nl.sikken.bertrik.hab.Location;
|
||||
|
||||
/**
|
||||
* Unit tests for ListenerTelemetryDoc.
|
||||
|
|
|
@ -16,7 +16,7 @@ public final class PayloadTelemetryDocTest {
|
|||
@Test
|
||||
public void testFormat() {
|
||||
Instant instant = Instant.now();
|
||||
PayloadTelemetryDoc doc = new PayloadTelemetryDoc(instant, new byte[] {1, 2, 3});
|
||||
HabitatPayloadTelemetryDoc doc = new HabitatPayloadTelemetryDoc(instant, new byte[] {1, 2, 3});
|
||||
doc.addCallSign("BERTRIK");
|
||||
String json = doc.format();
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue