bug6059: removed Igtimi "Resource" concept; all we need is their *data*;

made Selenium API tests work
This commit is contained in:
Axel Uhl
2025-01-07 23:46:33 +01:00
parent 65167ae2f9
commit 2ff915c034
28 changed files with 52 additions and 541 deletions
@@ -102,7 +102,6 @@ public class SecuredDomainType extends HasPermissionsImpl {
@Deprecated
public static final HasPermissions IGTIMI_ACCOUNT = new SecuredDomainType("IGTIMI_ACCOUNT");
public static final HasPermissions IGTIMI_DEVICE = new SecuredDomainType("IGTIMI_DEVICE");
public static final HasPermissions IGTIMI_RESOURCE = new SecuredDomainType("IGTIMI_RESOURCE");
public static final HasPermissions IGTIMI_DATA_ACCESS_WINDOW = new SecuredDomainType("IGTIMI_DATA_ACCESS_WINDOW");
public static final HasPermissions SWISS_TIMING_ACCOUNT = new SecuredDomainType("SWISS_TIMING_ACCOUNT");
public static final HasPermissions SWISS_TIMING_ARCHIVE_ACCOUNT = new SecuredDomainType(
@@ -57,7 +57,7 @@ public class RiotDevicesResource extends AbstractRiotServerResource {
final Device device = new DeviceDeserializer().createDeviceFromJson(deviceJson);
securityService.setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
device.getPermissionType(), device.getIdentifier().getTypeRelativeObjectIdentifier(),
device.getName(), ()->riot.addDevice(device));
device.getName(), ()->riot.createDevice(device.getSerialNumber()));
return Response.ok().build();
}
@@ -1,8 +1,6 @@
package com.sap.sailing.domain.igtimiadapter.gateway.impl;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.HashMap;
@@ -10,13 +8,8 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
@@ -29,26 +22,19 @@ import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import com.igtimi.IgtimiData.DataPoint.DataCase;
import com.igtimi.IgtimiStream.Msg;
import com.sap.sailing.domain.common.security.SecuredDomainType;
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
import com.sap.sailing.domain.igtimiadapter.Device;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sailing.domain.igtimiadapter.impl.ResourceDeserializer;
import com.sap.sailing.domain.igtimiadapter.impl.ResourceSerializer;
import com.sap.sailing.domain.igtimiadapter.server.riot.RiotServer;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.Util;
import com.sap.sse.security.SecurityService;
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
@Path(RestApiApplication.API + RestApiApplication.V1 + RiotResourcesResource.RESOURCES)
public class RiotResourcesResource extends AbstractRiotServerResource {
@@ -56,29 +42,6 @@ public class RiotResourcesResource extends AbstractRiotServerResource {
private static final String DATA = "/data";
private static final String LATEST = "/latest";
@GET
@Produces("application/json;charset=UTF-8")
public Response getResources(@QueryParam("permission") String permission,
@QueryParam("start_time") String startTime, @QueryParam("end_time") String endTime,
@QueryParam("serial_numbers[]") Set<String> serialNumbers,
@QueryParam("stream_ids[]") Set<String> streamIds)
throws ClientProtocolException, IllegalStateException, IOException, ParseException {
final RiotServer riot = Activator.getInstance().getRiotServer();
final JSONObject result = new JSONObject();
final JSONArray resourcesJson = new JSONArray();
result.put("resources", resourcesJson);
for (final Resource resource : riot.getResources()) {
if (SecurityUtils.getSubject().isPermitted(resource.getIdentifier().getStringPermission(DefaultActions.READ))
&& (serialNumbers.isEmpty() || serialNumbers.contains(resource.getDeviceSerialNumber()))
&& TimeRange.create(startTime == null ? null : TimePoint.of(Long.valueOf(startTime)),
endTime == null ? null : TimePoint.of(Long.valueOf(endTime))).intersects(
resource.getTimeRange())) {
resourcesJson.add(new ResourceSerializer().createJsonFromResource(resource));
}
}
return Response.ok(streamingOutput(result)).build();
}
/**
* Output is the same format as for {@link #getResourcesData(UriInfo)}
*/
@@ -109,20 +72,6 @@ public class RiotResourcesResource extends AbstractRiotServerResource {
return Response.ok(streamingOutput(result)).build();
}
@POST
@Consumes("application/json;charset=UTF-8")
public Response postResource(@Context HttpServletRequest request) throws IOException, ParseException {
final RiotServer riot = getRiotService();
final SecurityService securityService = getSecurityService();
final InputStream inputStream = request.getInputStream();
final JSONObject resourceJson = (JSONObject) new JSONParser().parse(new InputStreamReader(inputStream));
final Resource resource = new ResourceDeserializer().createResourceFromJson(resourceJson);
securityService.setOwnershipCheckPermissionForObjectCreationAndRevertOnError(SecuredDomainType.IGTIMI_RESOURCE,
new TypeRelativeObjectIdentifier(Long.toString(resource.getId())), resource.getName(),
()->riot.addResource(resource));
return Response.ok().build();
}
/**
* A response of a successful request contains a {@link JSONObject} where the keys are the device serial numbers,
* and where the values are arrays of {@link String}s representing Base64-encoded binary protobuf messages that are
@@ -185,18 +134,4 @@ public class RiotResourcesResource extends AbstractRiotServerResource {
return subject.isPermitted(daw.getIdentifier().getStringPermission(DefaultActions.READ));
});
}
@Path("{id}")
@DELETE
public Response deleteResource(@PathParam("id") long id) throws IOException, ParseException {
final RiotServer riot = getRiotService();
final SecurityService securityService = getSecurityService();
final Resource resource = riot.getResourceById(id);
if (resource != null) {
securityService.checkCurrentUserDeletePermission(resource);
riot.removeResource(id);
securityService.deleteAllDataForRemovedObject(resource.getIdentifier());
}
return Response.ok().build();
}
}
@@ -9,12 +9,9 @@ import com.igtimi.IgtimiStream.Msg;
import com.mongodb.client.ClientSession;
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
import com.sap.sailing.domain.igtimiadapter.Device;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sse.common.TimeRange;
public interface DomainObjectFactory {
Iterable<Resource> getResources(ClientSession clientSessionOrNull);
Iterable<DataAccessWindow> getDataAccessWindows(ClientSession clientSessionOrNull);
Iterable<Device> getDevices(ClientSession clientSessionOrNull);
@@ -4,14 +4,11 @@ import com.igtimi.IgtimiStream.Msg;
import com.mongodb.client.ClientSession;
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
import com.sap.sailing.domain.igtimiadapter.Device;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sse.common.TimePoint;
public interface MongoObjectFactory {
void storeDevice(Device device, ClientSession clientSessionOrNull);
void removeDevice(long deviceId, ClientSession clientSessionOrNull);
void storeResource(Resource resource, ClientSession clientSessionOrNull);
void removeResource(long resourceId, ClientSession clientSessionOrNull);
void storeDataAccessWindow(DataAccessWindow daw, ClientSession clientSessionOrNull);
void removeDataAccessWindow(long dawId, ClientSession clientSessionOrNull);
@@ -2,7 +2,6 @@ package com.sap.sailing.domain.igtimiadapter.persistence.impl;
public enum CollectionNames {
IGTIMI_DEVICES,
IGTIMI_RESOURCES,
IGTIMI_DATA_ACCESS_WINDOWS,
IGTIMI_MESSAGES;
}
@@ -25,7 +25,6 @@ import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Sorts;
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
import com.sap.sailing.domain.igtimiadapter.Device;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sailing.domain.igtimiadapter.persistence.DomainObjectFactory;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
@@ -42,27 +41,6 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
messagesCollection = MongoObjectFactoryImpl.getOrCreateMessagesCollection(db);
}
@Override
public Iterable<Resource> getResources(ClientSession clientSessionOrNull) {
final List<Resource> result = new ArrayList<>();
final MongoCollection<org.bson.Document> resourcesCollection = db.getCollection(CollectionNames.IGTIMI_RESOURCES.name());
final FindIterable<Document> resourceDocuments =
clientSessionOrNull == null ? resourcesCollection.find() : resourcesCollection.find(clientSessionOrNull);
for (Object o : resourceDocuments) {
final long id = ((Number) ((Document) o).get(FieldNames.IGTIMI_RESOURCES_ID.name())).longValue();
final TimePoint startTime = getTimePoint((Document) o, FieldNames.IGTIMI_RESOURCES_START_TIME_MILLIS);
final TimePoint endTime = getTimePoint((Document) o, FieldNames.IGTIMI_RESOURCES_END_TIME_MILLIS);
final String deviceSerialNumber = (String) ((Document) o).get(FieldNames.IGTIMI_RESOURCES_DEVICE_SERIAL_NUMBER.name());
final Integer[] dataTypesAsInteger = ((List<?>) ((Document) o).get(FieldNames.IGTIMI_RESOURCES_DATA_TYPES.name())).toArray(new Integer[0]);
final int[] dataTypes = new int[dataTypesAsInteger.length];
for (int i=0; i<dataTypesAsInteger.length; i++) {
dataTypes[i] = dataTypesAsInteger[i];
}
result.add(Resource.create(id, startTime, endTime, deviceSerialNumber, dataTypes));
}
return result;
}
private TimePoint getTimePoint(Document o, final FieldNames timePointFieldName) {
final Number startTimeMillisNumber = (Number) o.get(timePointFieldName.name());
final TimePoint startTime = startTimeMillisNumber == null ? null : TimePoint.of(startTimeMillisNumber.longValue());
@@ -2,7 +2,6 @@ package com.sap.sailing.domain.igtimiadapter.persistence.impl;
public enum FieldNames {
IGTIMI_DEVICES_ID, IGTIMI_DEVICES_SERIAL_NUMBER, IGTIMI_DEVICES_NAME,
IGTIMI_RESOURCES_ID, IGTIMI_RESOURCES_START_TIME_MILLIS, IGTIMI_RESOURCES_END_TIME_MILLIS, IGTIMI_RESOURCES_DEVICE_SERIAL_NUMBER, IGTIMI_RESOURCES_DATA_TYPES,
IGTIMI_DATA_ACCESS_WINDOWS_ID, IGTIMI_DATA_ACCESS_WINDOWS_START_TIME_MILLIS, IGTIMI_DATA_ACCESS_WINDOWS_END_TIME_MILLIS, IGTIMI_DATA_ACCESS_WINDOWS_DEVICE_SERIAL_NUMBER,
IGTIMI_MESSAGES_TIMESTAMP, IGTIMI_MESSAGES_DEVICE_SERIAL_NUMBER, IGTIMI_MESSAGES_PROTOBUF_MESSAGE;
}
@@ -18,7 +18,6 @@ import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
import com.sap.sailing.domain.igtimiadapter.DataPointTimePointExtractor;
import com.sap.sailing.domain.igtimiadapter.DataPointVisitor;
import com.sap.sailing.domain.igtimiadapter.Device;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sailing.domain.igtimiadapter.persistence.MongoObjectFactory;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -45,16 +44,13 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
@Override
public void clear(ClientSession clientSessionOrNull) {
final MongoCollection<Document> devicesCollection = db.getCollection(CollectionNames.IGTIMI_DEVICES.name()).withWriteConcern(WriteConcern.ACKNOWLEDGED);
final MongoCollection<Document> resourcesCollection = db.getCollection(CollectionNames.IGTIMI_RESOURCES.name()).withWriteConcern(WriteConcern.ACKNOWLEDGED);
final MongoCollection<Document> dataAccessWindowsCollections = db.getCollection(CollectionNames.IGTIMI_DATA_ACCESS_WINDOWS.name()).withWriteConcern(WriteConcern.ACKNOWLEDGED);
if (clientSessionOrNull == null) {
devicesCollection.drop();
resourcesCollection.drop();
dataAccessWindowsCollections.drop();
messagesCollection.withWriteConcern(WriteConcern.ACKNOWLEDGED).drop();
} else {
devicesCollection.drop(clientSessionOrNull);
resourcesCollection.drop(clientSessionOrNull);
dataAccessWindowsCollections.drop(clientSessionOrNull);
messagesCollection.withWriteConcern(WriteConcern.ACKNOWLEDGED).drop(clientSessionOrNull);
}
@@ -87,34 +83,6 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
}
}
@Override
public void storeResource(Resource resource, ClientSession clientSessionOrNull) {
final Document filter = new Document(FieldNames.IGTIMI_RESOURCES_ID.name(), resource.getId());
final Document update = new Document();
update.put(FieldNames.IGTIMI_RESOURCES_ID.name(), resource.getId());
update.put(FieldNames.IGTIMI_RESOURCES_DEVICE_SERIAL_NUMBER.name(), resource.getDeviceSerialNumber());
update.put(FieldNames.IGTIMI_RESOURCES_START_TIME_MILLIS.name(), resource.getStartTime() == null ? null : resource.getStartTime().asMillis());
update.put(FieldNames.IGTIMI_RESOURCES_END_TIME_MILLIS.name(), resource.getEndTime() == null ? null : resource.getEndTime().asMillis());
update.put(FieldNames.IGTIMI_RESOURCES_DATA_TYPES.name(), Util.asList(Util.map(resource.getDataTypes(), dataType->dataType.getCode())));
final MongoCollection<Document> resourcesCollection = db.getCollection(CollectionNames.IGTIMI_RESOURCES.name()).withWriteConcern(WriteConcern.ACKNOWLEDGED);
if (clientSessionOrNull == null) {
resourcesCollection.replaceOne(filter, update, new ReplaceOptions().upsert(true));
} else {
resourcesCollection.replaceOne(clientSessionOrNull, filter, update, new ReplaceOptions().upsert(true));
}
}
@Override
public void removeResource(long resourceId, ClientSession clientSessionOrNull) {
final Document filter = new Document(FieldNames.IGTIMI_DEVICES_ID.name(), resourceId);
final MongoCollection<Document> resourcesCollection = db.getCollection(CollectionNames.IGTIMI_RESOURCES.name()).withWriteConcern(WriteConcern.ACKNOWLEDGED);
if (clientSessionOrNull == null) {
resourcesCollection.deleteOne(filter);
} else {
resourcesCollection.deleteOne(clientSessionOrNull, filter);
}
}
@Override
public void storeDataAccessWindow(DataAccessWindow daw, ClientSession clientSessionOrNull) {
final Document filter = new Document(FieldNames.IGTIMI_DATA_ACCESS_WINDOWS_ID.name(), daw.getId());
@@ -11,7 +11,6 @@ import org.osgi.framework.BundleContext;
import com.sap.sailing.domain.common.security.SecuredDomainType;
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
import com.sap.sailing.domain.igtimiadapter.Device;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sailing.domain.igtimiadapter.persistence.DomainObjectFactory;
import com.sap.sailing.domain.igtimiadapter.persistence.MongoObjectFactory;
import com.sap.sailing.domain.igtimiadapter.persistence.PersistenceFactory;
@@ -72,9 +71,6 @@ public class Activator implements BundleActivator {
securityServiceServiceTracker = FullyInitializedReplicableTracker.createAndOpen(context, SecurityService.class);
try {
final SecurityService securityService = securityServiceServiceTracker.getInitializedService(0);
for (Resource resource : riotServer.getResources()) {
securityService.migrateOwnership(resource);
}
for (DataAccessWindow daw : riotServer.getDataAccessWindows()) {
securityService.migrateOwnership(daw);
}
@@ -86,7 +82,6 @@ public class Activator implements BundleActivator {
securityService.assumeOwnershipMigrated(igtimiAccountTypeName);
securityService.assumeOwnershipMigrated(SecuredDomainType.IGTIMI_DATA_ACCESS_WINDOW.getName());
securityService.assumeOwnershipMigrated(SecuredDomainType.IGTIMI_DEVICE.getName());
securityService.assumeOwnershipMigrated(SecuredDomainType.IGTIMI_RESOURCE.getName());
} catch (Exception e) {
logger.log(Level.SEVERE, "Error trying to create missing ownerships for Igtimi entities", e);
}
@@ -3,23 +3,16 @@ package com.sap.sailing.domain.igtimiadapter.server.replication;
import com.igtimi.IgtimiStream.Msg;
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
import com.sap.sailing.domain.igtimiadapter.Device;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sailing.domain.igtimiadapter.server.riot.RiotServer;
import com.sap.sse.common.TimePoint;
public interface ReplicableRiotServer extends RiotServer {
Void internalAddDevice(Device device);
Void internalRemoveDevice(long deviceId);
Void internalUpdateDeviceName(long deviceId, String name);
Void internalAddResource(Resource resource);
Void internalRemoveResource(long resourceId);
DataAccessWindow internalAddDataAccessWindow(String deviceSerialNumber, TimePoint startTime, TimePoint endTime);
DataAccessWindow internalCreateDataAccessWindow(String deviceSerialNumber, TimePoint startTime, TimePoint endTime);
Void internalRemoveDataAccessWindow(long dawId);
@@ -19,7 +19,6 @@ import com.sap.sailing.domain.igtimiadapter.Device;
import com.sap.sailing.domain.igtimiadapter.FixFactory;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
import com.sap.sailing.domain.igtimiadapter.IgtimiWindListener;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sailing.domain.igtimiadapter.persistence.DomainObjectFactory;
@@ -91,14 +90,6 @@ public interface RiotServer extends Replicable<ReplicableRiotServer, RiotReplica
*/
int getPort() throws IOException;
Iterable<Resource> getResources();
Resource getResourceById(long id);
void addResource(Resource resource);
void removeResource(long resourceId);
Iterable<Device> getDevices();
Device getDeviceById(long id);
@@ -110,8 +101,6 @@ public interface RiotServer extends Replicable<ReplicableRiotServer, RiotReplica
*/
Device createDevice(String deviceSerialNumber);
void addDevice(Device device);
void removeDevice(long deviceId);
void updateDeviceName(long deviceId, String name);
@@ -40,7 +40,6 @@ import com.sap.sailing.domain.igtimiadapter.DataPointVisitor;
import com.sap.sailing.domain.igtimiadapter.Device;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sailing.domain.igtimiadapter.persistence.DomainObjectFactory;
import com.sap.sailing.domain.igtimiadapter.persistence.MongoObjectFactory;
@@ -69,7 +68,6 @@ import com.sap.sse.util.ObjectInputStreamResolvingAgainstCache;
public class RiotServerImpl extends AbstractReplicableWithObjectInputStream<ReplicableRiotServer, RiotReplicationOperation<?>> implements RiotServer, ReplicableRiotServer, Runnable {
private static final Logger logger = Logger.getLogger(RiotServerImpl.class.getName());
private final ConcurrentMap<Long, Resource> resources;
private final ConcurrentMap<Long, DataAccessWindow> dataAccessWindows;
private final ConcurrentMap<Long, Device> devices;
private final ConcurrentMap<String, Device> devicesBySerialNumber;
@@ -109,7 +107,6 @@ public class RiotServerImpl extends AbstractReplicableWithObjectInputStream<Repl
public RiotServerImpl(SocketAddress localAddress, DomainObjectFactory domainObjectFactory,
MongoObjectFactory mongoObjectFactory) throws Exception {
this.listeners = ConcurrentHashMap.newKeySet();
this.resources = new ConcurrentHashMap<>();
this.dataAccessWindows = new ConcurrentHashMap<>();
this.devices = new ConcurrentHashMap<>();
this.devicesBySerialNumber = new ConcurrentHashMap<>();
@@ -121,9 +118,6 @@ public class RiotServerImpl extends AbstractReplicableWithObjectInputStream<Repl
devices.put(device.getId(), device);
devicesBySerialNumber.put(device.getSerialNumber(), device);
}
for (final Resource resource : domainObjectFactory.getResources(/* clientSessionOrNull */ null)) {
resources.put(resource.getId(), resource);
}
for (final DataAccessWindow daw : domainObjectFactory.getDataAccessWindows(/* clientSessionOrNull */ null)) {
dataAccessWindows.put(daw.getId(), daw);
}
@@ -368,21 +362,10 @@ public class RiotServerImpl extends AbstractReplicableWithObjectInputStream<Repl
public Device internalCreateDevice(String deviceSerialNumber) {
final long id = devices.isEmpty() ? 1 : Collections.max(devices.keySet()) + 1;
final Device device = Device.create(id, deviceSerialNumber);
internalAddDevice(device);
return device;
}
@Override
public void addDevice(Device device) {
apply(s->s.internalAddDevice(device));
}
@Override
public Void internalAddDevice(Device device) {
devices.put(device.getId(), device);
devicesBySerialNumber.put(device.getSerialNumber(), device);
mongoObjectFactory.storeDevice(device, /* clientSessionOrNull */ null);
return null;
return device;
}
@Override
@@ -413,40 +396,6 @@ public class RiotServerImpl extends AbstractReplicableWithObjectInputStream<Repl
return null;
}
@Override
public Iterable<Resource> getResources() {
return Collections.unmodifiableCollection(resources.values());
}
@Override
public Resource getResourceById(long id) {
return resources.get(id);
}
@Override
public void addResource(Resource resource) {
apply(s->s.internalAddResource(resource));
}
@Override
public Void internalAddResource(Resource resource) {
resources.put(resource.getId(), resource);
mongoObjectFactory.storeResource(resource, /* clientSessionOrNull */ null);
return null;
}
@Override
public void removeResource(long resourceId) {
apply(s->s.internalRemoveResource(resourceId));
}
@Override
public Void internalRemoveResource(long resourceId) {
resources.remove(resourceId);
mongoObjectFactory.removeResource(resourceId, /* clientSessionOrNull */ null);
return null;
}
@Override
public Iterable<DataAccessWindow> getDataAccessWindows() {
return Collections.unmodifiableCollection(dataAccessWindows.values());
@@ -460,7 +409,7 @@ public class RiotServerImpl extends AbstractReplicableWithObjectInputStream<Repl
@Override
public Iterable<DataAccessWindow> getDataAccessWindows(Iterable<String> deviceSerialNumbers, TimeRange timeRange) {
final Set<DataAccessWindow> result = new HashSet<>();
final Set<String> deviceSerialNumbersAsSet = new HashSet<>();
final Set<String> deviceSerialNumbersAsSet = Util.asSet(deviceSerialNumbers);
// TODO provide a more efficient implementation if this turns out to become a performance bottleneck, e.g., by keeping the DataAccessWindows in a time-sorted TreeSet
for (final DataAccessWindow daw : getDataAccessWindows()) {
if (deviceSerialNumbersAsSet.contains(daw.getDeviceSerialNumber()) && timeRange.intersects(daw.getTimeRange())) {
@@ -472,11 +421,11 @@ public class RiotServerImpl extends AbstractReplicableWithObjectInputStream<Repl
@Override
public DataAccessWindow createDataAccessWindow(String deviceSerialNumber, TimePoint from, TimePoint to) {
return apply(s->s.internalAddDataAccessWindow(deviceSerialNumber, from, to));
return apply(s->s.internalCreateDataAccessWindow(deviceSerialNumber, from, to));
}
@Override
public DataAccessWindow internalAddDataAccessWindow(String deviceSerialNumber, TimePoint from, TimePoint to) {
public DataAccessWindow internalCreateDataAccessWindow(String deviceSerialNumber, TimePoint from, TimePoint to) {
final long newId = dataAccessWindows.isEmpty() ? 1 : Collections.max(dataAccessWindows.keySet()) + 1;
final DataAccessWindow daw = DataAccessWindow.create(newId, from, to, deviceSerialNumber);
dataAccessWindows.put(daw.getId(), daw);
@@ -503,7 +452,6 @@ public class RiotServerImpl extends AbstractReplicableWithObjectInputStream<Repl
public void clear() {
devices.clear();
devicesBySerialNumber.clear();
resources.clear();
dataAccessWindows.clear();
connections.clear();
listeners.clear();
@@ -530,14 +478,12 @@ public class RiotServerImpl extends AbstractReplicableWithObjectInputStream<Repl
for (final Device device : devices.values()) {
devicesBySerialNumber.put(device.getSerialNumber(), device);
}
resources.putAll((Map<Long, Resource>) is.readObject());
dataAccessWindows.putAll((Map<Long, DataAccessWindow>) is.readObject());
}
@Override
public void serializeForInitialReplicationInternal(ObjectOutputStream objectOutputStream) throws IOException {
objectOutputStream.writeObject(new HashMap<>(devices));
objectOutputStream.writeObject(new HashMap<>(resources));
objectOutputStream.writeObject(new HashMap<>(dataAccessWindows));
}
@@ -23,7 +23,6 @@ import com.igtimi.IgtimiStream.Msg;
import com.mongodb.client.ClientSession;
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
import com.sap.sailing.domain.igtimiadapter.Device;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
@@ -67,21 +66,6 @@ public class IgtimiPersistenceTest {
assertEquals(device.getSerialNumber(), loadedDevice.getSerialNumber());
}
@Test
public void testStoringAndLoadingSimpleResource() {
final TimePoint from = TimePoint.now();
final TimePoint to = from.plus(Duration.ONE_MINUTE);
final Resource resource = Resource.create(234, from, to, "AA-DE-AACC", new int[] { 1, 4, 7 });
mongoObjectFactory.storeResource(resource, clientSession);
final Resource loadedResource = domainObjectFactory.getResources(clientSession).iterator().next();
assertEquals(resource.getId(), loadedResource.getId());
assertEquals(resource.getName(), loadedResource.getName()); // an inferred value; still useful to compare
assertEquals(resource.getStartTime(), loadedResource.getStartTime());
assertEquals(resource.getEndTime(), loadedResource.getEndTime());
assertEquals(resource.getDeviceSerialNumber(), loadedResource.getDeviceSerialNumber());
assertEquals(Util.asSet(resource.getDataTypes()), Util.asSet(loadedResource.getDataTypes()));
}
@Test
public void testStoringAndLoadingSimpleDataAccessWindow() {
final TimePoint from = TimePoint.now();
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.igtimiadapter.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
@@ -24,12 +25,14 @@ public class TestSimpleProtobufReading {
final InputStream is = getClass().getResourceAsStream("/windbot_session_20250107.base64");
final BufferedReader br = new BufferedReader(new InputStreamReader(is));
String base64;
int messageCount = 0;
while ((base64=br.readLine()) != null) {
final Msg msg = Msg.parseFrom(Base64.decode(base64));
assertNotNull(msg);
logger.info(String.format("Parsed the following message:\n %s", msg.toString()));
messageCount++;
}
is.close();
assertEquals(17920, messageCount);
}
@Test
@@ -65,21 +68,4 @@ public class TestSimpleProtobufReading {
}
is.close();
}
@Test
public void testReadSAPWindbotStartup() throws IOException {
final InputStream is = getClass().getResourceAsStream("/windbot_wdf53_20250107_2.protobuf");
final ExtensionRegistry er = ExtensionRegistry.newInstance();
final CodedInputStream cis = CodedInputStream.newInstance(is);
for (int i=0; i<20; i++) { // we know there are four full messages in the stream:
final int lengthOfMessage = cis.readRawVarint32();
assertNotEquals(0, lengthOfMessage);
final int oldLimit = cis.pushLimit(lengthOfMessage);
final Msg msg = Msg.parseFrom(cis, er);
assertNotNull(msg);
logger.info(String.format("Parsed the following message #%d:\n %s", i, msg.toString()));
cis.popLimit(oldLimit);
}
is.close();
}
}
@@ -3,5 +3,8 @@ package com.sap.sailing.domain.igtimiadapter;
import java.io.Serializable;
public interface HasId extends Serializable {
/**
* 0 is not a valid ID but is used as a marker to ask for an auto-generated ID
*/
long getId();
}
@@ -27,21 +27,6 @@ import com.sap.sse.security.shared.HasPermissions.DefaultActions;
* @author Axel Uhl (d043530)
*/
public interface IgtimiConnection {
/**
* @param startTime
* optional; may be <code>null</code>
* @param endTime
* optional; may be <code>null</code>
* @param deviceIds
* optional (may be <code>null</code>) if <code>permission</code> is {@link Permission#modify}; lists the
* devices for which to look for resources
* @param streamIds
* optional; may be <code>null</code>
*/
Iterable<Resource> getResources(Permission permission, TimePoint startTime, TimePoint endTime,
Iterable<String> deviceIds, Iterable<String> streamIds) throws IllegalStateException,
ClientProtocolException, IOException, ParseException;
/**
* All arguments are mandatory.
*
@@ -72,15 +57,6 @@ public interface IgtimiConnection {
*/
Iterable<Msg> getMessages(TimePoint startTime, TimePoint endTime, Iterable<String> deviceSerialNumbers, Type[] types) throws IllegalStateException, ClientProtocolException, IOException, ParseException;
/**
* Shorthand for {@link #getResourceData(TimePoint, TimePoint, Iterable, Map)} where no compression is requested for
* any type. The fixes received are forwarded to the {@link BulkFixReceiver} <code>bulkFixReceiver</code> in one call.
*/
Iterable<Fix> getAndNotifyResourceData(TimePoint startTime, TimePoint endTime,
Iterable<String> deviceSerialNumbers, BulkFixReceiver bulkFixReceiver, Type... types)
throws IllegalStateException, ClientProtocolException, IOException, ParseException;
/**
* Same as {@link #getResourceData(TimePoint, TimePoint, Iterable, Type...)}, but the resulting {@link Fix}es are
* grouped into {@link Track}s per fix type and per device.
@@ -1,26 +0,0 @@
package com.sap.sailing.domain.igtimiadapter;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sailing.domain.igtimiadapter.impl.ResourceImpl;
import com.sap.sse.common.TimePoint;
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
/**
* You can think of resources really as meta-info - identifying time blocks of data that have some meaning. For an
* Igtimi device it's a power cycle. However data access is not forced to be "through" a resource. If you only wanted to
* get data from a specific resource then of course you would just use the start and end time stamps in the request.
* Resources are meant to be non-overlapping, although this is not strictly enforced.
*
* @author Axel Uhl (d043530)
*
*/
public interface Resource extends HasId, HasStartAndEndTime, WithQualifiedObjectIdentifier {
Iterable<Type> getDataTypes();
String getDeviceSerialNumber();
static Resource create(long id, TimePoint startTime, TimePoint endTime, String deviceSerialNumber, int[] dataTypes) {
return new ResourceImpl(id, startTime, endTime, deviceSerialNumber, dataTypes);
}
}
@@ -12,7 +12,7 @@ public class DataAccessWindowDeserializer {
static final String DEVICE_SERIAL_NUMBER = "device_serial_number";
public DataAccessWindow createDataAccessWindowFromJson(JSONObject resourceJson) {
return new DataAccessWindowImpl((Long) resourceJson.get(ID),
return new DataAccessWindowImpl((Long) resourceJson.getOrDefault(ID, 0),
new MillisecondsTimePoint(((Number) resourceJson.get(START_TIME)).longValue()),
new MillisecondsTimePoint(((Number) resourceJson.get(END_TIME)).longValue()),
(String) resourceJson.get(DEVICE_SERIAL_NUMBER));
@@ -10,7 +10,7 @@ public class DeviceDeserializer {
static final String NAME = "name";
public Device createDeviceFromJson(JSONObject deviceJson) {
return new DeviceImpl((Long) deviceJson.get(ID), (String) deviceJson.get(SERIAL_NUMBER),
return new DeviceImpl((Long) deviceJson.getOrDefault(ID, 0), (String) deviceJson.get(SERIAL_NUMBER),
(String) deviceJson.get(NAME));
}
}
@@ -15,7 +15,7 @@ public class DeviceImpl extends HasIdImpl implements Device {
this(id, serialNumber, /* name */ null);
}
public DeviceImpl(Long id, String serialNumber, String name) {
public DeviceImpl(long id, String serialNumber, String name) {
super(id);
this.serialNumber = serialNumber;
this.name = name;
@@ -37,7 +37,6 @@ import com.sap.sailing.domain.igtimiadapter.FixFactory;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
import com.sap.sailing.domain.igtimiadapter.LiveDataConnection;
import com.sap.sailing.domain.igtimiadapter.Permission;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sailing.domain.igtimiadapter.shared.IgtimiWindReceiver;
@@ -75,19 +74,6 @@ public class IgtimiConnectionImpl extends SecuredServerImpl implements IgtimiCon
liveDataConnectionFactory = new LiveDataConnectionFactoryImpl(this);
}
@Override
public Iterable<Resource> getResources(Permission permission, TimePoint startTime, TimePoint endTime,
Iterable<String> deviceIds, Iterable<String> streamIds) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
final HttpGet getResources = new HttpGet(getResourcesUrl(permission, startTime, endTime, deviceIds, streamIds));
final JSONObject resourcesJson = (JSONObject) getJsonParsedResponse(getResources).getA();
final List<Resource> result = new ArrayList<>();
for (Object resourceJson : (JSONArray) resourcesJson.get("resources")) {
Resource resource = new ResourceDeserializer().createResourceFromJson((JSONObject) ((JSONObject) resourceJson).get("resource"));
result.add(resource);
}
return result;
}
@Override
public Iterable<Fix> getLatestFixes(Iterable<String> deviceSerialNumbers, Type type) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
final HttpGet getLatestData = new HttpGet(getLatestDatumUrl(deviceSerialNumbers, type));
@@ -191,8 +177,11 @@ public class IgtimiConnectionImpl extends SecuredServerImpl implements IgtimiCon
});
}
@Override
public Iterable<Fix> getAndNotifyResourceData(TimePoint startTime, TimePoint endTime,
/**
* Shorthand for {@link #getResourceData(TimePoint, TimePoint, Iterable, Map)} where no compression is requested for
* any type. The fixes received are forwarded to the {@link BulkFixReceiver} <code>bulkFixReceiver</code> in one call.
*/
private Iterable<Fix> getAndNotifyResourceData(TimePoint startTime, TimePoint endTime,
Iterable<String> deviceSerialNumbers, BulkFixReceiver bulkFixReceiver, Type... types)
throws IllegalStateException, ClientProtocolException, IOException, ParseException {
Iterable<Fix> result = getResourceData(startTime, endTime, deviceSerialNumbers, types);
@@ -307,29 +296,9 @@ public class IgtimiConnectionImpl extends SecuredServerImpl implements IgtimiCon
final TimePoint receivingEndTime = IgtimiWindTracker.getReceivingEndTime(trackedRace);
endOfWindow = Collections.max(Arrays.asList(endOfWindow, receivingEndTime==null?MillisecondsTimePoint.now():receivingEndTime));
}
Iterable<DataAccessWindow> daws = getDataAccessWindows(Permission.read, startOfWindow, endOfWindow, /* find all deviceSerialNumbers for window */ null);
logger.info("Found "+Util.size(daws)+" data access windows. Analyzing which ones contain wind data...");
Set<String> deviceSerialNumbers = new HashSet<>();
for (DataAccessWindow daw : daws) {
String deviceSerialNumber = daw.getDeviceSerialNumber();
// now filter for the wind-providing devices
Iterable<Resource> resources = getResources(Permission.read, startOfWindow, endOfWindow,
Collections.singleton(deviceSerialNumber), /* streamIds */ null);
if (hasWind(resources)) {
StringBuilder resourceIDs = new StringBuilder();
boolean first = true;
for (Resource resource : resources) {
if (!first) {
resourceIDs.append(", ");
} else {
first = false;
}
resourceIDs.append(resource.getId());
}
logger.info(" Resources ["+resourceIDs+"] for device "+deviceSerialNumber+" contain wind data");
deviceSerialNumbers.add(deviceSerialNumber);
}
}
final Iterable<DataAccessWindow> daws = getDataAccessWindows(Permission.read, startOfWindow, endOfWindow, /* find all deviceSerialNumbers for window */ null);
logger.info("Found "+Util.size(daws)+" data access windows.");
final Set<String> deviceSerialNumbers = Util.asSet(Util.map(daws, DataAccessWindow::getDeviceSerialNumber));
final Map<TrackedRace, Integer> result;
if (!deviceSerialNumbers.isEmpty()) {
IgtimiWindReceiver windReceiver = new IgtimiWindReceiver(correctByDeclination ? DeclinationService.INSTANCE : null);
@@ -347,15 +316,6 @@ public class IgtimiConnectionImpl extends SecuredServerImpl implements IgtimiCon
return result;
}
private boolean hasWind(Iterable<Resource> resources) {
for (Resource resource : resources) {
if (Util.contains(resource.getDataTypes(), Type.AWS)) {
return true;
}
}
return false;
}
@Override
public Iterable<String> getWindDevices() throws IllegalStateException, ClientProtocolException, IOException, ParseException {
// find all the devices from which we may read
@@ -475,36 +435,6 @@ public class IgtimiConnectionImpl extends SecuredServerImpl implements IgtimiCon
return url.toString();
}
private String getResourcesUrl(Permission permission, TimePoint startTime, TimePoint endTime,
Iterable<String> serialNumbers, Iterable<String> streamIds) {
final StringBuilder url = new StringBuilder(getApiV1BaseUrl());
url.append("resources?");
url.append("permission=");
url.append(permission.name());
if (startTime != null) {
url.append("&start_time=");
url.append(startTime.asMillis());
}
if (endTime != null) {
url.append("&end_time=");
url.append(endTime.asMillis());
}
if (serialNumbers != null) {
for (String serialNumber : serialNumbers) {
url.append("&serial_numbers[]=");
url.append(serialNumber);
}
}
if (streamIds != null) {
for (String streamId : streamIds) {
url.append("&stream_ids[]=");
url.append(streamId);
}
}
return url.toString();
}
@Override
public Iterable<URI> getWebsocketServers() throws IllegalStateException, ClientProtocolException, IOException, ParseException, URISyntaxException {
final HttpGet getWebsocketServers = new HttpGet(getApiV1BaseUrl()+"server_listers/web_sockets");
@@ -1,32 +0,0 @@
package com.sap.sailing.domain.igtimiadapter.impl;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class ResourceDeserializer {
static final String ID = "id";
static final String START_TIME = "start_time";
static final String END_TIME = "end_time";
static final String DEVICE_SERIAL_NUMBER = "device_serial_number";
static final String DATA_TYPES = "data_types";
public Resource createResourceFromJson(JSONObject resourceJson) {
return new ResourceImpl((Long) resourceJson.get(ID),
resourceJson.get(START_TIME)==null?null:new MillisecondsTimePoint(((Number) resourceJson.get(START_TIME)).longValue()),
resourceJson.get(END_TIME)==null?null:new MillisecondsTimePoint(((Number) resourceJson.get(END_TIME)).longValue()),
(String) resourceJson.get(DEVICE_SERIAL_NUMBER),
getDataTypes((JSONArray) resourceJson.get(DATA_TYPES)));
}
private int[] getDataTypes(JSONArray jsonArray) {
final int[] result = new int[jsonArray.size()];
int i=0;
for (Object o : jsonArray) {
result[i++] = ((Long) o).intValue();
}
return result;
}
}
@@ -1,74 +0,0 @@
package com.sap.sailing.domain.igtimiadapter.impl;
import java.util.ArrayList;
import java.util.List;
import com.sap.sailing.domain.common.security.SecuredDomainType;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sse.common.TimePoint;
import com.sap.sse.security.shared.HasPermissions;
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
public class ResourceImpl implements Resource {
private static final long serialVersionUID = -8944469021862963744L;
private final long id;
private final TimePoint startTime;
private final TimePoint endTime;
private final String deviceSerialNumber;
private final int[] dataTypes;
public ResourceImpl(long id, TimePoint startTime, TimePoint endTime, String deviceSerialNumber, int[] dataTypes) {
super();
this.id = id;
this.startTime = startTime;
this.endTime = endTime;
this.deviceSerialNumber = deviceSerialNumber;
this.dataTypes = dataTypes;
}
@Override
public long getId() {
return id;
}
@Override
public TimePoint getStartTime() {
return startTime;
}
@Override
public TimePoint getEndTime() {
return endTime;
}
@Override
public String getDeviceSerialNumber() {
return deviceSerialNumber;
}
@Override
public Iterable<Type> getDataTypes() {
List<Type> result = new ArrayList<Type>();
for (int dataType : dataTypes) {
result.add(Type.valueOf(dataType));
}
return result;
}
@Override
public QualifiedObjectIdentifier getIdentifier() {
return getPermissionType().getQualifiedObjectIdentifier(new TypeRelativeObjectIdentifier(""+getId()));
}
@Override
public HasPermissions getPermissionType() {
return SecuredDomainType.IGTIMI_RESOURCE;
}
@Override
public String getName() {
return "Igtimi Resource "+getId();
}
}
@@ -1,23 +0,0 @@
package com.sap.sailing.domain.igtimiadapter.impl;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
public class ResourceSerializer {
public JSONObject createJsonFromResource(Resource resource) {
final JSONObject resourceJson = new JSONObject();
resourceJson.put(ResourceDeserializer.ID, resource.getId());
resourceJson.put(ResourceDeserializer.START_TIME, resource.getStartTime()==null?null:resource.getStartTime().asMillis());
resourceJson.put(ResourceDeserializer.END_TIME, resource.getEndTime()==null?null:resource.getEndTime().asMillis());
resourceJson.put(ResourceDeserializer.DEVICE_SERIAL_NUMBER, resource.getDeviceSerialNumber());
final JSONArray dataTypesJson = new JSONArray();
for (final Type dataType : resource.getDataTypes()) {
dataTypesJson.add(dataType.getCode());
}
resourceJson.put(ResourceDeserializer.DATA_TYPES, dataTypesJson);
return resourceJson;
}
}
@@ -3,23 +3,25 @@ package com.sap.sailing.selenium.api.test;
import static com.sap.sailing.selenium.api.core.ApiContext.SHARED_SERVER_CONTEXT;
import static com.sap.sailing.selenium.api.core.ApiContext.createAdminApiContext;
import com.google.protobuf.CodedOutputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.URL;
import java.text.SimpleDateFormat;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.Timeout;
import com.google.protobuf.CodedOutputStream;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
import com.sap.sailing.selenium.api.core.ApiContext;
import com.sap.sailing.selenium.api.core.Authenticator;
import com.sap.sailing.selenium.test.AbstractSeleniumTest;
import com.sap.sse.common.TimePoint;
import com.sun.jersey.core.util.Base64;
/**
@@ -68,7 +70,10 @@ public class AbstractTestWithIgtimiConnection extends AbstractSeleniumTest {
cos.flush();
sos.close();
socket.close();
// TODO do we need to wait until the data has been received and processed?
final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
connection.createDataAccessWindow("DC-GD-AAED",
TimePoint.of(df.parse("2025-01-07T08:00:00Z")),
TimePoint.of(df.parse("2025-01-07T22:00:00Z")));
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -21,7 +21,6 @@ import org.junit.Test;
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
import com.sap.sailing.domain.igtimiadapter.Permission;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sailing.domain.igtimiadapter.datatypes.AWA;
import com.sap.sailing.domain.igtimiadapter.datatypes.AWS;
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
@@ -30,18 +29,15 @@ import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
// TODO This needs to become a Selenium test with the OSGi product and the igtimiadapter.gateway bundle running.
// TODO Furthermore, the test assumes specific data to be present in some Riot server to test against; is this a good idea?
// TODO If so, we would need to ensure that the data against which to test is actually present in the Riot test server...
public class BasicIgtimiAdapterTest extends AbstractTestWithIgtimiConnection {
@Test
public void testGetDataAccessWindows() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
Iterable<DataAccessWindow> daws = connection.getDataAccessWindows(Permission.read, /* startTime */ null,
/* endTime */ null, /* deviceSerialNumbers */ Collections.singleton("DD-EE-AAGA"));
/* endTime */ null, /* deviceSerialNumbers */ Collections.singleton("DC-GD-AAED"));
assertFalse(Util.isEmpty(daws));
for (DataAccessWindow daw : daws) {
assertEquals("DD-EE-AAGA", daw.getDeviceSerialNumber());
assertTrue(daw.getId() == 17012);
assertEquals("DC-GD-AAED", daw.getDeviceSerialNumber());
assertTrue(daw.getId() == 1);
}
}
@@ -53,27 +49,22 @@ public class BasicIgtimiAdapterTest extends AbstractTestWithIgtimiConnection {
}
@Test
public void testGetResources() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
Iterable<Resource> resources = connection.getResources(Permission.read, /* start time */ null, /* end time */ null,
/* serial numbers */ Collections.singleton("GA-EN-AAEJ"), /* stream IDs */ null);
assertTrue(resources.iterator().hasNext());
}
@Test
public void testGetResourceData() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
Map<Type, Double> typesAndCompression = new HashMap<>();
public void testGetResourceData() throws ClientProtocolException, IllegalStateException, IOException, ParseException, java.text.ParseException {
final Map<Type, Double> typesAndCompression = new HashMap<>();
typesAndCompression.put(Type.gps_latlong, 0.0);
Iterable<Fix> data = connection.getResourceData(new MillisecondsTimePoint(1384068419000l),
new MillisecondsTimePoint(1384091860000l), Collections.singleton("DD-EE-AAGA"), typesAndCompression);
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.GERMAN);
final TimePoint start = new MillisecondsTimePoint(dateFormat.parse("2025-01-07T07:00:00Z"));
final TimePoint end = new MillisecondsTimePoint(dateFormat.parse("2025-01-08T18:00:00Z"));
final Iterable<Fix> data = connection.getResourceData(start, end,
Collections.singleton("DC-GD-AAED"), typesAndCompression);
assertTrue(data.iterator().hasNext());
}
@Test
public void testDataAccessWindowForGivenTimeFrame() throws java.text.ParseException, IllegalStateException, ClientProtocolException, IOException, ParseException {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.GERMAN);
TimePoint start = new MillisecondsTimePoint(dateFormat.parse("2013-11-07T08:00:00Z"));
TimePoint end = new MillisecondsTimePoint(dateFormat.parse("2013-11-09T18:00:00Z"));
// URL is https://www.igtimi.com/api/v1/devices/data_access_windows?type=read&start_time=1383811200000&end_time=1383933600000&access_token=3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9
TimePoint start = new MillisecondsTimePoint(dateFormat.parse("2025-01-07T07:00:00Z"));
TimePoint end = new MillisecondsTimePoint(dateFormat.parse("2025-01-08T18:00:00Z"));
Iterable<DataAccessWindow> daws = connection.getDataAccessWindows(Permission.read, start, end, /* deviceSerialNumbers; get all devices available for that time */ null);
assertFalse(Util.isEmpty(daws));
for (DataAccessWindow daw : daws) {
@@ -86,15 +77,14 @@ public class BasicIgtimiAdapterTest extends AbstractTestWithIgtimiConnection {
@Test
public void testResourceDataForGivenTimeFrame() throws java.text.ParseException, IllegalStateException, ClientProtocolException, IOException, ParseException {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.GERMAN);
TimePoint start = new MillisecondsTimePoint(dateFormat.parse("2013-11-09T07:00:00Z"));
TimePoint end = new MillisecondsTimePoint(dateFormat.parse("2013-11-09T07:10:00Z"));
// URL is https://www.igtimi.com/api/v1/devices/data_access_windows?type=read&start_time=1383811200000&end_time=1383933600000&access_token=3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9
Iterable<DataAccessWindow> daws = connection.getDataAccessWindows(Permission.read, start, end, /* deviceSerialNumbers; get all devices available for that time */ null);
Set<String> deviceSerialNumbers = new HashSet<>();
for (DataAccessWindow daw : daws) {
final TimePoint start = new MillisecondsTimePoint(dateFormat.parse("2025-01-07T07:00:00Z"));
final TimePoint end = new MillisecondsTimePoint(dateFormat.parse("2025-01-08T18:00:00Z"));
final Iterable<DataAccessWindow> daws = connection.getDataAccessWindows(Permission.read, start, end, /* deviceSerialNumbers; get all devices available for that time */ null);
final Set<String> deviceSerialNumbers = new HashSet<>();
for (final DataAccessWindow daw : daws) {
deviceSerialNumbers.add(daw.getDeviceSerialNumber());
}
Iterable<Fix> windData = connection.getResourceData(start, end, deviceSerialNumbers, Type.gps_latlong, Type.AWA, Type.AWS, Type.HDG);
final Iterable<Fix> windData = connection.getResourceData(start, end, deviceSerialNumbers, Type.gps_latlong, Type.AWA, Type.AWS, Type.HDG);
assertFalse(Util.isEmpty(windData));
boolean foundWind = false;
for (Fix fix : windData) {
@@ -106,15 +96,12 @@ public class BasicIgtimiAdapterTest extends AbstractTestWithIgtimiConnection {
@Test
public void testReadLatestData() throws IllegalStateException, ClientProtocolException, IOException, ParseException {
Iterable<Fix> fixes = connection.getLatestFixes(Arrays.asList(new String[] { "DD-EE-AAHG", "GA-EN-AAEA", "DD-EE-AAGA" }), Type.SOG );
assertEquals(2, Util.size(fixes));
Iterable<Fix> fixes = connection.getLatestFixes(Arrays.asList(new String[] { "DC-GD-AAED", "GA-EN-AAEA", "DD-EE-AAGA" }), Type.SOG );
assertEquals(1, Util.size(fixes));
Iterator<Fix> i = fixes.iterator();
Fix fix1 = i.next();
assertEquals("DD-EE-AAGA", fix1.getSensor().getDeviceSerialNumber());
assertEquals("DC-GD-AAED", fix1.getSensor().getDeviceSerialNumber());
assertEquals(Type.SOG, fix1.getType());
Fix fix2 = i.next();
assertEquals("DD-EE-AAHG", fix2.getSensor().getDeviceSerialNumber());
assertEquals(Type.SOG, fix2.getType());
}
}