2 Copyright (C) 2012 Intel Corporation
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "obd2source.h"
22 #include <boost/assert.hpp>
23 #include <boost/lexical_cast.hpp>
26 //#include <json-glib/json-glib.h>
27 #include <listplusplus.h>
29 #include "bluetooth.hpp"
30 #include "timestamp.h"
32 #define __SMALLFILE__ std::string(__FILE__).substr(std::string(__FILE__).rfind("/")+1)
33 AbstractRoutingEngine *m_re;
35 //std::list<ObdPid*> Obd2Amb::supportedPidsList;
36 Obd2Amb *obd2AmbInstance = new Obd2Amb;
37 VehicleProperty::Property Obd2Connected = "Obd2Connected";
38 int calledPersecond = 0;
40 bool sendElmCommand(obdLib *obd,std::string command)
42 std::vector<unsigned char> replyVector;
44 obd->sendObdRequestString(command.append("\r").c_str(),command.length()+1,&replyVector,10,3);
45 for (unsigned int i=0;i<replyVector.size();i++)
47 reply += replyVector[i];
49 if (reply.find("OK") == -1)
61 bool beginsWith(std::string a, std::string b)
63 return (a.compare(0, b.length(), b) == 0);
66 bool connect(obdLib* obd, std::string device, std::string strbaud)
68 //printf("First: %s\nSecond: %s\n",req->arg.substr(0,req->arg.find(':')).c_str(),req->arg.substr(req->arg.find(':')+1).c_str());
69 std::string port = device;
70 DebugOut() << "Obd2Source::Connect()" << device << strbaud << endl;
71 int baud = boost::lexical_cast<int>(strbaud);
73 if(obd->openPort(port.c_str(),baud) == -1)
76 ObdPid::ByteArray replyVector;
78 obd->sendObdRequestString("ATZ\r",4,&replyVector,500,3);
79 for (unsigned int i=0;i<replyVector.size();i++)
81 reply += replyVector[i];
83 if (reply.find("ELM") == -1)
87 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error resetting ELM"<<endl;
92 //printf("Reply to reset: %s\n",reply.c_str());
94 if (!sendElmCommand(obd,"ATSP0"))
96 //printf("Error sending echo\n");
97 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error setting auto protocol"<<endl;
100 if (!sendElmCommand(obd,"ATE0"))
102 //printf("Error sending echo\n");
103 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error turning off echo"<<endl;
106 if (!sendElmCommand(obd,"ATH0"))
108 //printf("Error sending headers off\n");
109 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error turning off headers"<<endl;
112 if (!sendElmCommand(obd,"ATL0"))
114 //printf("Error turning linefeeds off\n");
115 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error turning off linefeeds"<<endl;
118 obd->sendObdRequestString("010C1\r",6,&replyVector,500,5);
123 void threadLoop(gpointer data)
125 GAsyncQueue *privCommandQueue = g_async_queue_ref(((OBD2Source*)data)->commandQueue);
126 GAsyncQueue *privResponseQueue = g_async_queue_ref(((OBD2Source*)data)->responseQueue);
127 GAsyncQueue *privSingleShotQueue = g_async_queue_ref(((OBD2Source*)data)->singleShotQueue);
128 GAsyncQueue *privSubscriptionAddQueue = g_async_queue_ref(((OBD2Source*)data)->subscriptionAddQueue);
129 GAsyncQueue *privSubscriptionRemoveQueue = g_async_queue_ref(((OBD2Source*)data)->subscriptionRemoveQueue);
130 GAsyncQueue *privStatusQueue = g_async_queue_ref(((OBD2Source*)data)->statusQueue);
132 obdLib *obd = new obdLib();
133 OBD2Source *source = (OBD2Source*)data;
135 obd->setCommsCallback([](const char* mssg, void* data) { DebugOut(6)<<mssg<<endl; },NULL);
136 obd->setDebugCallback([](const char* mssg, void* data, obdLib::DebugLevel debugLevel) { DebugOut(debugLevel)<<mssg<<endl; },NULL);
138 std::list<ObdPid*> reqList;
139 std::list<ObdPid*> repeatReqList;
140 ObdPid::ByteArray replyVector;
144 bool connected=false;
146 int timeoutCount = 0;
147 while (source->m_threadLive)
149 //gpointer query = g_async_queue_pop(privCommandQueue);
152 gpointer query = g_async_queue_try_pop(privSingleShotQueue);
153 if (query != nullptr)
155 //printf("Got request!\n");
157 ObdPid *req = (ObdPid*)query;
158 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Got single shot request: " << req->pid.substr(0,req->pid.length()-1) << ":" << req->property <<endl;
159 repeatReqList.push_back(req);
161 query = g_async_queue_try_pop(privSubscriptionAddQueue);
162 if (query != nullptr)
165 ObdPid *req = (ObdPid*)query;
166 //DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Got subscription request for "<<req->req<<endl;
167 reqList.push_back(req);
169 query = g_async_queue_try_pop(privCommandQueue);
170 if (query != nullptr)
172 //ObdPid *req = (ObdPid*)query;
173 CommandRequest *req = (CommandRequest*)query;
174 //commandMap[req->req] = req->arg;
175 //printf("Command: %s\n",req->req.c_str());
176 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Command:" << req->req << endl;
177 if (req->req == "connect" )
180 if (source->m_isBluetooth)
182 ObdBluetoothDevice bt;
183 std::string tempPort = bt.getDeviceForAddress(source->m_btDeviceAddress, source->m_btAdapterAddress);
186 DebugOut(3)<<"Using bluetooth device \""<<source->m_btDeviceAddress<<"\" bound to: "<<tempPort<<endl;
192 port = req->arglist[0];
193 baud = req->arglist[1];
195 bool isconnected = connect(obd,port,baud);
199 StatusMessage *statusreq = new StatusMessage();
200 statusreq->statusStr = "connected";
201 g_async_queue_push(privStatusQueue,statusreq);
203 else if(!isconnected && connected)
205 StatusMessage *statusreq = new StatusMessage();
206 statusreq->statusStr = "disconnected";
207 g_async_queue_push(privStatusQueue,statusreq);
210 connected = isconnected;
213 else if (req->req == "connectifnot")
217 if (source->m_isBluetooth)
219 ObdBluetoothDevice bt;
220 std::string tempPort = bt.getDeviceForAddress(source->m_btDeviceAddress, source->m_btAdapterAddress);
223 DebugOut(3)<<"Using bluetooth device \""<<source->m_btDeviceAddress<<"\" bound to: "<<tempPort<<endl;
228 DebugOut(DebugOut::Error)<<"Error creating bluetooth device"<<endl;
231 bool isconnected = connect(obd,port,baud);
235 StatusMessage *statusreq = new StatusMessage();
236 statusreq->statusStr = "connected";
237 g_async_queue_push(privStatusQueue,statusreq);
239 else if(!isconnected && connected)
241 StatusMessage *statusreq = new StatusMessage();
242 statusreq->statusStr = "disconnected";
243 g_async_queue_push(privStatusQueue,statusreq);
246 connected = isconnected;
249 else if (req->req == "setportandbaud")
251 port = req->arglist[0];
252 baud = req->arglist[1];
254 else if (req->req == "disconnect")
256 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "Using queued disconnect" << (ulong)req << endl;
258 ObdBluetoothDevice bt;
259 bt.disconnect(source->m_btDeviceAddress, source->m_btAdapterAddress);
261 StatusMessage *statusreq = new StatusMessage();
262 statusreq->statusStr = "disconnected";
263 g_async_queue_push(privStatusQueue,statusreq);
267 query = g_async_queue_try_pop(privSubscriptionRemoveQueue);
268 if (query != nullptr)
270 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Got unsubscription request"<<endl;
271 ObdPid *req = (ObdPid*)query;
272 for (std::list<ObdPid*>::iterator i=reqList.begin();i!= reqList.end();i++)
274 if ((*i)->property == req->property)
279 if (reqList.size() == 0)
285 //reqList.push_back(req->req);
288 if (reqList.size() > 0 && !connected)
290 /*CommandRequest *req = new CommandRequest();
291 req->req = "connect";
292 req->arglist.push_back(port);
293 req->arglist.push_back(baud);
294 g_async_queue_push(privCommandQueue,req);
297 else if (reqList.size() == 0 && connected)
300 if (emptycount < 1000)
306 CommandRequest *req = new CommandRequest();
307 req->req = "disconnect";
308 g_async_queue_push(privCommandQueue,req);
316 for (std::list<ObdPid*>::iterator i=reqList.begin();i!= reqList.end();i++)
318 repeatReqList.push_back(*i);
321 for (std::list<ObdPid*>::iterator i=repeatReqList.begin();i!= repeatReqList.end();i++)
323 DebugOut(10) << __SMALLFILE__ << ":" << __LINE__ << "Requesting pid: " << (*i)->pid.substr(0,(*i)->pid.length()-1) << (*i)->property << endl;
324 if (source->m_blacklistPidCountMap.find((*i)->pid) != source->m_blacklistPidCountMap.end())
326 //Don't erase the pid, just skip over it.
327 int count = (*source->m_blacklistPidCountMap.find((*i)->pid)).second;
337 if(beginsWith((*i)->pid,"AT") || beginsWith((*i)->pid, "ST"))
339 result = obd->sendObdRequestString((*i)->pid.c_str(),(*i)->pid.length(),&replyVector);
341 else result = obd->sendObdRequestString((*i)->pid.c_str(),(*i)->pid.length(),&replyVector,5,3);
345 //This only happens during a error with the com port. Close it and re-open it later.
346 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Unable to send request:" << (*i)->pid.substr(0,(*i)->pid.length()-1) << endl;
347 if (obd->lastError() == obdLib::NODATA)
349 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "OBDLib::NODATA for pid" << (*i)->pid.substr(0,(*i)->pid.length()-1) << " expected property: " << (*i)->property << endl;
350 if (source->m_blacklistPidCountMap.find((*i)->pid) != source->m_blacklistPidCountMap.end())
352 //pid value i not yet in the list.
353 int count = (*source->m_blacklistPidCountMap.find((*i)->pid)).second;
358 source->m_blacklistPidCountMap.erase(source->m_blacklistPidCountMap.find((*i)->pid));
359 source->m_blacklistPidCountMap.insert(pair<std::string,int>((*i)->pid,count));
363 source->m_blacklistPidCountMap.insert(pair<std::string,int>((*i)->pid,1));
365 StatusMessage *statusreq = new StatusMessage();
366 statusreq->statusStr = "error:nodata";
367 statusreq->property = (*i)->property;
368 g_async_queue_push(privStatusQueue,statusreq);
371 else if (obd->lastError() == obdLib::TIMEOUT)
374 if (timeoutCount < 2)
376 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "OBDLib::TIMEOUT for pid" << (*i)->pid << endl;
377 StatusMessage *statusreq = new StatusMessage();
378 statusreq->statusStr = "error:timeout";
379 g_async_queue_push(privStatusQueue,statusreq);
385 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "OBD Other error:" << obd->lastError() << endl;
388 CommandRequest *req = new CommandRequest();
389 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "Queuing up a disconnect" << (ulong)req << endl;
390 req->req = "disconnect";
391 g_async_queue_push(privCommandQueue,req);
392 i = repeatReqList.end();
396 if (source->m_blacklistPidCountMap.find((*i)->pid) != source->m_blacklistPidCountMap.end())
398 //If we get the pid response, then we want to clear out the blacklist list.
399 source->m_blacklistPidCountMap.erase(source->m_blacklistPidCountMap.find((*i)->pid));
402 //ObdPid *pid = ObdPid::pidFromReply(replyVector);
403 ObdPid *pid = obd2AmbInstance->createPidFromReply(replyVector);
407 DebugOut() << "Invalid reply"<<endl;
412 DebugOut(11) << __SMALLFILE__ <<":"<< __LINE__ << "Reply recieved and queued for:" << (*i)->pid.substr(0,(*i)->pid.length()-1) << endl;
414 for (int i=0;i<replyVector.size();i++)
416 if (replyVector[i] != 13)
418 repstr += (char)replyVector[i];
420 //DebugOut(11) << replyVector[i];
422 DebugOut(11) << "Reply:" << repstr << endl;
424 g_async_queue_push(privResponseQueue,pid);
428 //We had zero non-blacklisted events. Pause for a moment here to keep from burning CPU.
431 repeatReqList.clear();
439 static int updateProperties( gpointer data)
442 OBD2Source* src = (OBD2Source*)data;
444 while (gpointer retval = g_async_queue_try_pop(src->statusQueue))
446 StatusMessage *reply = (StatusMessage*)retval;
447 if (reply->statusStr == "disconnected")
450 BasicPropertyType<bool> val(Obd2Connected,false);
451 src->updateProperty(Obd2Connected,&val);
453 else if (reply->statusStr == "connected")
455 BasicPropertyType<bool> val(Obd2Connected, true);
456 src->updateProperty(Obd2Connected,&val);
458 else if (reply->statusStr == "error:nodata" || reply->statusStr == "error:timeout")
460 if (src->propertyReplyMap.find(reply->property) != src->propertyReplyMap.end())
462 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << reply->statusStr << " on property:" << reply->property << endl;
463 src->propertyReplyMap[reply->property]->success = false;
464 src->propertyReplyMap[reply->property]->completed(src->propertyReplyMap[reply->property]);
465 src->propertyReplyMap.erase(reply->property);
469 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << reply->statusStr << " on unrequested property:" << reply->property << endl;
473 while(gpointer retval = g_async_queue_try_pop(src->responseQueue))
475 ObdPid *reply = (ObdPid*)retval;
478 AbstractPropertyType* value = VehicleProperty::getPropertyTypeForPropertyNameValue(reply->property, reply->value);
479 src->updateProperty(reply->property, value);
487 void OBD2Source::updateProperty(VehicleProperty::Property property,AbstractPropertyType* value)
491 if (propertyReplyMap.find(property) != propertyReplyMap.end())
493 propertyReplyMap[property]->value = value;
494 propertyReplyMap[property]->success = true;
495 propertyReplyMap[property]->completed(propertyReplyMap[property]);
496 propertyReplyMap.erase(property);
500 if(oldValueMap.find(property) != oldValueMap.end())
502 AbstractPropertyType* old = oldValueMap[property];
504 if((*old) == (*value))
512 oldValueMap[property] = value->copy();
514 m_re->updateProperty(property,value,uuid());
518 void OBD2Source::setSupported(PropertyList list)
520 m_supportedProperties = list;
521 m_re->updateSupported(list,PropertyList());
523 /*void OBD2Source::propertySignal(VehicleProperty::Property property,boost::any value)
526 void OBD2Source::checkProperty()
529 void OBD2Source::setConfiguration(map<string, string> config)
531 // //Config has been passed, let's start stuff up.
532 configuration = config;
535 std::string port = "/dev/ttyUSB0";
536 std::string baud = "115200";
537 std::string btadapter = "";
538 m_isBluetooth = false;
541 //printf("OBD2Source::setConfiguration\n");
542 for (map<string,string>::iterator i=configuration.begin();i!=configuration.end();i++)
544 //printf("Incoming setting: %s:%s\n",(*i).first.c_str(),(*i).second.c_str());
545 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << "Incoming setting:" << (*i).first << ":" << (*i).second << endl;
546 if ((*i).first == "device")
550 else if ((*i).first == "baud")
552 if((*i).second != "")
556 else if ((*i).first == "bluetoothAdapter")
558 btadapter = (*i).second;
562 if(port.find(":") != string::npos)
564 m_btDeviceAddress = port;
565 m_btAdapterAddress = btadapter;
566 m_isBluetooth = true;
568 DebugOut()<<"bluetooth device?"<<endl;
569 ObdBluetoothDevice bt;
571 std::string tempPort = bt.getDeviceForAddress(port, btadapter);
574 DebugOut(3)<<"Using bluetooth device \""<<port<<"\" bound to: "<<tempPort<<endl;
579 DebugOut(0)<<"Device Error"<<endl;
581 //throw std::runtime_error("Device Error");
585 //connect(obd, port, baud);
586 CommandRequest *req = new CommandRequest();
587 req->req = "setportandbaud";
588 req->arglist.push_back(port);
589 req->arglist.push_back(baud);
590 g_async_queue_push(commandQueue,req);
594 m_gThread = g_thread_new("mythread",(GThreadFunc)&threadLoop,this);
595 //g_idle_add(updateProperties, this);
596 g_timeout_add(5,updateProperties,this);
599 OBD2Source::OBD2Source(AbstractRoutingEngine *re, map<string, string> config)
600 : AbstractSource(re, config)
602 bool success = VehicleProperty::registerProperty(Obd2Connected,[](){ return new Obd2ConnectType(Obd2Connected,false); });
609 clientConnected = false;
616 for(auto itr = obd2amb.supportedPidsList.begin(); itr != obd2amb.supportedPidsList.end(); itr++)
618 m_supportedProperties.push_back((*itr)->property);
621 m_supportedProperties.push_back(Obd2Connected);
623 re->setSupported(supported(), this);
624 /*if (openPort(std::string("/dev/pts/7"),115200))
626 printf("Error opening OBD2 port\n");
628 statusQueue = g_async_queue_new();
629 commandQueue = g_async_queue_new();
630 subscriptionAddQueue = g_async_queue_new();
631 subscriptionRemoveQueue = g_async_queue_new();
632 responseQueue = g_async_queue_new();
633 singleShotQueue = g_async_queue_new();
635 setConfiguration(config);
637 OBD2Source::~OBD2Source()
639 DebugOut() << "OBD2Source Destructor called!!!"<<endl;
640 m_threadLive = false;
641 g_thread_join(m_gThread);
644 PropertyList OBD2Source::supported()
646 return m_supportedProperties;
649 int OBD2Source::supportedOperations()
654 extern "C" AbstractSource * create(AbstractRoutingEngine* routingengine, map<string, string> config)
656 return new OBD2Source(routingengine, config);
659 string OBD2Source::uuid()
661 return "f77af740-f1f8-11e1-aff1-0800200c9a66";
663 void OBD2Source::subscribeToPropertyChanges(VehicleProperty::Property property)
665 if (property == VehicleProperty::VIN)
667 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "VIN subscription requested... but there's no point!"<<endl;
669 else if (property == VehicleProperty::WMI)
671 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "WMI subscription requested... but there's no point!"<<endl;
675 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
677 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
682 ObdPid *pid = obd2AmbInstance->createPidforProperty(property);
689 //If the pid is currently in the blacklist map, erase it. This allows for applications
690 //to "un-blacklist" a pid by re-subscribing to it.
691 if (m_blacklistPidCountMap.find(pid->pid) != m_blacklistPidCountMap.end())
693 m_blacklistPidCountMap.erase(m_blacklistPidCountMap.find(pid->pid));
697 g_async_queue_push(subscriptionAddQueue,pid);
698 CommandRequest *req = new CommandRequest();
699 req->req = "connectifnot";
700 g_async_queue_push(commandQueue,req);
705 void OBD2Source::unsubscribeToPropertyChanges(VehicleProperty::Property property)
707 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
709 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
713 ObdPid *pid = obd2AmbInstance->createPidforProperty(property);
714 g_async_queue_push(subscriptionRemoveQueue,pid);
718 void OBD2Source::getPropertyAsync(AsyncPropertyReply *reply)
720 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << "getPropertyAsync requested for " << reply->property << endl;
722 VehicleProperty::Property property = reply->property;
725 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
727 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
731 propertyReplyMap[reply->property] = reply;
733 ObdPid* requ = obd2AmbInstance->createPidforProperty(property);
734 g_async_queue_push(singleShotQueue,requ);
735 CommandRequest *req = new CommandRequest();
736 req->req = "connectifnot";
737 g_async_queue_push(commandQueue,req);
740 AsyncPropertyReply *OBD2Source::setProperty(AsyncSetPropertyRequest request )
742 AsyncPropertyReply* reply = new AsyncPropertyReply (request);
746 if(request.property == Obd2Connected)
748 propertyReplyMap[reply->property] = reply;
749 reply->success = true;
751 if(request.value->value<bool>() == true)
753 CommandRequest *req = new CommandRequest();
754 req->req = "connectifnot";
755 g_async_queue_push(commandQueue,req);
759 CommandRequest *req = new CommandRequest();
760 req->req = "disconnect";
761 g_async_queue_push(commandQueue,req);
768 reply->success = false;
771 reply->completed(reply);