diff --git a/README.md b/README.md index 93c8d0ec..869daf51 100644 --- a/README.md +++ b/README.md @@ -60,5 +60,4 @@ docker-compose run --rm tools radar-schemas-tools register http://schema-registr docker-compose run --rm tools radar-schemas-tools create zookeeper-1:2181 # run source-catalogue webservice docker-compose run --rm tools radar-schemas-tools serve -p - ``` diff --git a/commons/catalogue/unit.avsc b/commons/catalogue/unit.avsc index ec069dcf..0a4585d3 100644 --- a/commons/catalogue/unit.avsc +++ b/commons/catalogue/unit.avsc @@ -20,6 +20,7 @@ "RADAIAN_PER_SEC", "RMSSD_IN_MILLI_SEC", "SECOND", + "MICRO_VOLT", "UNKNOWN" ] } diff --git a/commons/passive/emotion/emotion_faros_acceleration.avsc b/commons/passive/emotion/emotion_faros_acceleration.avsc new file mode 100644 index 00000000..cbea1f09 --- /dev/null +++ b/commons/passive/emotion/emotion_faros_acceleration.avsc @@ -0,0 +1,13 @@ +{ + "namespace": "org.radarcns.passive.emotion", + "type": "record", + "name": "EmotionFarosAcceleration", + "doc": "Data from 3-axis accelerometer sensor of a eMotion Faros device.", + "fields": [ + { "name": "time", "type": "double", "doc": "Device timestamp in UTC (s)." }, + { "name": "timeReceived", "type": "double", "doc": "Device receiver timestamp in UTC (s)." } , + { "name": "x", "type": "float", "doc": "Acceleration in the x-axis (g)." }, + { "name": "y", "type": "float", "doc": "Acceleration in the y-axis (g)." }, + { "name": "z", "type": "float", "doc": "Acceleration in the z-axis (g)." } + ] +} diff --git a/commons/passive/emotion/emotion_faros_battery_level.avsc b/commons/passive/emotion/emotion_faros_battery_level.avsc new file mode 100644 index 00000000..1280946f --- /dev/null +++ b/commons/passive/emotion/emotion_faros_battery_level.avsc @@ -0,0 +1,11 @@ +{ + "namespace": "org.radarcns.passive.emotion", + "type": "record", + "name": "EmotionFarosBatteryLevel", + "doc": "Battery level of a eMotion Faros device.", + "fields": [ + { "name": "time", "type": "double", "doc": "Device timestamp in UTC (s)." }, + { "name": "timeReceived", "type": "double", "doc": "Device receiver timestamp in UTC (s)." }, + { "name": "batteryLevel", "type": "float", "doc": "Battery level from 0 to 1. Note that the battery level is a rough estimate." } + ] +} diff --git a/commons/passive/emotion/emotion_faros_ecg.avsc b/commons/passive/emotion/emotion_faros_ecg.avsc new file mode 100644 index 00000000..23100a2e --- /dev/null +++ b/commons/passive/emotion/emotion_faros_ecg.avsc @@ -0,0 +1,13 @@ +{ + "namespace": "org.radarcns.passive.emotion", + "type": "record", + "name": "EmotionFarosEcg", + "doc": "Data from eMotion Faros' multi-channel electrocardiography electrodes. Not all Faros models possess the same number of channels, so the second and third channels are optional.", + "fields": [ + { "name": "time", "type": "double", "doc": "Device timestamp in UTC (s)." }, + { "name": "timeReceived", "type": "double", "doc": "Device receiver timestamp in UTC (s)." } , + { "name": "ecgChannel1", "type": "float", "doc": "ECG signal from the first channel (µV)." }, + { "name": "ecgChannel2", "type": ["null", "float"], "doc": "ECG signal from the second channel (µV). Null if the device does not have at least two channels.", "default": null }, + { "name": "ecgChannel3", "type": ["null", "float"], "doc": "ECG signal from the first channel (µV). Null if the device does not have at least two three channels.", "default": null } + ] +} diff --git a/commons/passive/emotion/emotion_faros_inter_beat_interval.avsc b/commons/passive/emotion/emotion_faros_inter_beat_interval.avsc new file mode 100644 index 00000000..addbd4f1 --- /dev/null +++ b/commons/passive/emotion/emotion_faros_inter_beat_interval.avsc @@ -0,0 +1,11 @@ +{ + "namespace": "org.radarcns.passive.emotion", + "type": "record", + "name": "EmotionFarosInterBeatInterval", + "doc": "Time between two successive R-peaks (RR-interval), as calculated by a eMotion Faros device from the ECG signal. You can compute the heart rate as (60 / ibi).", + "fields": [ + {"name": "time", "type": "double", "doc": "Device timestamp in UTC (s)." }, + {"name": "timeReceived", "type": "double", "doc": "Device receiver timestamp in UTC (s)." }, + {"name": "interBeatInterval", "type": "float", "doc": "Duration (s) of the detected inter beat interval." } + ] +} diff --git a/commons/passive/emotion/emotion_faros_temperature.avsc b/commons/passive/emotion/emotion_faros_temperature.avsc new file mode 100644 index 00000000..d81574cf --- /dev/null +++ b/commons/passive/emotion/emotion_faros_temperature.avsc @@ -0,0 +1,11 @@ +{ + "namespace": "org.radarcns.passive.emotion", + "type": "record", + "name": "EmotionFarosTemperature", + "doc": "Data from an eMotion Faros' temperature sensor.", + "fields": [ + {"name": "time", "type": "double", "doc": "Device timestamp in UTC (s)." }, + {"name": "timeReceived", "type": "double", "doc": "Device receiver timestamp in UTC (s)." }, + {"name": "temperature", "type": "float", "doc": "Skin temperature (°C) converted from analog-to-digital converter (ADC) values. Conversion is reliable between 35 and 45 degrees Celcius." } + ] +} diff --git a/commons/stream/source_statistics.avsc b/commons/stream/source_statistics.avsc new file mode 100644 index 00000000..46b17355 --- /dev/null +++ b/commons/stream/source_statistics.avsc @@ -0,0 +1,11 @@ +{ + "namespace": "org.radarcns.stream", + "type": "record", + "name": "SourceStatistics", + "doc": "Statistics about single sources.", + "version": "1.0.0", + "fields": [ + { "name": "timeStart", "type": "double", "doc": "First time (seconds since the UNIX Epoch) that a source streamed any data." }, + { "name": "timeEnd", "type": "double", "doc": "Last time (seconds since the UNIX Epoch) that a source streamed any data." } + ] +} diff --git a/java-sdk/README.md b/java-sdk/README.md index f921b930..525278f7 100644 --- a/java-sdk/README.md +++ b/java-sdk/README.md @@ -9,13 +9,13 @@ repositories { dependencies { // Commons schemas (backend, passive remote monitoring app) - compile 'org.radarcns:radar-schemas-commons:0.3' + compile 'org.radarcns:radar-schemas-commons:0.3.1' // REST API schemas (REST API, testing) - compile 'org.radarcns:radar-schemas-restapi:0.3' + compile 'org.radarcns:radar-schemas-restapi:0.3.1' // Questionnaire schemas (active remote monitoring app) - compile 'org.radarcns:radar-schemas-tools:0.3' + compile 'org.radarcns:radar-schemas-tools:0.3.1' } ``` Usually, you only need to include the schemas you actually need in your dependencies. diff --git a/java-sdk/build.gradle b/java-sdk/build.gradle index e29c3848..777812ae 100644 --- a/java-sdk/build.gradle +++ b/java-sdk/build.gradle @@ -17,7 +17,7 @@ subprojects { apply plugin: 'idea' // Configuration - version = '0.3' + version = '0.3.1' group = 'org.radarcns' ext.githubRepoName = 'RADAR-CNS/RADAR-Schemas' @@ -128,17 +128,17 @@ subprojects { downloadSources = true } } -} -task downloadDependencies { - description "Pre-downloads *most* dependencies" - doLast { - configurations.getAsMap().each { name, config -> - println "Retrieving dependencies for $name" - try { - config.files - } catch (e) { - project.logger.info e.message // some cannot be resolved, silentlyish skip them + task downloadDependencies { + description "Pre-downloads *most* dependencies" + doLast { + configurations.getAsMap().each { name, config -> + println "Retrieving dependencies for $name" + try { + config.files + } catch (e) { + project.logger.info e.message // some cannot be resolved, silentlyish skip them + } } } } diff --git a/java-sdk/radar-schemas-tools/README.md b/java-sdk/radar-schemas-tools/README.md index c48d5e65..34abbf87 100644 --- a/java-sdk/radar-schemas-tools/README.md +++ b/java-sdk/radar-schemas-tools/README.md @@ -1,5 +1,9 @@ # RADAR Schemas tools +A number of tools are provided with RADAR-Schemas. They are most easily accessed by using the docker +image as described in the main readme. Below is more information about the schema validation that +the tools can perform. + The RADAR Schemas Validator checks if the `Schema Catalog` is in a valid state. It first checks the folder structure, it has to be compliant with: @@ -70,9 +74,8 @@ All tests are enable by default. ```yaml validation: - schema_to_skip: - - record_name_check: [ ENABLE | DISABLE ] - fields: + org.radarcns.passive.biovotion.BiovotionVSMSpO2: + - fields: - fieldnameOne - fieldnameTwo ``` diff --git a/java-sdk/radar-schemas-tools/build.gradle b/java-sdk/radar-schemas-tools/build.gradle index dad1d038..625b482e 100644 --- a/java-sdk/radar-schemas-tools/build.gradle +++ b/java-sdk/radar-schemas-tools/build.gradle @@ -3,10 +3,9 @@ plugins { id 'checkstyle' id 'pmd' id 'application' + id 'com.jfrog.artifactory' } -apply plugin: 'com.jfrog.artifactory' - ext.artifactName = 'radar-schemas-tools' ext.description = 'RADAR Schemas specification and validation tools.' @@ -23,8 +22,9 @@ sourceSets { ext.junitVersion = '4.12' ext.slf4jVersion = '1.7.25' -ext.jettyVersion = '9.4.7.v20170914' -ext.jerseyVersion = '2.9' +ext.jettyVersion = '9.4.8.v20171121' +ext.jerseyVersion = '2.26' + dependencies { api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.8.10' api group: 'javax.validation', name: 'validation-api', version: '2.0.0.Final' @@ -116,6 +116,11 @@ publishing { root.appendNode('name', artifactName) root.appendNode('url', githubUrl) root.children().last() + pomConfig + root.dependencies.'*'.findAll() { + it.artifactId.text() == 'slf4j-simple' + }.each() { + it.parent().remove(it) + } } } } diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/CommandLineApp.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/CommandLineApp.java index c9d16aeb..992076b4 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/CommandLineApp.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/CommandLineApp.java @@ -50,7 +50,7 @@ import static java.util.stream.Collectors.toList; /** - * TODO. + * Command line app containing a source catalogue. */ @SuppressWarnings("PMD.SystemPrintln") public class CommandLineApp { @@ -60,6 +60,12 @@ public class CommandLineApp { private final SourceCatalogue catalogue; private final Path root; + /** + * Command line app started at a RADAR-Schemas root. The source catalogue is read from the + * {@code specifications} directory in that root. + * @param root path to the root of a RADAR-Schemas directory. + * @throws IOException if the source catalogue cannot be loaded. + */ public CommandLineApp(Path root) throws IOException { this.root = root; this.catalogue = SourceCatalogue.load(root); @@ -163,6 +169,7 @@ private Map> getTopicsInfoVerbose(boolean prettyPrin DataTopic::getTopic, d -> d.toString(prettyPrint))))); } + /** Command to execute. */ public static void main(String... args) { SortedMap subCommands = commandsToMap( KafkaTopics.command(), @@ -221,6 +228,7 @@ private static SortedMap commandsToMap(SubCommand... command return map; } + /** Command to list the topics. */ private static SubCommand listCommand() { return new SubCommand() { @Override @@ -262,13 +270,21 @@ public void addParser(ArgumentParser parser) { }; } + /** + * Create a pattern to match given topic. If the exact match is non-null, it is returned + * as an exact match, otherwise if regex is non-null, it is used, and otherwise + * {@code null} is returned. + * @param exact string that should be exactly matched. + * @param regex string that should be matched as a regex. + * @return pattern or {@code null} if both exact and regex are {@code null}. + */ public static Pattern matchTopic(String exact, String regex) { if (exact != null) { return Pattern.compile("^" + Pattern.quote(exact) + "$"); - } - if (regex != null) { + } else if (regex != null) { return Pattern.compile(regex); + } else { + return null; } - return null; } } diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/registration/KafkaTopics.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/registration/KafkaTopics.java index b0ba5f80..94b2dea0 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/registration/KafkaTopics.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/registration/KafkaTopics.java @@ -1,5 +1,11 @@ package org.radarcns.schema.registration; +import static org.radarcns.schema.CommandLineApp.matchTopic; + +import java.io.Closeable; +import java.util.Optional; +import java.util.Properties; +import java.util.regex.Pattern; import kafka.admin.AdminUtils; import kafka.admin.RackAwareMode; import kafka.utils.ZKStringSerializer; @@ -8,22 +14,15 @@ import net.sourceforge.argparse4j.inf.Namespace; import org.I0Itec.zkclient.ZkClient; import org.I0Itec.zkclient.ZkConnection; +import org.I0Itec.zkclient.exception.ZkException; import org.I0Itec.zkclient.exception.ZkMarshallingError; import org.I0Itec.zkclient.serialize.ZkSerializer; -import org.apache.zookeeper.KeeperException; import org.radarcns.schema.CommandLineApp; import org.radarcns.schema.specification.SourceCatalogue; import org.radarcns.schema.util.SubCommand; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.Closeable; -import java.util.Optional; -import java.util.Properties; -import java.util.regex.Pattern; - -import static org.radarcns.schema.CommandLineApp.matchTopic; - /** * Registers Kafka topics with Zookeeper. */ @@ -60,10 +59,11 @@ public Object deserialize(byte[] bytes) throws ZkMarshallingError { * waiting for at most 200 seconds. * @param brokers number of brokers to wait for * @return whether the brokers where available - * @throws InterruptedException - * @throws KeeperException + * @throws InterruptedException when waiting for the brokers is interrepted. + * @throws ZkException if the Zookeeper instance cannot be reached or returns an unexpected + * result. */ - public boolean waitForBrokers(int brokers) throws InterruptedException, KeeperException { + public boolean waitForBrokers(int brokers) throws InterruptedException, ZkException { boolean brokersAvailable = false; int sleep = 2; for (int tries = 0; tries < 10; tries++) { @@ -124,7 +124,7 @@ public boolean createTopic(String topic, int partitions, int replication) { } } - public int getNumberOfBrokers() throws KeeperException, InterruptedException { + public int getNumberOfBrokers() throws ZkException { return zkUtils.getAllBrokersInCluster().length(); } @@ -185,8 +185,8 @@ public int execute(Namespace options, CommandLineApp app) { return result.get() ? 0 : 1; } } - } catch (InterruptedException | KeeperException e) { - logger.error("Cannot retrieve number of active Kafka brokers." + } catch (InterruptedException | ZkException e) { + logger.error("Cannot retrieve number of addActive Kafka brokers." + " Please check that Zookeeper is running."); return 1; } diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/registration/SchemaRegistry.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/registration/SchemaRegistry.java index 9a44db5c..b0d68817 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/registration/SchemaRegistry.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/registration/SchemaRegistry.java @@ -48,6 +48,9 @@ import static org.radarcns.schema.CommandLineApp.matchTopic; +/** + * Schema registry interface. + */ public class SchemaRegistry implements Closeable { public enum Compatibility { NONE, FULL, BACKWARD, FORWARD, BACKWARD_TRANSITIVE, FORWARD_TRANSITIVE, FULL_TRANSITIVE @@ -57,6 +60,11 @@ public enum Compatibility { private final SchemaRetriever schemaClient; private final RestClient httpClient; + /** + * Schema registry for given URL. If this is https, unsafe certificates are accepted. + * @param baseUrl URL of the schema registry + * @throws MalformedURLException if given URL is invalid. + */ public SchemaRegistry(String baseUrl) throws MalformedURLException { ServerConfig config = new ServerConfig(baseUrl); config.setUnsafe(true); @@ -64,6 +72,11 @@ public SchemaRegistry(String baseUrl) throws MalformedURLException { this.httpClient = new RestClient(config, 10, ManagedConnectionPool.GLOBAL_POOL); } + /** + * Register all schemas in a source catalogue. Stream sources are ignored. + * @param catalogue schema catalogue to read schemas from + * @return whether all schemas were successfully registered. + */ public boolean registerSchemas(SourceCatalogue catalogue) { return Stream.of( catalogue.getActiveSources(), @@ -74,6 +87,7 @@ public boolean registerSchemas(SourceCatalogue catalogue) { .allMatch(this::registerSchema); } + /** Register the schema of a single topic. */ public boolean registerSchema(AvroTopic topic) { try { Schema schema = topic.getKeySchema(); @@ -94,6 +108,11 @@ public boolean registerSchema(AvroTopic topic) { } } + /** + * Set the compatibility level of the schema registry. + * @param compatibility target compatibility level. + * @return whether the request was successful. + */ public boolean setCompatibility(Compatibility compatibility) { logger.info("Setting compatibility to {}", compatibility); @@ -136,11 +155,13 @@ public void writeTo(BufferedSink sink) throws IOException { } } + @Override public void close() { schemaClient.close(); httpClient.close(); } + /** Return the schema registry as a subcommand. */ public static SubCommand command() { return new RegisterCommand(); } diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/service/SourceCatalogueService.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/service/SourceCatalogueService.java index 3fccb14d..5baf8a18 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/service/SourceCatalogueService.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/service/SourceCatalogueService.java @@ -23,42 +23,52 @@ @Produces(MediaType.APPLICATION_JSON) public class SourceCatalogueService { - private static final String SOURCE_TYPE_CLASS_HEADER = "Source-Type-Class"; private final SourceCatalogue sourceCatalogue; SourceCatalogueService(SourceCatalogue sourceCatalogue) { this.sourceCatalogue = sourceCatalogue; } + /** Get all passive sources from the source catalogue. */ @GET @Path("/passive") public Response getPassiveSources() { - return Response.ok().entity(new SourceTypeResponse(this.sourceCatalogue).passive()) - .header(SOURCE_TYPE_CLASS_HEADER, "PASSIVE").build(); + return Response.ok() + .entity(new SourceTypeResponse(this.sourceCatalogue).addPassive()) + .build(); } + /** Get all passive sources from the source catalogue. */ @GET @Path("/active") public Response getActiveSources() { - return Response.ok().entity(new SourceTypeResponse(this.sourceCatalogue).active()) - .header(SOURCE_TYPE_CLASS_HEADER, "ACTIVE").build(); + return Response.ok() + .entity(new SourceTypeResponse(this.sourceCatalogue).addActive()) + .build(); } + /** Get all monitor sources from the source catalogue. */ @GET @Path("/monitor") public Response getMonitorSources() { - return Response.ok().entity(new SourceTypeResponse(this.sourceCatalogue).monitor()) - .header(SOURCE_TYPE_CLASS_HEADER, "MONITOR").build(); + return Response.ok() + .entity(new SourceTypeResponse(this.sourceCatalogue).addMonitor()) + .build(); } + /** Get all sources from the source catalogue. */ @GET public Response getAllSourceTypes() { - return Response.ok().entity(new SourceTypeResponse(this.sourceCatalogue).all()) - .header(SOURCE_TYPE_CLASS_HEADER, "ALL").build(); + return Response.ok() + .entity(new SourceTypeResponse(this.sourceCatalogue) + .addPassive() + .addActive() + .addMonitor()) + .build(); } + /** Response with source types. */ public class SourceTypeResponse { - @JsonIgnore private final SourceCatalogue sourceCatalogue; @@ -75,27 +85,18 @@ private SourceTypeResponse(SourceCatalogue sourceCatalogue) { this.sourceCatalogue = sourceCatalogue; } - private SourceTypeResponse passive() { + private SourceTypeResponse addPassive() { this.passiveSources = new ArrayList<>( this.sourceCatalogue.getPassiveSources().values()); return this; } - public SourceTypeResponse active() { + private SourceTypeResponse addActive() { this.activeSources = new ArrayList<>(this.sourceCatalogue.getActiveSources().values()); return this; } - public SourceTypeResponse monitor() { - this.monitorSources = new ArrayList<>( - this.sourceCatalogue.getMonitorSources().values()); - return this; - } - - private SourceTypeResponse all() { - this.passiveSources = new ArrayList<>( - this.sourceCatalogue.getPassiveSources().values()); - this.activeSources = new ArrayList<>(this.sourceCatalogue.getActiveSources().values()); + private SourceTypeResponse addMonitor() { this.monitorSources = new ArrayList<>( this.sourceCatalogue.getMonitorSources().values()); return this; diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/DataTopic.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/DataTopic.java index 731f382e..d1d27917 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/DataTopic.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/DataTopic.java @@ -27,18 +27,23 @@ public class DataTopic extends AvroTopicConfig { private static final Logger logger = LoggerFactory.getLogger(DataTopic.class); + /** Type of topic. Its meaning is class-specific.*/ @JsonProperty private String type; + /** Documentation string for this topic. */ @JsonProperty private String doc; + /** Sampling rate, how frequently messages are expected to be sent on average. */ @JsonProperty("sample_rate") private SampleRateConfig sampleRate; + /** Output unit. */ @JsonProperty private Unit unit; + /** Record fields that the given unit applies to. */ @JsonProperty private List fields; @@ -103,6 +108,11 @@ public String toString() { return toString(false); } + /** + * Convert the topic to String, either as dense string or as verbose YAML string. + * @param prettyString Whether the result should be a verbose pretty-printed string. + * @return topic as a string. + */ public String toString(boolean prettyString) { String name = getClass().getSimpleName(); // preserves insertion order @@ -126,6 +136,11 @@ public String toString(boolean prettyString) { } } + /** + * Turns this topic into an descriptive properties map. + * @param map properties to add to. + * @param reduced whether to set a reduced set of properties, to decrease verbosity. + */ protected void propertiesMap(Map map, boolean reduced) { map.put("type", type); if (!reduced && doc != null) { diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/SourceCatalogue.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/SourceCatalogue.java index b1c418a9..36d9d576 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/SourceCatalogue.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/SourceCatalogue.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.PropertyNamingStrategy; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import java.nio.file.InvalidPathException; import org.radarcns.schema.Scope; import org.radarcns.schema.specification.active.ActiveSource; import org.radarcns.schema.specification.stream.StreamGroup; @@ -80,6 +81,14 @@ public class SourceCatalogue { sources.addAll(streamGroups.values()); } + /** + * Load the source catalogue based at the given root directory. + * @param root Directory containing a specifications subdirectory. + * @return parsed source catalogue. + * @throws InvalidPathException if the {@code specifications} directory cannot be found in given + * root. + * @throws IOException if the source catalogue could not be read. + */ public static SourceCatalogue load(Path root) throws IOException { Path specRoot = root.resolve("specifications"); @@ -195,6 +204,7 @@ public Stream getTopicNames() { .flatMap(DataProducer::getTopicNames); } + /** Get all topics in the catalogue. */ public Stream> getTopics() { return sources.stream() .flatMap(DataProducer::getTopics); diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/stream/StreamDataTopic.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/stream/StreamDataTopic.java index 9a7e62cc..40e892b7 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/stream/StreamDataTopic.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/stream/StreamDataTopic.java @@ -1,35 +1,37 @@ package org.radarcns.schema.specification.stream; +import static org.radarcns.schema.util.Utils.applyOrEmpty; + import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSetter; -import org.radarcns.catalogue.Unit; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; import org.radarcns.config.AvroTopicConfig; -import org.radarcns.kafka.ObservationKey; import org.radarcns.kafka.AggregateKey; +import org.radarcns.kafka.ObservationKey; import org.radarcns.schema.specification.DataTopic; import org.radarcns.stream.TimeWindowMetadata; import org.radarcns.topic.AvroTopic; -import javax.validation.constraints.NotBlank; -import java.io.IOException; -import java.util.Arrays; -import java.util.Map; -import java.util.stream.Stream; - -import static org.radarcns.schema.util.Utils.applyOrEmpty; - public class StreamDataTopic extends DataTopic { + /** Whether the stream is a windowed stream with standard TimeWindow windows. */ @JsonProperty private boolean windowed = false; - @JsonProperty - private Unit unit; - - @JsonProperty("input_topic") - @NotBlank - private String inputTopic; + /** Input topic for the stream. */ + @JsonProperty("input_topics") + private final List inputTopics = new ArrayList<>(); + /** + * Base topic name for output topics. If windowed, output topics would become + * {@code [topicBase]_[time-frame]}, otherwise it becomes {@code [topicBase]_output}. + * If a fixed topic is set, this will override the topic base for non-windowed topics. + */ @JsonProperty("topic_base") private String topicBase; @@ -43,18 +45,24 @@ private void setWindowed(boolean windowed) { } } - @JsonSetter + @JsonSetter("input_topic") @SuppressWarnings("PMD.UnusedPrivateMethod") private void setInputTopic(String inputTopic) { if (topicBase == null) { topicBase = inputTopic; } - this.inputTopic = inputTopic; + if (!this.inputTopics.isEmpty()) { + throw new IllegalStateException("Input topics already set"); + } + this.inputTopics.add(inputTopic); } + /** Get human readable output topic. */ public String getTopic() { if (windowed) { return topicBase + "_"; + } else if (super.getTopic() != null) { + return super.getTopic(); } else { return topicBase + "_output"; } @@ -64,18 +72,25 @@ public boolean isWindowed() { return windowed; } - public String getInputTopic() { - return inputTopic; + /** Get the input topics. */ + public List getInputTopics() { + return inputTopics; } - public Unit getUnit() { - return unit; + @JsonSetter + @SuppressWarnings("PMD.UnusedPrivateMethod") + private void setInputTopics(Collection topics) { + if (!this.inputTopics.isEmpty()) { + throw new IllegalStateException("Input topics already set"); + } + this.inputTopics.addAll(topics); } public String getTopicBase() { return topicBase; } + @Override public Stream getTopicNames() { if (windowed) { return Arrays.stream(TimeWindowMetadata.values()) @@ -90,7 +105,8 @@ public Stream getTopicNames() { } } - public Stream> getTopics() throws IOException { + @Override + public Stream> getTopics() { return getTopicNames() .flatMap(applyOrEmpty(topic -> { AvroTopicConfig config = new AvroTopicConfig(); @@ -101,6 +117,7 @@ public Stream getTopicNames() { })); } + /** Get only topic names that are used with the fixed time windows. */ public Stream getTimedTopicNames() { if (windowed) { return getTopicNames(); @@ -111,15 +128,10 @@ public Stream getTimedTopicNames() { @Override protected void propertiesMap(Map properties, boolean reduce) { - properties.put("input_topic", inputTopic); + properties.put("input_topics", inputTopics); properties.put("windowed", windowed); if (!reduce) { - if (!topicBase.equals(inputTopic)) { - properties.put("topic_base", topicBase); - } - if (unit != null) { - properties.put("unit", unit); - } + properties.put("topic_base", topicBase); } } } diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/stream/StreamGroup.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/stream/StreamGroup.java index e238b951..4dfcdc8f 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/stream/StreamGroup.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/specification/stream/StreamGroup.java @@ -25,6 +25,7 @@ public Scope getScope() { return Scope.STREAM; } + /** Get only the topic names that are the output of a timed stream. */ public Stream getTimedTopicNames() { return data.stream().flatMap(StreamDataTopic::getTimedTopicNames); } diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/util/SubCommand.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/util/SubCommand.java index 27855214..80b9ba9e 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/util/SubCommand.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/util/SubCommand.java @@ -4,13 +4,31 @@ import net.sourceforge.argparse4j.inf.Namespace; import org.radarcns.schema.CommandLineApp; +/** Subcommand of the command line utility. */ public interface SubCommand { + /** Name of the command to be used on the command line. */ String getName(); + /** + * Execute the subcommand based on the options and app given. + * @param options the options passed on the command line. + * @param app application with source catalogue. + * @return command exit code. + */ int execute(Namespace options, CommandLineApp app); + /** + * Add the description and arguments for this sub-command to the argument parser. The values + * of the arguments will then be passed to {@link #execute(Namespace, CommandLineApp)}. + * + * @param parser argument parser of the current subcommand. + */ void addParser(ArgumentParser parser); + /** + * Adds the root directory as an argument. This should be called as the last option, + * if at all. + */ static void addRootArgument(ArgumentParser parser) { parser.addArgument("root") .nargs("?") diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/util/Utils.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/util/Utils.java index 85e6d179..452444f6 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/util/Utils.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/util/Utils.java @@ -69,6 +69,11 @@ public static synchronized String getProjectGroup() { return projectGroup; } + /** + * Expand a class name with the group name if it starts with a dot. + * @param classShorthand class name, possibly starting with a dot as a shorthand. + * @return class name or {@code null} if null or empty. + */ public static String expandClass(String classShorthand) { if (classShorthand == null || classShorthand.isEmpty()) { return null; @@ -79,7 +84,14 @@ public static String expandClass(String classShorthand) { } } - public static String toSnakeCase(String value) { + /** + * Converts given file name from snake_case to CamelCase. This will cause underscores to be + * removed, and the next character to be uppercase. This only converts the value up to the + * first dot encountered. + * @param value file name in snake_case + * @return main part of file name in CamelCase. + */ + public static String snakeToCamelCase(String value) { char[] fileName = value.toCharArray(); StringBuilder builder = new StringBuilder(fileName.length); @@ -106,6 +118,7 @@ public static String toSnakeCase(String value) { return builder.toString(); } + /** Apply a throwing function, and if it throws, log it and let it return an empty Stream. */ public static Function> applyOrEmpty(ThrowingFunction> func) { return t -> { try { @@ -117,6 +130,7 @@ public static Function> applyOrEmpty(ThrowingFunction Predicate testOrFalse(ThrowingPredicate test) { return t -> { try { @@ -128,15 +142,36 @@ public static Predicate testOrFalse(ThrowingPredicate test) { }; } + /** + * Function that may throw an exception. + * @param type of value taken. + * @param type of value returned. + */ @FunctionalInterface @SuppressWarnings("PMD.SignatureDeclareThrowsException") public interface ThrowingFunction { + /** + * Apply containing function. + * @param value value to apply function to. + * @return result of the function. + * @throws Exception if the function fails. + */ R apply(T value) throws Exception; } + /** + * Predicate that may throw an exception. + * @param type of value taken. + */ @FunctionalInterface @SuppressWarnings("PMD.SignatureDeclareThrowsException") public interface ThrowingPredicate { + /** + * Test containing predicate. + * @param value value to test predicate for. + * @return whether the predicate tests true. + * @throws Exception if the predicate fails to evaluate. + */ boolean test(T value) throws Exception; } } diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/SchemaValidator.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/SchemaValidator.java index ff30a32d..8fe8f8f2 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/SchemaValidator.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/SchemaValidator.java @@ -48,6 +48,9 @@ import static org.radarcns.schema.SchemaRepository.COMMONS_PATH; import static org.radarcns.schema.validation.rules.Validator.raise; +/** + * Validator for a set of RADAR-Schemas. + */ public class SchemaValidator { public static final String AVRO_EXTENSION = "avsc"; @@ -56,6 +59,11 @@ public class SchemaValidator { private final Validator validator; private final SchemaMetadataRules rules; + /** + * Schema validator for given RADAR-Schemas directory. + * @param root RADAR-Schemas directory. + * @param config configuration to exclude certain schemas or fields from validation. + */ public SchemaValidator(Path root, ExcludeConfig config) { this.config = config; this.root = root; @@ -131,16 +139,15 @@ public Stream analyseFiles() .flatMap(this::analyseFiles); } + /** Validate a single schema in given path. */ public Stream validate(Schema schema, Path path, Scope scope) { return validator.apply(new SchemaMetadata(schema, scope, path)); } - public static Stream formatStream(Stream exceptionStream) { - return exceptionStream.map(ex -> "Validation FAILED:\n" + ex.getMessage() + "\n\n"); - } - + /** Formats a stream of validation exceptions. */ public static String format(Stream exceptionStream) { - return SchemaValidator.formatStream(exceptionStream) + return exceptionStream + .map(ex -> "Validation FAILED:\n" + ex.getMessage() + "\n\n") .collect(Collectors.joining()); } @@ -149,7 +156,7 @@ public static String format(Stream exceptionStream) { * @param file TODO * @return TODO */ - public static boolean isAvscFile(Path file) { + private static boolean isAvscFile(Path file) { return ValidationSupport.matchesExtension(file, AVRO_EXTENSION); } @@ -170,6 +177,7 @@ public Map getValidatedSchemas() { return ((RadarSchemaRules) rules.getSchemaRules()).getSchemaStore(); } + /** Schema validator as a command. */ public static SubCommand command() { return new SchemaValidatorCommand(); } diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/SpecificationsValidator.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/SpecificationsValidator.java index 527f5220..14fd1763 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/SpecificationsValidator.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/SpecificationsValidator.java @@ -26,18 +26,24 @@ import static org.radarcns.schema.SchemaRepository.SPECIFICATIONS_PATH; /** - * TODO. + * Validates RADAR-Schemas specifications. */ public class SpecificationsValidator { public static final String YML_EXTENSION = "yml"; private final ExcludeConfig config; private final Path root; + /** + * Specifications validator for given RADAR-Schemas directory. + * @param root RADAR-Schemas directory. + * @param config configuration to exclude certain schemas or fields from validation. + */ public SpecificationsValidator(Path root, ExcludeConfig config) { this.root = root; this.config = config; } + /** Check that all files in the specifications directory are YAML files. */ public boolean specificationsAreYmlFiles(Scope scope) throws IOException { return Files.walk(scope.getPath(root.resolve(SPECIFICATIONS_PATH))) .filter(Files::isRegularFile) diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/ValidationSupport.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/ValidationSupport.java index a467f18f..d3cd980d 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/ValidationSupport.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/ValidationSupport.java @@ -28,7 +28,7 @@ import static org.radarcns.schema.SchemaRepository.COMMONS_PATH; import static org.radarcns.schema.util.Utils.getProjectGroup; -import static org.radarcns.schema.util.Utils.toSnakeCase; +import static org.radarcns.schema.util.Utils.snakeToCamelCase; /** * TODO. @@ -92,7 +92,7 @@ public static String getNamespace(Path root, Path schemaPath, Scope scope) { public static String getRecordName(Path path) { Objects.requireNonNull(path); - return toSnakeCase(path.getFileName().toString()); + return snakeToCamelCase(path.getFileName().toString()); } /** diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/config/ExcludeConfig.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/config/ExcludeConfig.java index 3d3d8d53..8277cdd6 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/config/ExcludeConfig.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/config/ExcludeConfig.java @@ -1,5 +1,3 @@ -package org.radarcns.schema.validation.config; - /* * Copyright 2017 King's College London and The Hyve * @@ -16,6 +14,8 @@ * limitations under the License. */ +package org.radarcns.schema.validation.config; + import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.databind.ObjectMapper; @@ -59,7 +59,7 @@ public class ExcludeConfig { private static final String FILE_NAME = "schema.yml"; /** Wild card to suppress check for entire package. */ - public static final String WILD_CARD_PACKAGE = ".*"; + private static final String WILD_CARD_PACKAGE = ".*"; /** Regex for validating the yml file. */ public static final String VALID_INPUT_REGEX = "^[a-z][a-zA-Z0-9.*]*$"; @@ -103,29 +103,6 @@ private static boolean validateClass(Stream stream) { return stream.allMatch(value -> value.matches(VALID_INPUT_REGEX)); } - /** - * TODO. - * @param schema TODO - * @return TODO - */ - public boolean contains(Schema schema) { - return validation.containsKey(schema.getNamespace() + WILD_CARD_PACKAGE) - || validation.containsKey(schema.getFullName()); - } - - /** - * TODO. - * @param schema TODO - * @return TODO - */ - public boolean isNameRecordEnable(Schema schema) { - ConfigItem item = validation.get(schema.getFullName()) == null - ? validation.get(schema.getNamespace() + WILD_CARD_PACKAGE) - : validation.get(schema.getFullName()); - - return item == null || item.isNameRecordDisable(); - } - /** * TODO. * @return TODO @@ -170,6 +147,7 @@ public void setFiles(String... files) { setFiles(Arrays.asList(files)); } + /** Set the files to be excluded. */ @JsonSetter("files") public void setFiles(Collection files) { FileSystem fs = FileSystems.getDefault(); @@ -202,6 +180,7 @@ public Set getFiles() { return Collections.unmodifiableSet(files); } + /** Set the validation to be excluded. */ @JsonSetter("validation") public void setValidation(Map validation) { //Validate validation key map diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/RadarSchemaFieldRules.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/RadarSchemaFieldRules.java index a81ad842..eee20b82 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/RadarSchemaFieldRules.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/RadarSchemaFieldRules.java @@ -17,6 +17,9 @@ import static org.radarcns.schema.validation.rules.Validator.valid; import static org.radarcns.schema.validation.rules.Validator.validateNonNull; +/** + * Rules for RADAR-Schemas schema fields. + */ public class RadarSchemaFieldRules implements SchemaFieldRules { private static final String UNKNOWN = "UNKNOWN"; private static final List FIELD_NAME_NOT_ALLOWED_SUFFIX = Arrays.asList( @@ -28,6 +31,9 @@ public class RadarSchemaFieldRules implements SchemaFieldRules { private final Map> defaultsValidator; + /** + * Rules for RADAR-Schemas schema fields. + */ public RadarSchemaFieldRules() { defaultsValidator = new HashMap<>(); defaultsValidator.put(Schema.Type.ENUM, this::validateDefaultEnum); diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/RadarSchemaMetadataRules.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/RadarSchemaMetadataRules.java index a5e09837..86b43dee 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/RadarSchemaMetadataRules.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/RadarSchemaMetadataRules.java @@ -10,15 +10,28 @@ import static org.radarcns.schema.validation.rules.Validator.raise; import static org.radarcns.schema.validation.rules.Validator.valid; +/** Rules for schemas with metadata in RADAR-Schemas. */ public class RadarSchemaMetadataRules implements SchemaMetadataRules { private final SchemaRules schemaRules; private final Path root; private final ExcludeConfig config; + /** + * Rules for schemas with metadata in RADAR-Schemas. {@link RadarSchemaRules} is used as schema + * rules implementation. + * @param root directory of RADAR-Schemas + * @param config configuration for excluding schemas from validation. + */ public RadarSchemaMetadataRules(Path root, ExcludeConfig config) { this(root, config, new RadarSchemaRules(config)); } + /** + * Rules for schemas with metadata in RADAR-Schemas. + * @param root directory of RADAR-Schemas + * @param config configuration for excluding schemas from validation. + * @param schemaRules schema rules implementation. + */ public RadarSchemaMetadataRules(Path root, ExcludeConfig config, SchemaRules schemaRules) { this.schemaRules = schemaRules; this.config = config; diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/SchemaFieldRules.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/SchemaFieldRules.java index c73a0be5..002cdef3 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/SchemaFieldRules.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/SchemaFieldRules.java @@ -20,6 +20,7 @@ public interface SchemaFieldRules { /** Checks field default values. */ Validator validateDefault(); + /** Get a validator for a field. */ default Validator getValidator(SchemaRules schemaRules) { return validateFieldTypes(schemaRules) .and(validateFieldName()) @@ -27,6 +28,7 @@ default Validator getValidator(SchemaRules schemaRules) { .and(validateFieldDocumentation()); } + /** Get a validator for a union inside a record. */ default Validator validateInternalUnion(SchemaRules schemaRules) { return field -> field.getField().schema().getTypes().stream() .flatMap(schema -> { @@ -44,6 +46,7 @@ default Validator validateInternalUnion(SchemaRules schemaRules) { }); } + /** A message function for a field, ending with given text. */ default Function message(String text) { return schema -> "Field " + schema.getField().name() + " in schema " + schema.getSchema().getFullName() + " is invalid. " + text; diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/SchemaMetadata.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/SchemaMetadata.java index 11836025..679d1c12 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/SchemaMetadata.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/SchemaMetadata.java @@ -5,15 +5,24 @@ import java.nio.file.Path; +/** + * Schema with metadata. + */ public class SchemaMetadata { private final Schema schema; private final Scope scope; private final Path path; + /** + * Schema with {@code null} metadata. + */ public SchemaMetadata(Schema schema) { this(schema, null, null); } + /** + * Schema with metadata. + */ public SchemaMetadata(Schema schema, Scope scope, Path path) { this.schema = schema; this.scope = scope; @@ -31,8 +40,4 @@ public Scope getScope() { public Schema getSchema() { return schema; } - - public SchemaMetadata withSubSchema(Schema schema) { - return new SchemaMetadata(schema, scope, path); - } } diff --git a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/SchemaRules.java b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/SchemaRules.java index 5a5d5fd8..d9bd97ff 100644 --- a/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/SchemaRules.java +++ b/java-sdk/radar-schemas-tools/src/main/java/org/radarcns/schema/validation/rules/SchemaRules.java @@ -24,21 +24,32 @@ public interface SchemaRules { /** Checks that the symbols of enums have the required format. */ Validator validateSymbols(); - /** Checks that schemas should have a {@code time} field. */ + /** + * Checks that schemas should have a {@code time} field. + */ Validator validateTime(); - /** Checks that schemas should have a {@code timeCompleted} field. */ + /** + * Checks that schemas should have a {@code timeCompleted} field. + */ Validator validateTimeCompleted(); - /** Checks that schemas should not have a {@code timeCompleted} field. */ + /** + * Checks that schemas should not have a {@code timeCompleted} field. + */ Validator validateNotTimeCompleted(); - /** Checks that schemas should have a {@code timeReceived} field. */ + /** + * Checks that schemas should have a {@code timeReceived} field. + */ Validator validateTimeReceived(); - /** Checks that schemas should not have a {@code timeReceived} field. */ + /** + * Checks that schemas should not have a {@code timeReceived} field. + */ Validator validateNotTimeReceived(); + /** Validate an enum. */ default Validator validateEnum() { return validateUniqueness() .and(validateNameSpace()) @@ -57,7 +68,7 @@ default Validator validateRecord() { } /** - * Validates record schemas of an active source + * Validates record schemas of an active source. * @return TODO */ default Validator validateActiveSource() { diff --git a/java-sdk/radar-schemas-tools/src/test/java/org/radarcns/schema/validation/PassiveValidation.java b/java-sdk/radar-schemas-tools/src/test/java/org/radarcns/schema/validation/PassiveValidation.java index c32c27eb..04b6067f 100644 --- a/java-sdk/radar-schemas-tools/src/test/java/org/radarcns/schema/validation/PassiveValidation.java +++ b/java-sdk/radar-schemas-tools/src/test/java/org/radarcns/schema/validation/PassiveValidation.java @@ -31,6 +31,9 @@ public class PassiveValidation { private static Map converter; + /** + * Set up converters. + */ @BeforeClass public static void initConverter() { converter = new HashMap<>(); diff --git a/specifications/passive/emotion_faros-1.0.0.yml b/specifications/passive/emotion_faros-1.0.0.yml new file mode 100644 index 00000000..038ef489 --- /dev/null +++ b/specifications/passive/emotion_faros-1.0.0.yml @@ -0,0 +1,41 @@ +#====================================== Empatica E4 Wristband =====================================# +vendor: eMotion +model: Faros +version: 1.0.0 +app_provider: .emotion.FarosServiceProvider +data: + - type: ACCELEROMETER + sample_rate: + dynamic: true + unit: G + processing_state: RAW + topic: android_emotion_faros_acceleration + value_schema: .passive.emotion.EmotionFarosAcceleration + - type: BATTERY + sample_rate: + dynamic: true + unit: PERCENTAGE + processing_state: RAW + topic: android_emotion_faros_battery_level + value_schema: .passive.empatica.EmpaticaE4BatteryLevel + - type: ECG + sample_rate: + dynamic: true + unit: MICRO_VOLT + processing_state: RAW + topic: android_emotion_faros_ecg + value_schema: .passive.emotion.EmotionFarosEcg + - type: INTER_BEAT_INTERVAL + sample_rate: + dynamic: true + unit: SECOND + processing_state: VENDOR + topic: android_emotion_faros_inter_beat_interval + value_schema: .passive.emotion.EmotionFarosInterBeatInterval + - type: THERMOMETER + sample_rate: + dynamic: true + unit: CELSIUS + processing_state: VENDOR + topic: android_emotion_faros_acceleration + value_schema: .passive.emotion.EmotionFarosTemperature diff --git a/specifications/stream/statistics.yml b/specifications/stream/statistics.yml new file mode 100644 index 00000000..a6bcdf26 --- /dev/null +++ b/specifications/stream/statistics.yml @@ -0,0 +1,24 @@ +name: STREAM_STATISTICS +doc: Statistics about the streaming data. +data: + - topic: source_statistics_empatica_e4 + input_topics: + - android_empatica_e4_blood_volume_pulse_1min + value_schema: .stream.SourceStatistics + doc: Statistics about E4 devices. + - topic: source_statistics_radar_prmt + input_topics: + - android_phone_acceleration_1min + - android_phone_bluetooth_devices + - android_phone_sms + - android_phone_call + - android_phone_contacts + - android_phone_usage_event + - android_phone_relative_location + value_schema: .stream.SourceStatistics + doc: Statistics about Android devices. + - topic: source_statistics_biovotion_vsm1 + input_topics: + - android_biovotion_vsm1_acceleration_1min + value_schema: .stream.SourceStatistics + doc: Statistics about Biovotion devices.