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 connected = connect(obd,port,baud);
199 StatusMessage *statusreq = new StatusMessage();
200 statusreq->statusStr = "connected";
201 g_async_queue_push(privStatusQueue,statusreq);
205 StatusMessage *statusreq = new StatusMessage();
206 statusreq->statusStr = "disconnected";
207 g_async_queue_push(privStatusQueue,statusreq);
211 else if (req->req == "connectifnot")
215 if (source->m_isBluetooth)
217 ObdBluetoothDevice bt;
218 std::string tempPort = bt.getDeviceForAddress(source->m_btDeviceAddress, source->m_btAdapterAddress);
221 DebugOut(3)<<"Using bluetooth device \""<<source->m_btDeviceAddress<<"\" bound to: "<<tempPort<<endl;
226 DebugOut(DebugOut::Error)<<"Error creating bluetooth device"<<endl;
229 connected = connect(obd,port,baud);
233 StatusMessage *statusreq = new StatusMessage();
234 statusreq->statusStr = "connected";
235 g_async_queue_push(privStatusQueue,statusreq);
239 StatusMessage *statusreq = new StatusMessage();
240 statusreq->statusStr = "disconnected";
241 g_async_queue_push(privStatusQueue,statusreq);
245 else if (req->req == "setportandbaud")
247 port = req->arglist[0];
248 baud = req->arglist[1];
250 else if (req->req == "disconnect")
252 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "Using queued disconnect" << (ulong)req << endl;
254 ObdBluetoothDevice bt;
255 bt.disconnect(source->m_btDeviceAddress, source->m_btAdapterAddress);
257 StatusMessage *statusreq = new StatusMessage();
258 statusreq->statusStr = "disconnected";
259 g_async_queue_push(privStatusQueue,statusreq);
263 query = g_async_queue_try_pop(privSubscriptionRemoveQueue);
264 if (query != nullptr)
266 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Got unsubscription request"<<endl;
267 ObdPid *req = (ObdPid*)query;
268 for (std::list<ObdPid*>::iterator i=reqList.begin();i!= reqList.end();i++)
270 if ((*i)->property == req->property)
275 if (reqList.size() == 0)
281 //reqList.push_back(req->req);
284 if (reqList.size() > 0 && !connected)
286 /*CommandRequest *req = new CommandRequest();
287 req->req = "connect";
288 req->arglist.push_back(port);
289 req->arglist.push_back(baud);
290 g_async_queue_push(privCommandQueue,req);
293 else if (reqList.size() == 0 && connected)
296 if (emptycount < 1000)
302 CommandRequest *req = new CommandRequest();
303 req->req = "disconnect";
304 g_async_queue_push(privCommandQueue,req);
312 for (std::list<ObdPid*>::iterator i=reqList.begin();i!= reqList.end();i++)
314 repeatReqList.push_back(*i);
317 for (std::list<ObdPid*>::iterator i=repeatReqList.begin();i!= repeatReqList.end();i++)
319 DebugOut(10) << __SMALLFILE__ << ":" << __LINE__ << "Requesting pid: " << (*i)->pid.substr(0,(*i)->pid.length()-1) << (*i)->property << endl;
320 if (source->m_blacklistPidCountMap.find((*i)->pid) != source->m_blacklistPidCountMap.end())
322 //Don't erase the pid, just skip over it.
323 int count = (*source->m_blacklistPidCountMap.find((*i)->pid)).second;
333 if(beginsWith((*i)->pid,"AT") || beginsWith((*i)->pid, "ST"))
335 result = obd->sendObdRequestString((*i)->pid.c_str(),(*i)->pid.length(),&replyVector);
337 else result = obd->sendObdRequestString((*i)->pid.c_str(),(*i)->pid.length(),&replyVector,5,3);
341 //This only happens during a error with the com port. Close it and re-open it later.
342 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Unable to send request:" << (*i)->pid.substr(0,(*i)->pid.length()-1) << endl;
343 if (obd->lastError() == obdLib::NODATA)
345 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "OBDLib::NODATA for pid" << (*i)->pid.substr(0,(*i)->pid.length()-1) << " expected property: " << (*i)->property << endl;
346 if (source->m_blacklistPidCountMap.find((*i)->pid) != source->m_blacklistPidCountMap.end())
348 //pid value i not yet in the list.
349 int count = (*source->m_blacklistPidCountMap.find((*i)->pid)).second;
354 source->m_blacklistPidCountMap.erase(source->m_blacklistPidCountMap.find((*i)->pid));
355 source->m_blacklistPidCountMap.insert(pair<std::string,int>((*i)->pid,count));
359 source->m_blacklistPidCountMap.insert(pair<std::string,int>((*i)->pid,1));
361 StatusMessage *statusreq = new StatusMessage();
362 statusreq->statusStr = "error:nodata";
363 statusreq->property = (*i)->property;
364 g_async_queue_push(privStatusQueue,statusreq);
367 else if (obd->lastError() == obdLib::TIMEOUT)
370 if (timeoutCount < 2)
372 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "OBDLib::TIMEOUT for pid" << (*i)->pid << endl;
373 StatusMessage *statusreq = new StatusMessage();
374 statusreq->statusStr = "error:timeout";
375 g_async_queue_push(privStatusQueue,statusreq);
381 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "OBD Other error:" << obd->lastError() << endl;
384 CommandRequest *req = new CommandRequest();
385 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "Queuing up a disconnect" << (ulong)req << endl;
386 req->req = "disconnect";
387 g_async_queue_push(privCommandQueue,req);
388 i = repeatReqList.end();
392 if (source->m_blacklistPidCountMap.find((*i)->pid) != source->m_blacklistPidCountMap.end())
394 //If we get the pid response, then we want to clear out the blacklist list.
395 source->m_blacklistPidCountMap.erase(source->m_blacklistPidCountMap.find((*i)->pid));
398 //ObdPid *pid = ObdPid::pidFromReply(replyVector);
399 ObdPid *pid = obd2AmbInstance->createPidFromReply(replyVector);
403 DebugOut() << "Invalid reply"<<endl;
408 DebugOut(11) << __SMALLFILE__ <<":"<< __LINE__ << "Reply recieved and queued for:" << (*i)->pid.substr(0,(*i)->pid.length()-1) << endl;
410 for (int i=0;i<replyVector.size();i++)
412 if (replyVector[i] != 13)
414 repstr += (char)replyVector[i];
416 //DebugOut(11) << replyVector[i];
418 DebugOut(11) << "Reply:" << repstr << endl;
420 g_async_queue_push(privResponseQueue,pid);
424 //We had zero non-blacklisted events. Pause for a moment here to keep from burning CPU.
427 repeatReqList.clear();
435 static int updateProperties( gpointer data)
438 OBD2Source* src = (OBD2Source*)data;
440 while (gpointer retval = g_async_queue_try_pop(src->statusQueue))
442 StatusMessage *reply = (StatusMessage*)retval;
443 if (reply->statusStr == "disconnected")
445 OBD2Source::Obd2ConnectType val(Obd2Connected,false);
446 src->updateProperty(Obd2Connected,&val);
448 else if (reply->statusStr == "connected")
450 OBD2Source::Obd2ConnectType val(Obd2Connected, true);
451 src->updateProperty(Obd2Connected,&val);
453 else if (reply->statusStr == "error:nodata" || reply->statusStr == "error:timeout")
455 if (src->propertyReplyMap.find(reply->property) != src->propertyReplyMap.end())
457 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << reply->statusStr << " on property:" << reply->property << endl;
458 src->propertyReplyMap[reply->property]->success = false;
459 src->propertyReplyMap[reply->property]->completed(src->propertyReplyMap[reply->property]);
460 src->propertyReplyMap.erase(reply->property);
464 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << reply->statusStr << " on unrequested property:" << reply->property << endl;
469 while(gpointer retval = g_async_queue_try_pop(src->responseQueue))
471 ObdPid *reply = (ObdPid*)retval;
474 AbstractPropertyType* value = VehicleProperty::getPropertyTypeForPropertyNameValue(reply->property, reply->value);
475 src->updateProperty(reply->property, value);
483 void OBD2Source::updateProperty(VehicleProperty::Property property,AbstractPropertyType* value)
487 if (propertyReplyMap.find(property) != propertyReplyMap.end())
489 propertyReplyMap[property]->value = value;
490 propertyReplyMap[property]->success = true;
492 propertyReplyMap[property]->completed(propertyReplyMap[property]);
495 DebugOut(DebugOut::Error)<<"failed to call reply completed callback"<<endl;
498 propertyReplyMap.erase(property);
502 if(oldValueMap.find(property) != oldValueMap.end())
504 AbstractPropertyType* old = oldValueMap[property];
506 if((*old) == (*value))
514 oldValueMap[property] = value->copy();
516 m_re->updateProperty(property,value,uuid());
520 void OBD2Source::setSupported(PropertyList list)
522 m_supportedProperties = list;
523 m_re->updateSupported(list,PropertyList());
525 /*void OBD2Source::propertySignal(VehicleProperty::Property property,boost::any value)
528 void OBD2Source::checkProperty()
531 void OBD2Source::setConfiguration(map<string, string> config)
533 // //Config has been passed, let's start stuff up.
534 configuration = config;
537 std::string port = "/dev/ttyUSB0";
538 std::string baud = "115200";
539 std::string btadapter = "";
540 m_isBluetooth = false;
543 //printf("OBD2Source::setConfiguration\n");
544 for (map<string,string>::iterator i=configuration.begin();i!=configuration.end();i++)
546 //printf("Incoming setting: %s:%s\n",(*i).first.c_str(),(*i).second.c_str());
547 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << "Incoming setting:" << (*i).first << ":" << (*i).second << endl;
548 if ((*i).first == "device")
552 else if ((*i).first == "baud")
554 if((*i).second != "")
558 else if ((*i).first == "bluetoothAdapter")
560 btadapter = (*i).second;
564 if(port.find(":") != string::npos)
566 m_btDeviceAddress = port;
567 m_btAdapterAddress = btadapter;
568 m_isBluetooth = true;
570 DebugOut()<<"bluetooth device?"<<endl;
571 ObdBluetoothDevice bt;
573 std::string tempPort = bt.getDeviceForAddress(port, btadapter);
576 DebugOut(3)<<"Using bluetooth device \""<<port<<"\" bound to: "<<tempPort<<endl;
581 DebugOut(0)<<"Device Error"<<endl;
583 //throw std::runtime_error("Device Error");
587 //connect(obd, port, baud);
588 CommandRequest *req = new CommandRequest();
589 req->req = "setportandbaud";
590 req->arglist.push_back(port);
591 req->arglist.push_back(baud);
592 g_async_queue_push(commandQueue,req);
596 m_gThread = g_thread_new("mythread",(GThreadFunc)&threadLoop,this);
597 //g_idle_add(updateProperties, this);
598 g_timeout_add(5,updateProperties,this);
601 OBD2Source::OBD2Source(AbstractRoutingEngine *re, map<string, string> config)
602 : AbstractSource(re, config)
604 bool success = VehicleProperty::registerProperty(Obd2Connected,[](){ return new Obd2ConnectType(Obd2Connected,false); });
611 clientConnected = false;
618 for(auto itr = obd2amb.supportedPidsList.begin(); itr != obd2amb.supportedPidsList.end(); itr++)
620 m_supportedProperties.push_back((*itr)->property);
623 m_supportedProperties.push_back(Obd2Connected);
625 re->setSupported(supported(), this);
626 /*if (openPort(std::string("/dev/pts/7"),115200))
628 printf("Error opening OBD2 port\n");
630 statusQueue = g_async_queue_new();
631 commandQueue = g_async_queue_new();
632 subscriptionAddQueue = g_async_queue_new();
633 subscriptionRemoveQueue = g_async_queue_new();
634 responseQueue = g_async_queue_new();
635 singleShotQueue = g_async_queue_new();
637 setConfiguration(config);
639 OBD2Source::~OBD2Source()
641 DebugOut() << "OBD2Source Destructor called!!!"<<endl;
642 m_threadLive = false;
643 g_thread_join(m_gThread);
646 PropertyList OBD2Source::supported()
648 return m_supportedProperties;
651 int OBD2Source::supportedOperations()
656 extern "C" AbstractSource * create(AbstractRoutingEngine* routingengine, map<string, string> config)
658 return new OBD2Source(routingengine, config);
661 string OBD2Source::uuid()
663 return "f77af740-f1f8-11e1-aff1-0800200c9a66";
665 void OBD2Source::subscribeToPropertyChanges(VehicleProperty::Property property)
667 if (property == VehicleProperty::VIN)
669 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "VIN subscription requested... but there's no point!"<<endl;
671 else if (property == VehicleProperty::WMI)
673 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "WMI subscription requested... but there's no point!"<<endl;
677 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
679 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
684 ObdPid *pid = obd2AmbInstance->createPidforProperty(property);
691 //If the pid is currently in the blacklist map, erase it. This allows for applications
692 //to "un-blacklist" a pid by re-subscribing to it.
693 if (m_blacklistPidCountMap.find(pid->pid) != m_blacklistPidCountMap.end())
695 m_blacklistPidCountMap.erase(m_blacklistPidCountMap.find(pid->pid));
699 g_async_queue_push(subscriptionAddQueue,pid);
700 CommandRequest *req = new CommandRequest();
701 req->req = "connectifnot";
702 g_async_queue_push(commandQueue,req);
707 void OBD2Source::unsubscribeToPropertyChanges(VehicleProperty::Property property)
709 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
711 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
715 ObdPid *pid = obd2AmbInstance->createPidforProperty(property);
716 g_async_queue_push(subscriptionRemoveQueue,pid);
720 void OBD2Source::getPropertyAsync(AsyncPropertyReply *reply)
722 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << "getPropertyAsync requested for " << reply->property << endl;
724 VehicleProperty::Property property = reply->property;
727 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
729 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
733 propertyReplyMap[reply->property] = reply;
735 ObdPid* requ = obd2AmbInstance->createPidforProperty(property);
736 g_async_queue_push(singleShotQueue,requ);
737 CommandRequest *req = new CommandRequest();
738 req->req = "connectifnot";
739 g_async_queue_push(commandQueue,req);
742 AsyncPropertyReply *OBD2Source::setProperty(AsyncSetPropertyRequest request )
744 AsyncPropertyReply* reply = new AsyncPropertyReply (request);
748 if(request.property == Obd2Connected)
750 propertyReplyMap[reply->property] = reply;
751 reply->success = true;
753 if(request.value->value<bool>() == true)
755 CommandRequest *req = new CommandRequest();
756 req->req = "connectifnot";
757 g_async_queue_push(commandQueue,req);
761 CommandRequest *req = new CommandRequest();
762 req->req = "disconnect";
763 g_async_queue_push(commandQueue,req);
770 reply->success = false;
773 reply->completed(reply);