[{
"SpaceName": "Storage",
"Geometry": {
"Type": "Polygon"
},
"Descriptions": [{
"Locale": "en",
"Label": "Storage Area"
}, {
"Locale": "it",
"Label": "Area Magazzino"
}],
"ParentSpaceId": null
}]
{
"Name": "iot.iotpmdemosenv.truck:geofenceViolationSet",
"PackageName": "iot.iotpmdemosenv.truck",
"Descriptions": [{
"LanguageCode": "en",
"Description": "geofenceViolationSet"
}, {
"LanguageCode": "it",
"Description": "geofenceViolationSet"
}],
"DataCategory": "EventData",
"Properties": [{
"Name": "geofenceViolation",
"Type": "String",
"PropertyLength": "127",
"Descriptions": [{
"LanguageCode": "en",
"Description": "geofence violation"
}, {
"LanguageCode": "it",
"Description": "Violazione di geofence"
}]
}]
}
{
"Name": "iot.iotpmdemosenv.truck:geofenceViolation",
"EventTypeState": "Mutable",
"Descriptions": [{
"LanguageCode": "en",
"Description": "Event type for geofence violation"
}, {
"LanguageCode": "it",
"Description": "Tipo evento per violazione di geofence"
}],
"PackageName": "iot.iotpmdemosenv.truck",
"PropertySetId": "geofenceViolationSet",
"PropertySetType": "iot.iotpmdemosenv.truck:geofenceViolationSet",
"PropertySetDescriptions": [{
"LanguageCode": "en",
"Description": "geofence violation property set"
}],
"Statuses": [{
"EventStatus": "Ingress",
"Descriptions": [{
"LanguageCode": "en",
"Description": "Entering the geofence"
}]
}, {
"EventStatus": "Outgress",
"Descriptions": [{
"LanguageCode": "en",
"Description": "Exiting the geofence"
}]
}, {
"EventStatus": "In",
"Descriptions": [{
"LanguageCode": "en",
"Description": "Inside the geofence"
}]
}, {
"EventStatus": "Out",
"Descriptions": [{
"LanguageCode": "en",
"Description": "Outside the geofence"
}]
}, {
"EventStatus": "Undefined",
"Descriptions": [{
"LanguageCode": "en",
"Description": "Illegal State"
}]
}],
"Severities": [{
"EventSeverity": 1,
"Descriptions": [{
"LanguageCode": "en",
"Description": "High"
}]
}, {
"EventSeverity": 2,
"Descriptions": [{
"LanguageCode": "en",
"Description": "Normal"
}]
}, {
"EventSeverity": 3,
"Descriptions": [{
"LanguageCode": "en",
"Description": "Low"
}]
}],
"Codes": [{
"EventCode": "EQ12",
"Descriptions": [{
"LanguageCode": "en",
"Description": "Event Code 12"
}]
}]
}
package com.sap.iot;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.scribejava.core.model.OAuth2AccessToken;
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.model.Response;
import com.github.scribejava.core.model.Verb;
import com.github.scribejava.core.oauth.OAuth20Service;
import com.github.underscore.lodash.U;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ExecutionException;
public class GeofenceEvaluation implements Runnable {
private ObjectMapper mapper = new ObjectMapper();
private Oauth2 oauth2;
private OAuth20Service serviceToken;
private Map<String, String> lastStatus_thingid_fenceid = new HashMap<>();
private String space;
private String clientid;
private String clientsecret;
private String eventsEndPoint;
private String thingsEndPoint;
private String thingtype;
private String packagename;
private String eventname;
private String geolocationEndPoint;
private boolean firstInit = false;
GeofenceEvaluation(Map<String, String> configurationSet) {
space = configurationSet.get("spacename");
clientid = configurationSet.get("clientid");
clientsecret = configurationSet.get("clientsecret");
eventsEndPoint = configurationSet.get("events-sap");
geolocationEndPoint = configurationSet.get("geolocation");
thingsEndPoint = configurationSet.get("details-thing-sap");
thingtype = configurationSet.get("thingtype");
packagename = configurationSet.get("packagename");
eventname = configurationSet.get("eventname");
if (StringUtils.isEmpty(clientid)){
throw new IllegalStateException("clientid parameter not defined");
}
if (StringUtils.isEmpty(clientsecret)){
throw new IllegalStateException("clientsecret parameter not defined");
}
if (StringUtils.isEmpty(eventsEndPoint)){
throw new IllegalStateException("events-sap parameter not defined");
}
if (StringUtils.isEmpty(geolocationEndPoint)){
throw new IllegalStateException("geolocation parameter not defined");
}
if (StringUtils.isEmpty(thingtype)){
throw new IllegalStateException("thingtype parameter not defined");
}
}
private Map<String, ThingDetails> getThingsDetails(List<String> ids){
Map<String, ThingDetails> map = new HashMap<>();
try {
final OAuth2AccessToken accessToken = serviceToken.getAccessTokenClientCredentialsGrant();
for (String id : ids) {
String query = thingsEndPoint + "/CompositeThings/ThingType/v1/" + packagename + ":" + thingtype
+ "/Things('" + id + "')?$expand=DYN_ENT_" + packagename.replaceAll("[.]","_") + "__Tracking";
OAuthRequest request = new OAuthRequest(Verb.GET, query);
request.addHeader("Accept", "*");
serviceToken.signRequest(accessToken, request);
final Response response = serviceToken.execute(request);
//The response body is xml. Use your preferred method to navigate into it.
//Navigate the xml response content-->m:properties-->d:ThingName-->link-->m:inline-->entry-->content-->m:properties-->d:Tracking.Longitude
//Navigate the xml response content-->m:properties-->d:ThingName-->link-->m:inline-->entry-->content-->m:properties-->d:Tracking.Latitude
Map<String, Object> tmpmap = (Map<String, Object>) U.fromXml(response.getBody());
Object l1Map = tmpmap.get("entry");
if (l1Map != null){
Map<String, Object> things = (Map<String, Object>)l1Map;
String thingname = (String)((Map<String, Object>)((Map<String, Object>)things.get("content")).get("m:properties")).get("d:ThingName");
List<Object> links = (List<Object>)((Map<String, Object>)l1Map).get("link");
ThingDetails pos = new ThingDetails();
for (Object link : links) {
Object inline = ((Map<String, Object>)link).get("m:inline");
if (inline != null){
Map<String, Object> content = (Map<String, Object>)((Map<String, Object>)((Map<String, Object>)inline).get("entry")).get("content");
try {
String lng = (String) ((Map<String, Object>) content.get("m:properties")).get("d:Tracking.Longitude");
String lat = (String) ((Map<String, Object>) content.get("m:properties")).get("d:Tracking.Latitude");
pos.setLat(lat);
pos.setLng(lng);
pos.setThingName(thingname);
map.put(id, pos);
}
catch(Exception e){
//no valid measurements
}
break;
}
}
}
}
} catch (IOException | InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return map;
}
private Map<String,String> getGeofences(){
Map<String,String> map = new HashMap<>();
try {
final OAuth2AccessToken accessToken = serviceToken.getAccessTokenClientCredentialsGrant();
String query = geolocationEndPoint + "/geolocation/v1/GeoLocations";
if (!StringUtils.isEmpty(space)){
query += "?SpaceName=" + space.replaceAll(" ", "%20");
}
OAuthRequest request = new OAuthRequest(Verb.GET, query);
serviceToken.signRequest(accessToken, request);
final Response response = serviceToken.execute(request);
JsonNode tmpmap = mapper.readValue(response.getBody(), JsonNode.class);
//get from the response value-->Properties-->GeoLocationId and value-->Properties-->GeoLocationName
JsonNode node = tmpmap.get("value");
for (int i = 0 ; i < node.size(); i++) {
String id = tmpmap.get("value").get(0).get("Properties").get("GeoLocationId").asText();
String name = tmpmap.get("value").get(0).get("Properties").get("GeoLocationName").asText();
map.put(id, name);
}
} catch (IOException | InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return map;
}
private boolean geotest(String id, ThingDetails details){
JsonNode map = null;
try {
final OAuth2AccessToken accessToken = serviceToken.getAccessTokenClientCredentialsGrant();
String query = geolocationEndPoint + "/geolocation/v1/GeoTest/Check/Within/GeoPosition?Latitude=" +
details.getLat() + "&Longitude=" + details.getLng() + "&GeoLocationId=" + id;
OAuthRequest request = new OAuthRequest(Verb.GET, query);
serviceToken.signRequest(accessToken, request);
final Response response = serviceToken.execute(request);
map = mapper.readValue(response.getBody(), JsonNode.class);
//has a property named Within
return map.get("Within").asBoolean();
} catch (IOException | ExecutionException | InterruptedException e) {
e.printStackTrace();
}
return false;
}
private void postGeofenceViolation(String id, String geofenceName, String thing, String thingName, boolean within){
try {
final OAuth2AccessToken accessToken = serviceToken.getAccessTokenClientCredentialsGrant();
String query = eventsEndPoint + "/ES/EventType/" + packagename + ":" + eventname + "/v1/Events";
OAuthRequest request = new OAuthRequest(Verb.POST, query);
SimpleDateFormat fmtTimestamp = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
Date d = new Date();
//the payload accordingly with the APIs of Leonardo IoT
String payload ="{\"BusinessTimestamp\": \"" + fmtTimestamp.format(d) + "\",\"Type\": \"Alert\",\"EventInfo\": \"Alert on geofence " +
geofenceName + "\",\"EventStatus\": \"" + (within ? "In" : "Out") + "\",\"EventSeverity\": 1,\"EventCode\": null,\"EventSource\": null,\"ThingId\": \"" +
thing + "\",\"ThingProperty\": \"Geofence Violation for Thing: " + thingName + "\",\"ExternalId\": \"" + id + "\"}";
request.setPayload(payload);
request.addHeader("Content-Type", "application/json");
request.addHeader("Accept", "*");
serviceToken.signRequest(accessToken, request);
final Response response = serviceToken.execute(request);
} catch (IOException | InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
public void run() {
if(oauth2 == null){
oauth2 = new Oauth2(clientid, clientsecret);
}
if(serviceToken == null){
serviceToken = oauth2.getNewService();
}
List<String> things = DiscoveryThings.getThingIds();
List<String> ids;
synchronized (things) {
ids = new ArrayList<>(things);
}
//init status map
initFenceStatus(ids);
Map<String, ThingDetails> thingsDetails = getThingsDetails(ids);
//getGeofences
Map<String,String> fenceIds = getGeofences();
for (String id : fenceIds.keySet()) {
for (String thing : thingsDetails.keySet()){
boolean within = geotest(id, thingsDetails.get(thing));
boolean differentStatus = checkLastStatus(thing, id, within);
if (differentStatus){
//create Event
postGeofenceViolation(id, fenceIds.get(id), thing, thingsDetails.get(thing).getThingName(), within);
}
}
}
}
private void initFenceStatus(List<String> ids) {
if (firstInit) {
return;
}
try {
final OAuth2AccessToken accessToken = serviceToken.getAccessTokenClientCredentialsGrant();
for (String id : ids) {
String query = eventsEndPoint + "/ES/EventType/" + packagename + ":" + eventname + "/v1/Events?$filter=substringof(%27" +
id + "%27,ThingId)%20and%20substringof(%27In%27,EventStatus)&$orderby=BusinessTimestamp%20desc";
OAuthRequest request = new OAuthRequest(Verb.GET, query);
request.addHeader("Accept", "*");
serviceToken.signRequest(accessToken, request);
final Response response = serviceToken.execute(request);
//The response body is xml. Use your preferred method to navigate into it.
//Navigate the xml response feed-->entry-->content-->m:properties-->d:ExternalId
Map<String, Object> map = (Map<String, Object>) U.fromXml(response.getBody());
Object l1Map = map.get("feed");
Object l2Map = null;
if (l1Map != null){
l2Map = ((Map<String, Object>)l1Map).get("entry");
}
if (l2Map != null){
Map<String, Object> events = (Map<String, Object>)l2Map;
String fenceid = (String)((Map<String, Object>)((Map<String, Object>)events.get("content")).get("m:properties")).get("d:ExternalId");
lastStatus_thingid_fenceid.put(id, fenceid);
}
}
firstInit = true;
} catch (IOException | InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
private boolean checkLastStatus(String thing, String id, boolean within) {
String lastfence = lastStatus_thingid_fenceid.get(thing);
boolean differentStatus = false;
//egress
if (lastfence != null && id.contentEquals(lastfence) && !within){
lastStatus_thingid_fenceid.remove(thing);
differentStatus = true;
}
//fence changed
else if(lastfence != null && !id.contentEquals(lastfence) && within){
lastStatus_thingid_fenceid.put(thing, id);
differentStatus = true;
}
//ingress
else if(lastfence == null && within){
lastStatus_thingid_fenceid.put(thing, id);
differentStatus = true;
}
return differentStatus;
}
}
package com.sap.iot;
import com.github.scribejava.core.model.OAuth2AccessToken;
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.model.Response;
import com.github.scribejava.core.model.Verb;
import com.github.scribejava.core.oauth.OAuth20Service;
import com.github.underscore.lodash.U;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
public class DiscoveryThings implements Runnable {
private Oauth2 oauth2;
private OAuth20Service serviceToken;
private String clientid;
private String clientsecret;
private String thingsEndPoint;
private String thingtype;
private static List<String> thingIds = Collections.synchronizedList(new ArrayList<>());
DiscoveryThings(Map<String, String> configurationSet) {
clientid = configurationSet.get("clientid");
clientsecret = configurationSet.get("clientsecret");
thingsEndPoint = configurationSet.get("advancedlist-thing-sap");
thingtype = configurationSet.get("thingtype");
if (StringUtils.isEmpty(clientid)){
throw new IllegalStateException("clientid parameter not defined");
}
if (StringUtils.isEmpty(clientsecret)){
throw new IllegalStateException("clientsecret parameter not defined");
}
if (StringUtils.isEmpty(thingsEndPoint)){
throw new IllegalStateException("advancedlist-thing-sap parameter not defined");
}
}
static List<String> getThingIds() {
return thingIds;
}
public static void setThingIds(List<String> thingIds) {
DiscoveryThings.thingIds = thingIds;
}
@Override
public void run() {
if(oauth2 == null){
oauth2 = new Oauth2(clientid, clientsecret);
}
if(serviceToken == null){
serviceToken = oauth2.getNewService();
}
try {
final OAuth2AccessToken accessToken = serviceToken.getAccessTokenClientCredentialsGrant();
String query = thingsEndPoint + "/CompositeThings/v1/Things?$filter=substringof(%27" + thingtype + "%27,ThingType)";
OAuthRequest request = new OAuthRequest(Verb.GET, query);
request.addHeader("Accept", "*");
serviceToken.signRequest(accessToken, request);
final Response response = serviceToken.execute(request);
//The response body is xml. Use your preferred method to navigate into it.
//Navigate the xml response feed-->entry-->content-->m:properties-->d:ThingId
Map<String, Object> map = (Map<String, Object>) U.fromXml(response.getBody());
Object l1Map = map.get("feed");
Object l2Map = null;
if (l1Map != null){
l2Map = ((Map<String, Object>)l1Map).get("entry");
}
if (l2Map != null){
List<Map<String, Object>> things = (List<Map<String, Object>>)l2Map;
synchronized (thingIds) {
thingIds.clear();
for(Map<String, Object> thing : things){
String thingId = (String)((Map<String, Object>)((Map<String, Object>)thing.get("content")).get("m:properties")).get("d:ThingId");
thingIds.add(thingId);
}
}
}
} catch (IOException | InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
package com.sap.iot;
import com.github.scribejava.core.builder.api.DefaultApi20;
import java.util.Map;
public class LeonardoIoTAPIAuth extends DefaultApi20 {
private static String accessToken;
private static String url;
public static void setAccessToken(String accessToken) {
LeonardoIoTAPIAuth.accessToken = accessToken + "/oauth/token";
}
public static void setUrl(String url) {
LeonardoIoTAPIAuth.url = url;
}
protected LeonardoIoTAPIAuth() {
}
private static class InstanceHolder {
private static final LeonardoIoTAPIAuth INSTANCE = new LeonardoIoTAPIAuth();
}
public static LeonardoIoTAPIAuth instance() {
return InstanceHolder.INSTANCE;
}
public String getAccessTokenEndpoint() {
return accessToken;
}
protected String getAuthorizationBaseUrl() {
return url;
}
@Override
public String getAuthorizationUrl(String responseType, String apiKey, String callback, String scope, String state, Map<String, String> additionalParams) {
return super.getAuthorizationUrl(responseType, apiKey, callback, scope, state, additionalParams);
}
}
package com.sap.iot;
import com.github.scribejava.core.builder.api.DefaultApi20;
import java.util.Map;
public class LeonardoIoTAPIAuth extends DefaultApi20 {
private static String accessToken;
private static String url;
public static void setAccessToken(String accessToken) {
LeonardoIoTAPIAuth.accessToken = accessToken + "/oauth/token";
}
public static void setUrl(String url) {
LeonardoIoTAPIAuth.url = url;
}
protected LeonardoIoTAPIAuth() {
}
private static class InstanceHolder {
private static final LeonardoIoTAPIAuth INSTANCE = new LeonardoIoTAPIAuth();
}
public static LeonardoIoTAPIAuth instance() {
return InstanceHolder.INSTANCE;
}
public String getAccessTokenEndpoint() {
return accessToken;
}
protected String getAuthorizationBaseUrl() {
return url;
}
@Override
public String getAuthorizationUrl(String responseType, String apiKey, String callback, String scope, String state, Map<String, String> additionalParams) {
return super.getAuthorizationUrl(responseType, apiKey, callback, scope, state, additionalParams);
}
}
public static void main (String[] args){
//Settings is a class not provided in the example that just import your configuration from a json file
Settings set = new Settings();
try {
set.importConfiguration();
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//The services
Runnable evaluateGeofence = new GeofenceEvaluation(set.getConfigurationSet());
Runnable thingDiscovery = new DiscoveryThings(set.getConfigurationSet());
//thing discovery
scheduledExecutorService.scheduleAtFixedRate(thingDiscovery, 0l, 5l, TimeUnit.HOURS);
//geofence evaluation
scheduledExecutorService.scheduleAtFixedRate(evaluateGeofence, 5l, 5l, TimeUnit.SECONDS);
}
{
"details-thing-sap": "https://details-thing-sap.cfapps.eu10.hana.ondemand.com",
"advancedlist-thing-sap": "https://advancedlist-thing-sap.cfapps.eu10.hana.ondemand.com",
"geolocation": "https://sap-iot-noah-live-geolocation-runtime.cfapps.eu10.hana.ondemand.com",
"events-sap": "https://events-sap.cfapps.eu10.hana.ondemand.com",
"clientid": "sb-dba2b987-9dc2-4e92-983a-06c7bccd5fa1!b11300|iotae_service!b5",
"spacename": "Storage",
"clientsecret": "<here my secret>",
"url": "https://iotpmdemosenv.authentication.eu10.hana.ondemand.com",
"thingtype": "PalletType",
"eventname": "geofenceViolation",
"packagename": "iot.iotpmdemosenv.truck"
}
cf push truckTrack -u process -p target/truckTrack-jar-with-dependencies.jar
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
11 | |
10 | |
10 | |
10 | |
8 | |
7 | |
7 | |
7 | |
7 | |
6 |