How to get DataLogger working from Android App - movesense

We are working on an app for the MoveSense that tracks the users movement in some particular cases. However due to our environment the Bluetooth connection can drop out intermittently. To not have any data loss we want to store the sensor data on the MoveSense and read it once the connection is back. In the documentation we found the DataLogger interface, but we are having issues getting it to work.
In our Android app we first subscribe to the sensor we want (for now only the gyro, but we will expand to include the accelerometer once we have the gyro up and running).
To do this we execute a put command:
Mds put() uri: suunto://<SERIAL>/Mem/DataLogger/Config contract: {"config": { "dataEntries": {"dataEntry": [{"path": "/Meas/Gyro/13"}]}}}
This command is accepted with a '200' code (figuring out the right JSON also took some time as the documentation lacks the 'config' part and uses a completely different path).
After this we try to activate the logger:
Mds put() uri: suunto://<SERIAL>/Mem/DataLogger/State contract: {"newState": 5}
But on this command we get a '403' (FORBIDDEN) error back:
[SDS RESPONSE] type: PUT status: FORBIDDEN header: {"Status": 403, "TaskId": 28, "Reason": "FORBIDDEN", "Uri": "suunto://<SERIAL>/Mem/DataLogger/State", "Content-Length": 0}
What are we doing wrong here? Is there an error in the config? Of are we forgetting some other action?
Note that we made sure to flash an app on the MoveSense that has the DataLoger and Logbook modules enabled.

First step before we can start logging we need to create DataLogger config.
Example for config with Accelerometer and Gyroscope logs.
{
"dataEntries" : {
"dataEntry" : [{
"path" : "/Meas/Acc/13"
}, {
"path" : "/Meas/Gyro/13"
}
]
}
}
Creating config in Android example:
PATH: {serial}/Mem/DataLogger/Config/ REQUEST: PUT
Mds.builder().build(context).put("suunto://" + movesenseSerial + "/Mem/DataLogger/Config/",
jsonConfig, new MdsResponseListener() {
#Override
public void onSuccess(String s) {
}
#Override
public void onError(MdsException e) {
}
});
EXAMPLE RESPONSE:
{"Content": {"dataEntries": {"dataEntry": [{"path": "/Meas/Acc/13"}, {"path": "/Meas/Gyro/13"}]}}}
When config is ready we can start logging.
To start logging, PUT value DATALOGGER_LOGGING (=3) to Mem/DataLogger/State resource
Android start logging example:
PATH: {serial}/Mem/DataLogger/State/ REQUEST: PUT
Mds.builder().build(context).put("suunto://" + movesenseSerial + /Mem/DataLogger/State/,
"{\"newState\":3}", new MdsResponseListener() {
#Override
public void onSuccess(String data) {
}
#Override
public void onError(MdsException error) {
}
});
EXAMPLE RESPONSE:
{"Content": 3}
To stop logging, PUT value DATALOGGER_READY (=2) to Mem/DataLogger/State resource
Android stop logging example:
PATH: {serial}/Mem/DataLogger/State/ REQUEST: PUT
Mds.builder().build(context).put("suunto://" + movesenseSerial + /Mem/DataLogger/State/,
"{\"newState\":2}", new MdsResponseListener() {
#Override
public void onSuccess(String data) {
}
#Override
public void onError(MdsException error) {
}
});
EXAMPLE RESPONSE:
{"Content": 2}
After log file is created we can get all entries / logs from the device:
PATH: /MDS/Logbook/{serial}/Entries REQUEST: GET
Mds.builder().build(context).get("suunto://" + movesenseSerial + "/Mem/Logbook/Entries/",
null, new MdsResponseListener() {
#Override
public void onSuccess(String data) {
}
#Override
public void onError(MdsException error) {
}
});
EXAMPLE RESPONSE:
{"elements": [{"Id": 1, "ModificationTimestamp": 536927972, "Size": null}, {"Id": 5, "ModificationTimestamp": 4446227, "Size": null}]}
When we have Entries we can read them
PATH: /MDS/Logbook/{serial}/byId/{LogId}/Summary REQUEST: GET
Mds.builder().build(context).get("suunto://MDS/Logbook/" + movesenseSerial + "/byId/" + entryId + "/Data",
null, new MdsResponseListener() {
#Override
public void onSuccess(String data) {
}
#Override
public void onError(MdsException error) {
}
});

Related

Java Kube client patch doesn't update configmap

Using Java client - "io.kubernetes:client-java:11.0.0"
I am trying to use patch utils with JSON as
"{ "op": "replace", "path": "/data/stream.properties", "value": "abcd.topic[com.test.xyz.poc.basicauditbatch1]=avro_test1 " }";
CoreV1Api api = new CoreV1Api(client);
V1ConfigMap configMap = PatchUtils.patch(V1ConfigMap.class, () -> api.patchNamespacedConfigMapCall(
name,
namespace,
new V1Patch(jsonPatchStr),
null,
null,
null,
null,
new ApiCallbackDebugger()), V1Patch.PATCH_FORMAT_STRATEGIC_MERGE_PATCH, client);
System.out.println("patched configmap data "+ configMap.getData());
return configMap;
API call to the server went without any exception, however configmap remains unaffected. I have tried add, remove and replace Json patch options but still no luck.
Any suggestions what could I am missing, also tried following patch formats but still response remains same.
public static final String PATCH_FORMAT_JSON_PATCH = "application/json-patch+json";
public static final String PATCH_FORMAT_JSON_MERGE_PATCH = "application/merge-patch+json";
Though one strange thing on callback added for debugging, I neither see success nor failure just calls to onUpload and onDownload progress.
public static class ApiCallbackDebugger implements ApiCallback {
#Override
public void onFailure(ApiException e, int statusCode, Map responseHeaders) {
System.out.println("Call back Failure " );
e.printStackTrace();
LOGGER.error(
"kube.call.failure status {} response {} headers {}",
statusCode,
e.getResponseBody(),
responseHeaders,
e);
}
#Override
public void onSuccess(Object result, int statusCode, Map responseHeaders) {
System.out.println("CALLBACK Successful - " + statusCode + " status "+statusCode + " responseHeader "+ responseHeaders);
LOGGER.debug(
"api.call.success statusCode {} response {} responseHeader {}",
statusCode,
result,
responseHeaders);
}
#Override
public void onUploadProgress(long bytesWritten, long contentLength, boolean done) {
System.out.println("On Callback upload progress.");
}
#Override
public void onDownloadProgress(long bytesRead, long contentLength, boolean done) {
System.out.println("On Callback download progress.");
}
}
Any inputs / pointers appreciated.

Spring Cloud Gateway altering form data does not work

I defined this GatewayFilter:
EDIT More context information:
What I would like to achieve is to avoid the client providing its credentials to get an access token from an authorization server.
The client sends a POST request with user's credentials (username/password) and the gateway adds all complementary information like scope, client_id, grant_type etc... before forwarding the request to the authorization server.
#Component
public class OAuth2CredentialsAppenderGatewayFilterFactory extends AbstractGatewayFilterFactory<OAuth2CredentialsAppenderGatewayFilterFactory.Config> {
public OAuth2CredentialsAppenderGatewayFilterFactory() {
super(Config.class);
}
#Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpRequest.Builder requestBuilder = exchange.getRequest().mutate();
if ("x-www-form-urlencoded".equals(request.getHeaders().getContentType().getSubtype())) {
//This code is not executed, the call of formData.put does not do anything, even a breakpoint is not reached!
if (request.getMethod().equals(HttpMethod.POST)) {
exchange.getFormData().map(formData -> {
formData.put("key1", List.of("value1"));
formData.put("key2", List.of("value2"));
formData.put("key3", List.of("value3"));
return formData;
});
}
//This part of code works well, the header is added to the forwarded request
requestBuilder.header(HttpHeaders.AUTHORIZATION,
"Basic " + Base64Utils.encodeToString((this.uiClientId + ":" + this.uiClientSecret).getBytes()));
}
return chain.filter(exchange.mutate().request(requestBuilder.build()).build());
};
}
}
I use the filter like this:
- id: keycloak_token_route
uri: http://localhost:8180
predicates:
- Path=/kc/token
filters:
- OAuth2CredentialsAppender
- SetPath=/auth/realms/main/protocol/openid-connect/token
- name: RequestRateLimiter
args:
key-resolver: "#{#userIpKeyResolver}"
redis-rate-limiter.replenishRate: 20
redis-rate-limiter.burstCapacity: 30
denyEmptyKey: false
The filter is well invoked but altering the incoming request body does not work.
I am new to the reactive world so I am a bit confused, any help will be appreciated.
For those who would like to do the same thing, this is how I solved my problem. Again I am not an expert of Reactive programming, I am still learning it so it might be a better answer.
#Component
public class OAuth2CredentialsAppenderGatewayFilterFactory extends AbstractGatewayFilterFactory<OAuth2CredentialsAppenderGatewayFilterFactory.Config> {
#Value("${uiservice.clientId}")
private String uiClientId;
#Value("${uiservice.clientSecret}")
private String uiClientSecret;
public OAuth2CredentialsAppenderGatewayFilterFactory() {
super(Config.class);
}
#Override
public GatewayFilter apply(Config config) {
return (ServerWebExchange exchange, GatewayFilterChain chain) -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpRequest.Builder requestBuilder = exchange.getRequest().mutate();
if (nonNull(request.getHeaders().getContentType()) && request.getHeaders().getContentType().equals(MediaType.APPLICATION_FORM_URLENCODED)) {
if (requireNonNull(request.getMethod()).equals(HttpMethod.POST)) {
//Use this filter to modify the request body
ModifyRequestBodyGatewayFilterFactory.Config requestConf = new ModifyRequestBodyGatewayFilterFactory.Config()
.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.setRewriteFunction(String.class, String.class, this.completeRequestBody());
requestBuilder.header(HttpHeaders.AUTHORIZATION, base64Encoding(this.uiClientId, this.uiClientSecret));
return new ModifyRequestBodyGatewayFilterFactory().apply(requestConf).filter(exchange.mutate().request(requestBuilder.build()).build(), chain);
}
}
return chain.filter(exchange.mutate().request(requestBuilder.build()).build());
};
}
/** Add some config params if needed */
public static class Config {
}
/** Complete request by adding required information to get the access token. Here we can get 2 type of token: client_credentials or password. If the param client_only=true we should get a client_credentials token */
private RewriteFunction<String, String> completeRequestBody() {
return (ServerWebExchange ex, String requestBody) -> {
requireNonNull(requestBody, "Body is required");
//if body contains only this, we should get a client_credentials token
var idForClientCredentialsOnly = "client=ui&client_only=true";
String finalRequestBody;
var joiner = new StringJoiner("");
if (idForClientCredentialsOnly.equalsIgnoreCase(requestBody)) {
joiner.add("grant_type=").add("client_credentials");
}
else {
joiner.add(requestBody);
if (!containsIgnoreCase(requestBody, "grant_type")) {
joiner.add("&grant_type=").add("password");
}
}
if (!containsIgnoreCase(requestBody, "scope")) {
joiner.add("&scope=").add("uiclient");//I use Keycloak so I specify the scope to get some extra information
}
finalRequestBody = joiner.toString();
return Mono.just(isBlank(finalRequestBody) ? requestBody : finalRequestBody);
};
}
}

vertx.executeBlocking failing

I am new to vertx and am trying to execute a function1 using vertx.executeBlocking from ABCHandler.java
public class ABCHandler implements Handler<RoutingContext> {
public ABCHandler( Vertx vertx)
{this.vertx =vertx;}
#Override
public void handle(RoutingContext routingContext) {
vertx.executeBlocking(future -> {
function1(routingContext, as ->
{
if (as.failed()) {
future.fail(as.cause());
} else {
future.complete(as.result());
}
});
}, rs -> {
if (rs.failed()) {
routingContext.response().putHeader(CONTENT_TYPE,
"application/json").setStatusCode(Integer.valueOf(401)).end("error");
} else {
routingContext.put("key_1", rs.result());
routingContext.next();
}
});
}
}
ABCHandler is meant to validate some data before request is routed to actual URI. But after routingContext.next(); I am getting 500 (Internal server error).
WebClientCreator.getWebClient(vertx);
Router router = Router.router(vertx);
router.route().handler(new ABCHandler(vertx));
router.post(AgentBindingConstants.AGENT_ENROLLMENT_URI).handler(
BodyHandler.create().setBodyLimit(10000));
router.post("/abc").handler(routingContext -> {
//some code
});
Also, when I run same code as non blocking it works.
Any help here is much appreciated.

Vertx delay when call many request to api

this is mycode. It seem only execute 1 request
public class RestFulService extends AbstractVerticle {
#Override
public void start() throws Exception {
Router router = Router.router(vertx);
router.get("/test/hello/:input").handler(new Handler<RoutingContext>() {
#Override
public void handle(RoutingContext routingContext) {
WorkerExecutor executor = vertx.createSharedWorkerExecutor("my-worker-pool",10,120000);
executor.executeBlocking(future -> {
try {
Thread.sleep(5000);
future.complete();
} catch (InterruptedException e) {
e.printStackTrace();
}
},false, res -> {
System.out.println("The result is: " + res.result());
routingContext.response().end("routing1"+res.result());
executor.close();
});
}
});
}
When i call 10 request from browser in same time, it take 50000ms to done all request.
Please guide me fix it.
Try with curl, I suspect your browser is using the same connection for all requests (thus waiting for a response before sending the next request).
By the way, you don't need to call createSharedWorkerExecutor on each request. You can do it once when the verticle is started.

Issue with Google Contacts API

I'm running an issue with gwt-oauth and Google contacts API.
I use gwt-oauth to login and everything works fine.
While running the RPC for retrieving the contacts I get
WARNING: Authentication error: Unable to respond to any of these challenges: {}
java.lang.NullPointerException: No authentication header information
Here is the code in Client
Button button = new Button("Authenticate with Google");
button.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
final AuthRequest req = new AuthRequest(K.GOOGLE_AUTH_URL, K.GOOGLE_CLIENT_ID).withScopes(K.CONTACTS_SCOPE, K.AUTH_SCOPE);
AUTH.expiresIn(req);
AUTH.login(req, new Callback<String, Throwable>() {
#Override
public void onSuccess(final String token) {
greetingService.loginDetails(token, new AsyncCallback<LoginInfo>() {
#Override
public void onSuccess(LoginInfo result) {
greetingService.getContactList(token, new AsyncCallback<Boolean>() {
#Override
public void onSuccess(Boolean result) {
Window.alert("oh");
}
#Override
public void onFailure(Throwable caught) {
Window.alert("Error:\n" + caught.getMessage());
}
});
}
#Override
public void onFailure(Throwable caught) {
// TODO Auto-generated method stub
}
});
}
#Override
public void onFailure(Throwable caught) {
Window.alert("Error:\n" + caught.getMessage());
}
});
}
});
And here the serverside for contacts:
try {
ContactsService s = new ContactsService(K.APPLICATION_NAME);
s.setProtocolVersion(ContactsService.Versions.V3);
s.setAuthSubToken(token);
s.setHeader("Authorization", "Bearer " + token);
for (ContactEntry entry : s.query(new Query(new URL(K.CONTACTS_SCOPE)), ContactFeed.class).getEntries())
System.out.println(entry.toString());
} catch (Exception e) {
e.printStackTrace();
}
return true;
This was working couple of weeks ago...
I assume is not a scope issue since loginDetails works properly...
Any idea?
Solved.
Scope for contacts in Auth was set to: https://www.google.com/m8/feeds/contacts/default/full/
Apparently this doesn't work anymore and I just set https://www.google.com/m8/feeds/ for auth
and the full url for querying in ContactService