I've never really done much C and am a bit stumped on the best way to send a boolean from an Android app to the Pebble Watch.
I have strings working fine, but there doesn't seem to be an addBoolean method on PebbleDictionary. As a work around I am trying to use addUint8 to send a 1 or 0, but am having trouble handling the message on the Pebble.
Here is my Android code:
PebbleDictionary data = new PebbleDictionary();
if (isGPSFix()){
data.addUint8(GPS_HAS_FIX_KEY, Byte.valueOf("1"));
} else {
data.addUint8(GPS_HAS_FIX_KEY, Byte.valueOf("0"));
}
PebbleKit.sendDataToPebble(app.getContext(), UUID, data);
And in my Pebble I have a data struct:
static struct MyData {
uint8_t haveGPS[1];
.... // other stuff ommitted
AppSync sync;
uint8_t sync_buffer[256];
} s_data;
And then I am trying to compare it like this in my sync_tuple_changed callback.
static void sync_tuple_changed_callback(const uint32_t key, const Tuple* new_tuple, const Tuple* old_tuple, void* context) {
(void) old_tuple;
switch (key) {
case GPS_HAS_FIX_KEY:
if (memcmp(s_data.haveGPS, new_tuple->value->data, 8) == 0){
memcpy(s_data.haveGPS,new_tuple->value->data, new_tuple->length);
vibes_short_pulse();
}
break;
default:
return;
}
}
The watch doesn't crash, it just never vibrates when the phone drops or acquires GPS.
Things look good on the Android side. I think this is more of an AppSync problem.
Here are a few things to check in the watch application:
Make sure you create a list of tuples with initial values on the watch. This list needs to to contain your key GPS_HAS_FIX_KEY;
Tuplet initial_values[] = {
TupletInteger(GPS_HAS_FIX_KEY, (uint8_t) 0),
/* Other tuplets that you will synchronize */
};
Make sure you pass those tuplets to the app_sync_init() function:
app_sync_init(&sync, sync_buffer, sizeof(sync_buffer),
initial_values, ARRAY_LENGTH(initial_values),
sync_tuple_changed_callback, sync_error_callback, NULL);
Those two steps are required for app_sync to work (cf AppSync reference documentation).
As far as I understand you have to send an Dictionary of keys and objects to the watch. In Objective C it looks like this:
NSDictionary *update = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
Keys are all integer and objects I would think can be of Boolean. In my case only String what works well.
In a loop I perform something like this on the watch:
t=dict_find(received,count);
strcpy(strs[count], dict_find(received,count)->value->cstring);
But I have to tell you that I am still a rookie.
Thanks to sarfata's accepted answer above, I found that I had not added this new item to the Tuple that the Pebble was expecting.
Once I added that, my switch statement started working and I just had to get the memory compare to work. Here is the working code for my memory compare in case it helps anyone.
case GPS_HAS_FIX_KEY:
if (memcmp(s_data.haveGPS, new_tuple->value->data, 1) != 0){
memcpy(s_data.haveGPS,new_tuple->value->data, 1);
vibes_short_pulse();
}
break;
It really was a simple as expecting one byte (not 8 bytes - I thought it was doing a bit compare) and negating the logic for the case where the new value is NOT like the old one.
Related
I'm working with android sensors and have a method inside a listener that keeps appending data on a string builder with really high frequency. After some data is collected I compress the string with gzip and write it on a file to avoid out of memory exceptions. This keeps repeating forever. This is all in the same thread so as the file gets bigger it starts to block the thread and the data appending on the string. I do create new files if they get too large but i think i need to implement a threading and lock mechanism for the compression and file writing to avoid any blocking but at the same time not have any problems with leakage of data. Can anyone help me with that? Im not sure if im wording my question correctly.
// on rotation method of gyroscope
#Override
public void onRotation(long timestamp,float rx, float ry, float rz) {
try {
//get string of new lines of the write data for the sensor
str.append("gyroTest,userTag=testUser,deviceTag="+deviceName+" rx="+rx+",ry="+ry+",rz="+rz+" "+timestamp+"\n");
if(count >=2000){
b = GZIPCompression.compress(str);
Log.i(FILE_TAG, "Write gyroscope file");
FileHandling.testWrite( GYROSCOPE,b);
str.setLength(0);
count=0;
}
count++;
} catch (Exception e) {
e.printStackTrace();
}
}
You're on the right track in that you need to separate reading from the sensor, processing the data, and writing it all back to disk.
To pass the data from the sensor reads, you may consider using something like a LinkedBlockingQueue with your Strings.
private LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<String>();
#Override
public void onRotation(long timestamp, float rx, float ry, float rz) {
queue.add(
"gyroTest,userTag=testUser,deviceTag="+deviceName+" rx="+rx+",ry="+ry+",rz="+rz+" "+timestamp+"\n"
);
}
And then in another Thread, looping until canceled, you could drain the queue, process, and write without blocking the reading (main) Thread.
private boolean canceled = false;
private void startProcessingQueue() {
Runnable processQueueRunnable = new Runnable() {
#Override
public void run() {
while (!canceled) {
drainQueueAndWriteLog();
Thread.sleep(250);
}
}
};
new Thread(processQueueRunnable)
.start();
}
private void drainQueueAndWriteLog() {
List<String> dequeuedRotations = new ArrayList<String>();
queue.drainTo(dequeuedRotations);
if (0 < dequeuedRotations.size()) {
// Write each line, or all lines together
}
}
Note: take care to ensure the runnable is canceled when your Activity is paused.
As mentioned in your question, the more data you're writing, the slower it's going to be. Since you're writing data from a sensor, it's inevitably going to grow. For this, you could partition your files into smaller segments, by using something like a date-based naming convention for your log files.
For instance, a log name pattern of yyyyMMddHHmm would create minute-spaced log files, which you could then later aggregate and sort.
private SimpleDateFormat logFileDateFormat = new SimpleDateFormat("yyyyMMddHHmm");
private String getCurrentLogFileName() {
return String.format(
"rotations-%s.log",
logFileDateFormat.format(new Date())
);
}
Just keep in mind that since you're not writing in the same thread you're reading from, your timestamps may not match up perfectly with your log file names. This shouldn't be a problem, though, as you're already including the timestamps in the persisted data.
Further down the line, if you're still finding you're not quite hitting the level of write-throughput that your project requires, you may also want to consider condensing the amount of information you're actually storing by encoding common byte usages, or even reducing the length of each key to their most-unique values. For example, consider this 1 line output:
"gyroTest,userTag=testUser,deviceTag=some-device-name rx=12345,ry=4567,rz=87901872166251542545144\n"
And now reducing the keys:
"gyroTest,u=testUser,d=some-device-name x=12345,y=4567,z=87901872166251542545144\n"
Removes 18 characters from every line that needs to be written, without sacrificing any information.
Also worth noting: you either need a space (or better a comma) before the timestamp in your data line, else you won't be able to nicely pick out rz from it. And your deviceName should be escaped with quotation marks if it can contain spaces, else it will conflict with pulling out rx.
In a C++ file, I want to convert a const char* to KString, so that I can then pass the KString to a Kotlin file using Kotlin/Native.
I believe the answer lies in the function
OBJ_GETTER(utf8ToUtf16, const char* rawString, size_t rawStringLength)
that I found in KString.cpp. But even though I discovered the used define statements in Memory.h, I have not yet managed to properly call the function utf8ToUtf16 from my own C++ file to get a KString. Any help is appreciated.
It depends on how you want to interact with Kotlin code. If you produce dynamic library with -produce dynamic, then string are converted automatically, see for example https://github.com/JetBrains/kotlin-native/blob/adf8614889e8cf5038a79960aa9651ca7d45e409/samples/python_extension/src/main/c/kotlin_bridge.c#L72.
So no additional magic is required at all. Same with Objective-C strings and -produce framework. And for other cases, there shall be no need to pass strings C -> Kotlin (callbacks produced with staticCFunction also do autoconversion).
I ended up taking the pieces to write my own function:
KString getKString(const char* rawString) {
size_t rawStringLength = strlen(rawString);
ObjHeader** OBJ_RESULT;
uint32_t charCount = utf8::unchecked::distance(rawString, rawString + rawStringLength);
ArrayHeader* result = AllocArrayInstance(theStringTypeInfo, charCount, OBJ_RESULT)->array();
KChar* rawResult = CharArrayAddressOfElementAt(result, 0);
auto convertResult =
utf8::unchecked::utf8to16(rawString, rawString + rawStringLength, rawResult);
ObjHeader* obj = result->obj();
UpdateReturnRef(OBJ_RESULT, obj);
return (const ArrayHeader*)obj;
}
In my test code (C++), I use it like this:
...
RuntimeState* state = InitRuntime();
KString inMessage;
{
ObjHolder args;
inMessage = getKString("Hello from C++");
}
...
DeinitRuntime(state);
and include Memory.h, Natives.h, Runtime.h, KString.h, utf8.h, stdlib.h, and string. You may be able to get rid of some of these.
As a side remark, you may realize how AllocArrayInstance is used in the function. It would be nice, if one simply could do the same thing for getting a KString, something like:
ObjHeader** OBJ_RESULT;
KString kstr = utf8ToUtf16(rawString, rawStringLength, OBJ_RESULT);
This did not work from my function, since utf8ToUtf16 was not found. I believe the reason is, that (at the time of writing) the respective function in KString.cpp is inside a namespace {...} block, such that it cannot be used from another file. That's why I ended up mimicking the function as shown above.
I have limit knowledge of Refbase, sp, wp of android. As far as I know if the refcount of one sp(strong point) decreased to 0, then the object wrapped in sp would be deleted.
Now I want to debug a snippet of codes from android AOSP, it looks like:
void OMXClient::disconnect() {
ALOGI("[Alan] OMXClient::disconnect!!!");
if (mOMX.get() != NULL) {
ALOGI("[Alan] will mOMX.clear() !!!");
mOMX.clear();
mOMX = NULL;
}
}
I want to print the refCount of mOMX every time before mOMX.clear().
Is it possible to do that ?
I am currently developing an Android application using Flex 4.5.1 and I am having an issue when trying to pass data that I have stored in a SharedObject array to my Web Service for a Database query. the code below shows how I am storing the data in the SharedObject:
var so:SharedObject = SharedObject.getLocal("app");
public var prefsArray:ArrayCollection = new ArrayCollection(so.data.prefs);
protected function prefs_btn_click(event:MouseEvent):void
{
prefsArray.source.push(getFrsByIDResult.lastResult.id);
so.data.prefs = [prefsArray];
var flushStatus:String = so.flush();
if (flushStatus != null) {
switch(flushStatus) {
case SharedObjectFlushStatus.PENDING:
so.addEventListener(NetStatusEvent.NET_STATUS,
onFlushStatus);
break;
case SharedObjectFlushStatus.FLUSHED:
trace("success");
break;
}
}
}
protected function onFlushStatus(event:NetStatusEvent):void
{
trace(event.info.code);
}
I have tested the SharedObject to see if the information is being entered into it correctly and all seems fine. Now I have used the code below in order to retrieve the data from the SharedObject and try and send it to the PHP web Service to run the DB query.
var so:SharedObject = SharedObject.getLocal("app");
var arrCol:ArrayCollection = new ArrayCollection(so.data.prefs);
var str:String = new String(arrCol.toString());
protected function list_creationCompleteHandler(event:FlexEvent):void
{
getPrefsByprefIdsResult.token = prefsService.getPrefsByPrefIds(so.data.prefs);
}
I have tested the Webservice in Flex and have it configured to recieve an Array of Ints (int[]) and it works when i run a test operation on it with two dummy values. However when I try to use the code above to pass the Web Service the Shared Object data I get this error:
TypeError: Error #1034: Type Coercion failed: cannot convert []#97e97e1 to mx.collections.ArrayCollection.
at views::**************/list_creationCompleteHandler()[C:\Users\Jack\Adobe Flash Builder 4.5\****************\src\views\*******************.mxml:25]
at views::*********************/__list_creationComplete()[C:\Users\Jack\Adobe Flash Builder 4.5\****************\src\views\***************.mxml:94]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\core\UIComponent.as:13128]
at mx.core::UIComponent/set initialized()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\core\UIComponent.as:1818]
at mx.managers::LayoutManager/validateClient()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\managers\LayoutManager.as:1090]
at mx.core::UIComponent/validateNow()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\core\UIComponent.as:8067]
at spark.components::ViewNavigator/commitNavigatorAction()[E:\dev\4.5.1\frameworks\projects\mobilecomponents\src\spark\components\ViewNavigator.as:1878]
at spark.components::ViewNavigator/commitProperties()[E:\dev\4.5.1\frameworks\projects\mobilecomponents\src\spark\components\ViewNavigator.as:1236]
at mx.core::UIComponent/validateProperties()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\core\UIComponent.as:8209]
at mx.managers::LayoutManager/validateProperties()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\managers\LayoutManager.as:597]
at mx.managers::LayoutManager/doPhasedInstantiation()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\managers\LayoutManager.as:783]
at mx.managers::LayoutManager/doPhasedInstantiationCallback()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\managers\LayoutManager.as:1180]
I have replaced certain filenames and locations with *'s to protect the work i am doing, but can someone please help me with this issues as I believe it has to be something simple???
Thanks
ok so let me explain in more detail. This is being designed for an Android app like I said, but image what I am trying to do is to store Bookmarks persistently using the Local Shared Object.
The first chunck of code you see above is designed to create the LSO attribute for the bookmark i want to create and imagine that there can be more than one bookmark set at different times like in a web browser. The only way i could find to do this was to store these items/details in an array which I retrieve and then update before saving back to the LSO and saving.
The second piece of code related to imagine a "Bookmarks Page" with a list of all the content that I have bookmarked. Now what I wanted to happen was thta I would be able to call up the LSO attribute which held the id's of the bookmarks and then load up thier details in a list format.
I have managed to create the LSO and store the bookmark deatils in and allow them to be updated and entries added. Also I have made sure that the PHP code that I have pulls back all the database objects relating to the array of id's and this has been tested using flex. The only thing that I cant seem to do is to pass the id's to the PHP web service file. The code in the Web Service file is below if that helps:
public function getPrefsByPrefIds($PrefIds) {
$stmt = mysqli_prepare($this->connection, "SELECT * FROM $this->tablename WHERE $this->tablename.id IN(" .implode(",", $PrefIds). ")");
$this->throwExceptionOnError();
mysqli_stmt_execute($stmt);
$this->throwExceptionOnError();
$rows = array();
mysqli_stmt_bind_result($stmt, $row->id, $row->name, $row->desc);
while (mysqli_stmt_fetch($stmt)) {
$rows[] = $row;
$row = new stdClass();
mysqli_stmt_bind_result($stmt, $row->id, $row->name, $row->desc);
}
mysqli_stmt_free_result($stmt);
mysqli_close($this->connection);
return $rows;
}
Yes I had already tried that but thanks. I have made some more progress on my own as I have been experimenting with the different types of objects that can be stored in SharedObjects. I have managed to get the solution part working with this code:
This code is designed to capture the boomark info and store it in an arrayCollection before transferring it to a bytesArray and saving
var so:SharedObject = SharedObject.getLocal("app");
public var prefArray:ArrayCollection = new ArrayCollection(so.data.prefs);
protected function prefs_btn_click(event:MouseEvent):void
{
prefArray.source.push(getCompaniesByIDResult.lastResult.id);
so.data.prefs = [prefArray];
var bytes:ByteArray = new ByteArray();
bytes.writeObject(prefArray);
so.data.ac = bytes;
var flushStatus:String = so.flush();
if (flushStatus != null) {
switch(flushStatus) {
case SharedObjectFlushStatus.PENDING:
so.addEventListener(NetStatusEvent.NET_STATUS,
onFlushStatus);
break;
case SharedObjectFlushStatus.FLUSHED:
trace("success");
break;
}
}
}
protected function onFlushStatus(event:NetStatusEvent):void
{
trace(event.info.code);
}
This next code is the designed to retrieve that information from the SahredObjects bytesArray and put it back into an Array Collection
var so:SharedObject = SharedObject.getLocal("app");
var ba:ByteArray = so.data.ac as ByteArray;
var ac:ArrayCollection;
protected function list_creationCompleteHandler(event:FlexEvent):void
{
ba.position = 0;
ac = ba.readObject() as ArrayCollection;
getPrefsByPrefIdsResult.token = prefsService.getPrefsByPrefIds(ac);
}
however as I have said this works in a small way only as if I store only one Bookmark (id) for an item and then go to the bookmarks list the details for that bookark are successfully retrieved, however if I save more than one Bookmark(2 or more id's) the page will not load the details, i do not get an error but I believe it is hanging because it is looking for say id's "1,2" instead of "1" and "2" but i dont know why this is or how to resolve this. I appreciate the advice I have been given but am finding it hard there is no one who can help me with this issue and I am having to do various experiemnts with the code. Can someone please help me with this I would really appreciate it :-) Thanks
I've got a weird problem. When I debug my program and put a breakpoint before the "writeBlock" command to write my MifareClassic card, everything is going fine. The card is written and my program continues.
If I remove the breakpoint, I get an "IO Exception : transceived failed"! I put the breakpoint back without changing my code, it works again!
I'm lost... Could it be possible that the problem comes from the speed of the program execution? Having a breakpoint makes the execution slower...
Here's my code (the authentication is done before this function):
private static boolean WriteMfcBlock(MifareClassic mfc, int blockNumber, byte[] value) {
try {
byte[] toWrite = new byte[MifareClassic.BLOCK_SIZE];
//if the value is less than 16 bytes, fill it with '0'
for (int i=0; i<MifareClassic.BLOCK_SIZE; i++) {
if (i < value.length) toWrite[i] = value[i];
else toWrite[i] = 0;
}
if (!mfc.isConnected()) mfc.connect();
mfc.writeBlock(blockNumber, toWrite);
//Check if the writing is well done
byte[] read = mfc.readBlock(blockNumber);
for (int i = 0; i < MifareClassic.BLOCK_SIZE; i++ ) {
if (toWrite[i] != read[i]) return false;
}
return true;
}
catch (IOException e) {
textViewInfo.setText("IO EXCEPTION");
return false;
}
}
Thanks for your help
Sylvain
I go a step forward. It seems that it can come from a thread issue.
The "writeblock" command of the MifareClassic has to be triggered by the main process of the activity.
In my app, it's a button (implementing OnClickListener) that triggered the "writeblock".
When in debugging mode, the debug thread can hide this behavior because it's the main thread and make the app running well.
So from now, what I did is just to ask the user to remove the tag from the rf field and put it back. So I get the intent that a tag has been discovered again and then I can do the "writeblock" command without any problem.
Finally I thing the best way to handle read and write on tags is the create 2 activities, one for readings and one for writings.
If you have any comment or other way to do it .. please, answer this thread.
Sylvain