Serial Scol plugin
BufferedAsyncSerial.cpp
1/*
2 * File: BufferedAsyncSerial.cpp
3 * Author: Terraneo Federico
4 * Distributed under the Boost Software License, Version 1.0.
5 * Created on January 6, 2011, 3:31 PM
6 */
7
8#include "BufferedAsyncSerial.h"
9#include "plugin.h"
10
11#include <string>
12#include <algorithm>
13#include <iostream>
14#include <boost/bind/bind.hpp>
15
16using namespace std;
17using namespace boost;
18
19//
20//Class BufferedAsyncSerial
21//
22
23#ifndef ANDROID
24BufferedAsyncSerial::BufferedAsyncSerial() : AsyncSerial()
25{
26 setReadCallback(boost::bind(&BufferedAsyncSerial::readCallback, this, boost::placeholders::_1, boost::placeholders::_2));
27}
28
29BufferedAsyncSerial::BufferedAsyncSerial(const std::string& devname,
30 unsigned int baud_rate,
31 asio::serial_port_base::parity opt_parity,
32 asio::serial_port_base::character_size opt_csize,
33 asio::serial_port_base::flow_control opt_flow,
34 asio::serial_port_base::stop_bits opt_stop)
35 : AsyncSerial(devname, baud_rate, opt_parity, opt_csize, opt_flow, opt_stop)
36{
37 setReadCallback(boost::bind(&BufferedAsyncSerial::readCallback, this, boost::placeholders::_1, boost::placeholders::_2));
38}
39
40size_t BufferedAsyncSerial::read(char *data, size_t size)
41{
42 boost::lock_guard<boost::mutex> l(readQueueMutex);
43 size_t result = min(size, readQueue.size());
44 vector<char>::iterator it = readQueue.begin() + result;
45 copy(readQueue.begin(), it, data);
46 readQueue.erase(readQueue.begin(), it);
47 return result;
48}
49
50std::vector<char> BufferedAsyncSerial::read()
51{
52 boost::lock_guard<boost::mutex> l(readQueueMutex);
53 vector<char> result;
54 result.swap(readQueue);
55 return result;
56}
57
59{
60 boost::lock_guard<boost::mutex> l(readQueueMutex);
61 string result(readQueue.begin(), readQueue.end());
62 readQueue.clear();
63 return result;
64}
65
66std::string BufferedAsyncSerial::readStringUntil(const std::string delim)
67{
68 boost::lock_guard<boost::mutex> l(readQueueMutex);
69 vector<char>::iterator it = findStringInVector(readQueue, delim);
70 if (it == readQueue.end()) return "";
71 string result(readQueue.begin(), it);
72 it += delim.size();//Do remove the delimiter from the queue
73 readQueue.erase(readQueue.begin(), it);
74 return result;
75}
76
77void BufferedAsyncSerial::readCallback(const char *data, size_t len)
78{
79 boost::lock_guard<boost::mutex> l(readQueueMutex);
80 readQueue.insert(readQueue.end(), data, data + len);
81
82 //read callback
83 std::string* buffer = new std::string(readQueue.begin(), readQueue.end());
84 OBJpostEvent(SERIAL_READ_CB, SCOL_PTR this, SCOL_PTR buffer);
85 readQueue.clear();
86}
87
88std::vector<char>::iterator BufferedAsyncSerial::findStringInVector(
89 std::vector<char>& v, const std::string& s)
90{
91 if (s.size() == 0) return v.end();
92
93 vector<char>::iterator it = v.begin();
94 for (;;)
95 {
96 vector<char>::iterator result = find(it, v.end(), s[0]);
97 if (result == v.end()) return v.end();//If not found return
98
99 for (size_t i = 0; i < s.size(); i++)
100 {
101 vector<char>::iterator temp = result + i;
102 if (temp == v.end()) return v.end();
103 if (s[i] != *temp) goto mismatch;
104 }
105 //Found
106 return result;
107
108 mismatch:
109 it = result + 1;
110 }
111}
112
113BufferedAsyncSerial::~BufferedAsyncSerial()
114{
116}
117#else
118BufferedAsyncSerial::BufferedAsyncSerial()
119{
120 mPort = "0";
121 mConnected = false;
122 mEnable = true;
123 mThread = boost::thread(boost::bind(&BufferedAsyncSerial::cbThread, this));
124}
125
126BufferedAsyncSerial::BufferedAsyncSerial(const std::string& devname)
127{
128 mPort = devname;
129 mConnected = false;
130 mEnable = true;
131 mThread = boost::thread(boost::bind(&BufferedAsyncSerial::cbThread, this));
132}
133
134BufferedAsyncSerial::~BufferedAsyncSerial()
135{
136 mEnable = false;
137 if (mThread.joinable())
138 mThread.join();
139
140 disconnect();
141}
142
143bool BufferedAsyncSerial::connect()
144{
145 JNIEnv* env = 0;
146 struct android_app* androidApp = (struct android_app*)SCgetExtra("this_inst");
147 androidApp->activity->vm->AttachCurrentThread(&env, NULL);
148
149 // Get class
150 jobject oActivity = androidApp->activity->clazz;
151 jclass cActivity = env->GetObjectClass(oActivity);
152 if (cActivity)
153 {
154 jobject scolexternal_object = 0;
155 jclass scolexternal_clazz = 0;
156 jmethodID jHaveBluetooth = 0;
157
158 jfieldID scolexternal_fieldID = env->GetFieldID(cActivity, "mScolExternal", "Lorg/imaginer/scol/ScolExternal;");
159 if (scolexternal_fieldID)
160 scolexternal_object = env->GetObjectField(oActivity, scolexternal_fieldID);
161 if (scolexternal_object)
162 scolexternal_clazz = env->GetObjectClass(scolexternal_object);
163 if (scolexternal_clazz)
164 jHaveBluetooth = env->GetMethodID(scolexternal_clazz, "haveBluetooth", "()Z");
165
166 if (!jHaveBluetooth)
167 {
168 //LOGI("Error : haveBluetooth not found");
169 env->DeleteLocalRef(cActivity);
170 androidApp->activity->vm->DetachCurrentThread();
171 return false;
172 }
173 bool havebt = (bool)env->CallBooleanMethod(scolexternal_object, jHaveBluetooth);
174
175 if (!havebt)
176 {
177 if (env->ExceptionCheck())
178 {
179 env->ExceptionClear();
180 MMechostr(MSKRUNTIME, "Android bluetooth exception : Check android.permission.BLUETOOTH\n");
181 }
182
183 // Release references
184 env->DeleteLocalRef(scolexternal_object);
185 env->DeleteLocalRef(cActivity);
186 androidApp->activity->vm->DetachCurrentThread();
187 return false;
188 }
189 jmethodID jConnectBluetooth = env->GetMethodID(scolexternal_clazz, "connectBluetooth", "(Ljava/lang/String;)Z");
190
191 if (!jConnectBluetooth)
192 {
193 // Release references
194 env->DeleteLocalRef(scolexternal_clazz);
195 env->DeleteLocalRef(scolexternal_object);
196 env->DeleteLocalRef(cActivity);
197 androidApp->activity->vm->DetachCurrentThread();
198 return false;
199 }
200
201 jstring address = env->NewStringUTF(mPort.c_str());
202 mConnected = (bool)env->CallBooleanMethod(scolexternal_object, jConnectBluetooth, address);
203
204 if (env->ExceptionCheck())
205 {
206 env->ExceptionClear();
207 MMechostr(MSKRUNTIME, "Android bluetooth exception : Could not connect to device.\n");
208 }
209
210 // Release references
211 env->DeleteLocalRef(address);
212 env->DeleteLocalRef(scolexternal_clazz);
213 env->DeleteLocalRef(scolexternal_object);
214 env->DeleteLocalRef(cActivity);
215 }
216
217 androidApp->activity->vm->DetachCurrentThread();
218 return mConnected;
219}
220
221void BufferedAsyncSerial::disconnect()
222{
223 JNIEnv* env = 0;
224 struct android_app* androidApp = (struct android_app*)SCgetExtra("this_inst");
225 androidApp->activity->vm->AttachCurrentThread(&env, NULL);
226
227 // Get class
228 jobject oActivity = androidApp->activity->clazz;
229 jclass cActivity = env->GetObjectClass(oActivity);
230 if (cActivity)
231 {
232 jobject scolexternal_object = 0;
233 jclass scolexternal_clazz = 0;
234 jmethodID jDisconnectBluetooth = 0;
235
236 jfieldID scolexternal_fieldID = env->GetFieldID(cActivity, "mScolExternal", "Lorg/imaginer/scol/ScolExternal;");
237 if (scolexternal_fieldID)
238 scolexternal_object = env->GetObjectField(oActivity, scolexternal_fieldID);
239 if (scolexternal_object)
240 scolexternal_clazz = env->GetObjectClass(scolexternal_object);
241 if (scolexternal_clazz)
242 jDisconnectBluetooth = env->GetMethodID(scolexternal_clazz, "disconnectBluetooth", "()V");
243 if (jDisconnectBluetooth)
244 env->CallVoidMethod(scolexternal_object, jDisconnectBluetooth);
245
246 if (env->ExceptionCheck())
247 {
248 env->ExceptionClear();
249 MMechostr(MSKRUNTIME, "Android bluetooth exception : could not disconnect from device.\n");
250 }
251
252 // Release references
253 env->DeleteLocalRef(scolexternal_clazz);
254 env->DeleteLocalRef(scolexternal_object);
255 env->DeleteLocalRef(cActivity);
256 }
257
258 androidApp->activity->vm->DetachCurrentThread();
259}
260
261void BufferedAsyncSerial::cbThread()
262{
263 while (mEnable)
264 {
265 if (!mConnected)
266 connect();
267
268 //still not conencted wait a bit more
269 if (!mConnected)
270 boost::this_thread::sleep_for(boost::chrono::milliseconds(1000));
271 else
272 readData();
273
274 boost::this_thread::sleep_for(boost::chrono::milliseconds(16));
275 }
276}
277
278void BufferedAsyncSerial::readData()
279{
280 JNIEnv* env = 0;
281 struct android_app* androidApp = (struct android_app*)SCgetExtra("this_inst");
282 androidApp->activity->vm->AttachCurrentThread(&env, NULL);
283
284 // Get class
285 jobject oActivity = androidApp->activity->clazz;
286 jclass cActivity = env->GetObjectClass(oActivity);
287 if (cActivity)
288 {
289 jobject scolexternal_object = 0;
290 jclass scolexternal_clazz = 0;
291 jmethodID jReadBluetooth = 0;
292
293 jfieldID scolexternal_fieldID = env->GetFieldID(cActivity, "mScolExternal", "Lorg/imaginer/scol/ScolExternal;");
294 if (scolexternal_fieldID)
295 scolexternal_object = env->GetObjectField(oActivity, scolexternal_fieldID);
296 if (scolexternal_object)
297 scolexternal_clazz = env->GetObjectClass(scolexternal_object);
298 if (scolexternal_clazz)
299 jReadBluetooth = env->GetMethodID(scolexternal_clazz, "getBluetoothData", "()[B");
300
301 if (!jReadBluetooth)
302 {
303 //LOGI("Error : haveBluetooth not found");
304 env->DeleteLocalRef(cActivity);
305 androidApp->activity->vm->DetachCurrentThread();
306 return;
307 }
308
309 jbyteArray jarray = (jbyteArray)env->CallObjectMethod(scolexternal_object, jReadBluetooth);
310 if (jarray != NULL)
311 {
312 jboolean isCopy;
313 jsize lenght = env->GetArrayLength(jarray);
314 jbyte* data = env->GetByteArrayElements(jarray, &isCopy);
315 if (env->ExceptionCheck())
316 {
317 env->ExceptionClear();
318 env->DeleteLocalRef(jarray);
319 env->DeleteLocalRef(scolexternal_object);
320 env->DeleteLocalRef(cActivity);
321 androidApp->activity->vm->DetachCurrentThread();
322 return;
323 }
324
325 if (lenght > 0)
326 {
327 char* sdata = (char*)malloc(lenght);
328 memcpy(sdata, (char*)data, lenght);
329
330 //send to scol callback
331 std::string* buffer = new std::string(sdata, lenght);
332 OBJpostEvent(SERIAL_READ_CB, SCOL_PTR this, SCOL_PTR buffer);
333
334 free(sdata);
335 }
336
337 if (data != NULL)
338 env->ReleaseByteArrayElements(jarray, data, 0);
339
340 if (env->ExceptionCheck())
341 {
342 env->ExceptionClear();
343 env->DeleteLocalRef(jarray);
344 env->DeleteLocalRef(scolexternal_object);
345 env->DeleteLocalRef(cActivity);
346 androidApp->activity->vm->DetachCurrentThread();
347 return;
348 }
349 env->DeleteLocalRef(jarray);
350 }
351
352 // Release references
353 env->DeleteLocalRef(scolexternal_object);
354 env->DeleteLocalRef(cActivity);
355 }
356
357 androidApp->activity->vm->DetachCurrentThread();
358}
359
360void BufferedAsyncSerial::writeString(const std::string& s)
361{
362 JNIEnv* env = 0;
363 struct android_app* androidApp = (struct android_app*)SCgetExtra("this_inst");
364 androidApp->activity->vm->AttachCurrentThread(&env, NULL);
365
366 // Get class
367 jobject oActivity = androidApp->activity->clazz;
368 jclass cActivity = env->GetObjectClass(oActivity);
369 if (cActivity)
370 {
371 jobject scolexternal_object = 0;
372 jclass scolexternal_clazz = 0;
373 jmethodID jWriteBluetooth = 0;
374
375 jfieldID scolexternal_fieldID = env->GetFieldID(cActivity, "mScolExternal", "Lorg/imaginer/scol/ScolExternal;");
376 if (scolexternal_fieldID)
377 scolexternal_object = env->GetObjectField(oActivity, scolexternal_fieldID);
378 if (scolexternal_object)
379 scolexternal_clazz = env->GetObjectClass(scolexternal_object);
380 if (scolexternal_clazz)
381 jWriteBluetooth = env->GetMethodID(scolexternal_clazz, "writeBluetoothData", "([B)Z");
382
383 if (!jWriteBluetooth)
384 {
385 //LOGI("Error : haveBluetooth not found");
386 env->DeleteLocalRef(cActivity);
387 androidApp->activity->vm->DetachCurrentThread();
388 return;
389 }
390
391 char* sdata = (char*)malloc(s.size());
392 memcpy(sdata, s.data(), s.size());
393 int dataSize = s.size();
394
395 jbyteArray jdata = env->NewByteArray(dataSize);
396 env->SetByteArrayRegion(jdata, 0, dataSize, (jbyte *)sdata);
397
398 bool result = (bool)env->CallBooleanMethod(scolexternal_object, jWriteBluetooth, jdata);
399 jboolean isCopy;
400 if (jdata != NULL)
401 {
402 env->ReleaseByteArrayElements(jdata, (jbyte *)sdata, JNI_COMMIT);
403
404 if (env->ExceptionCheck())
405 {
406 env->ExceptionClear();
407 env->DeleteLocalRef(jdata);
408 env->DeleteLocalRef(scolexternal_object);
409 env->DeleteLocalRef(cActivity);
410 free(sdata);
411
412 if (!result)
413 mConnected = false;
414
415 androidApp->activity->vm->DetachCurrentThread();
416 return;
417 }
418 }
419
420 // Release references
421 env->DeleteLocalRef(jdata);
422 env->DeleteLocalRef(scolexternal_object);
423 env->DeleteLocalRef(cActivity);
424 free(sdata);
425
426 //error disconnected
427 if (!result)
428 mConnected = false;
429 }
430
431 androidApp->activity->vm->DetachCurrentThread();
432}
433
434#endif
void clearReadCallback()
void writeString(const std::string &s)
std::string readStringUntil(const std::string delim="\n")
std::vector< char > read()