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)
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)
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;
231 connected = connect(obd,port,baud);
235 StatusMessage *statusreq = new StatusMessage();
236 statusreq->statusStr = "connected";
237 g_async_queue_push(privStatusQueue,statusreq);
241 StatusMessage *statusreq = new StatusMessage();
242 statusreq->statusStr = "disconnected";
243 g_async_queue_push(privStatusQueue,statusreq);
247 else if (req->req == "setportandbaud")
249 port = req->arglist[0];
250 baud = req->arglist[1];
252 else if (req->req == "disconnect")
254 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "Using queued disconnect" << (ulong)req << endl;
257 bt.disconnect(source->m_btDeviceAddress, source->m_btAdapterAddress);
259 StatusMessage *statusreq = new StatusMessage();
260 statusreq->statusStr = "disconnected";
261 g_async_queue_push(privStatusQueue,statusreq);
265 query = g_async_queue_try_pop(privSubscriptionRemoveQueue);
266 if (query != nullptr)
268 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Got unsubscription request"<<endl;
269 ObdPid *req = (ObdPid*)query;
270 for (std::list<ObdPid*>::iterator i=reqList.begin();i!= reqList.end();i++)
272 if ((*i)->property == req->property)
277 if (reqList.size() == 0)
283 //reqList.push_back(req->req);
286 if (reqList.size() > 0 && !connected)
288 /*CommandRequest *req = new CommandRequest();
289 req->req = "connect";
290 req->arglist.push_back(port);
291 req->arglist.push_back(baud);
292 g_async_queue_push(privCommandQueue,req);
295 else if (reqList.size() == 0 && connected)
298 if (emptycount < 1000)
304 CommandRequest *req = new CommandRequest();
305 req->req = "disconnect";
306 g_async_queue_push(privCommandQueue,req);
314 for (std::list<ObdPid*>::iterator i=reqList.begin();i!= reqList.end();i++)
316 repeatReqList.push_back(*i);
319 for (std::list<ObdPid*>::iterator i=repeatReqList.begin();i!= repeatReqList.end();i++)
321 DebugOut(10) << __SMALLFILE__ << ":" << __LINE__ << "Requesting pid: " << (*i)->pid.substr(0,(*i)->pid.length()-1) << (*i)->property << endl;
322 if (source->m_blacklistPidCountMap.find((*i)->pid) != source->m_blacklistPidCountMap.end())
324 //Don't erase the pid, just skip over it.
325 int count = (*source->m_blacklistPidCountMap.find((*i)->pid)).second;
335 if(beginsWith((*i)->pid,"AT") || beginsWith((*i)->pid, "ST"))
337 result = obd->sendObdRequestString((*i)->pid.c_str(),(*i)->pid.length(),&replyVector);
339 else result = obd->sendObdRequestString((*i)->pid.c_str(),(*i)->pid.length(),&replyVector,5,3);
343 //This only happens during a error with the com port. Close it and re-open it later.
344 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Unable to send request:" << (*i)->pid.substr(0,(*i)->pid.length()-1) << endl;
345 if (obd->lastError() == obdLib::NODATA)
347 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "OBDLib::NODATA for pid" << (*i)->pid.substr(0,(*i)->pid.length()-1) << " expected property: " << (*i)->property << endl;
348 if (source->m_blacklistPidCountMap.find((*i)->pid) != source->m_blacklistPidCountMap.end())
350 //pid value i not yet in the list.
351 int count = (*source->m_blacklistPidCountMap.find((*i)->pid)).second;
356 source->m_blacklistPidCountMap.erase(source->m_blacklistPidCountMap.find((*i)->pid));
357 source->m_blacklistPidCountMap.insert(pair<std::string,int>((*i)->pid,count));
361 source->m_blacklistPidCountMap.insert(pair<std::string,int>((*i)->pid,1));
363 StatusMessage *statusreq = new StatusMessage();
364 statusreq->statusStr = "error:nodata";
365 statusreq->property = (*i)->property;
366 g_async_queue_push(privStatusQueue,statusreq);
369 else if (obd->lastError() == obdLib::TIMEOUT)
372 if (timeoutCount < 2)
374 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "OBDLib::TIMEOUT for pid" << (*i)->pid << endl;
375 StatusMessage *statusreq = new StatusMessage();
376 statusreq->statusStr = "error:timeout";
377 g_async_queue_push(privStatusQueue,statusreq);
383 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "OBD Other error:" << obd->lastError() << endl;
386 CommandRequest *req = new CommandRequest();
387 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "Queuing up a disconnect" << (ulong)req << endl;
388 req->req = "disconnect";
389 g_async_queue_push(privCommandQueue,req);
390 i = repeatReqList.end();
394 if (source->m_blacklistPidCountMap.find((*i)->pid) != source->m_blacklistPidCountMap.end())
396 //If we get the pid response, then we want to clear out the blacklist list.
397 source->m_blacklistPidCountMap.erase(source->m_blacklistPidCountMap.find((*i)->pid));
400 //ObdPid *pid = ObdPid::pidFromReply(replyVector);
401 ObdPid *pid = obd2AmbInstance->createPidFromReply(replyVector);
405 DebugOut() << "Invalid reply"<<endl;
410 DebugOut(11) << __SMALLFILE__ <<":"<< __LINE__ << "Reply recieved and queued for:" << (*i)->pid.substr(0,(*i)->pid.length()-1) << endl;
412 for (int i=0;i<replyVector.size();i++)
414 if (replyVector[i] != 13)
416 repstr += (char)replyVector[i];
418 //DebugOut(11) << replyVector[i];
420 DebugOut(11) << "Reply:" << repstr << endl;
422 g_async_queue_push(privResponseQueue,pid);
426 //We had zero non-blacklisted events. Pause for a moment here to keep from burning CPU.
429 repeatReqList.clear();
437 static int updateProperties( gpointer data)
440 OBD2Source* src = (OBD2Source*)data;
442 while (gpointer retval = g_async_queue_try_pop(src->statusQueue))
444 StatusMessage *reply = (StatusMessage*)retval;
445 if (reply->statusStr == "disconnected")
447 OBD2Source::Obd2ConnectType val(Obd2Connected,false);
448 src->updateProperty(Obd2Connected,&val);
450 else if (reply->statusStr == "connected")
452 OBD2Source::Obd2ConnectType val(Obd2Connected, true);
453 src->updateProperty(Obd2Connected,&val);
455 else if (reply->statusStr == "error:nodata" || reply->statusStr == "error:timeout")
457 if (src->propertyReplyMap.find(reply->property) != src->propertyReplyMap.end())
459 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << reply->statusStr << " on property:" << reply->property << endl;
460 src->propertyReplyMap[reply->property]->success = false;
461 src->propertyReplyMap[reply->property]->error = AsyncPropertyReply::InvalidOperation;
462 src->propertyReplyMap[reply->property]->completed(src->propertyReplyMap[reply->property]);
463 src->propertyReplyMap.erase(reply->property);
465 /// Remove support for this pid:
466 PropertyList list = src->supported();
467 list.remove(reply->property);
468 src->setSupported(list);
472 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << reply->statusStr << " on unrequested property:" << reply->property << endl;
477 while(gpointer retval = g_async_queue_try_pop(src->responseQueue))
479 ObdPid *reply = (ObdPid*)retval;
482 AbstractPropertyType* value = VehicleProperty::getPropertyTypeForPropertyNameValue(reply->property, reply->value);
483 src->updateProperty(reply->property, value);
490 void OBD2Source::updateProperty(VehicleProperty::Property property,AbstractPropertyType* value)
492 if(property == Obd2Connected)
493 obd2Connected.setValue(value->anyValue());
495 if (propertyReplyMap.find(property) != propertyReplyMap.end())
497 propertyReplyMap[property]->value = value;
498 propertyReplyMap[property]->success = true;
500 propertyReplyMap[property]->completed(propertyReplyMap[property]);
503 DebugOut(DebugOut::Error)<<"failed to call reply completed callback"<<endl;
506 propertyReplyMap.erase(property);
510 if(oldValueMap.find(property) != oldValueMap.end())
512 AbstractPropertyType* old = oldValueMap[property];
514 if((*old) == (*value))
522 oldValueMap[property] = value->copy();
524 m_re->updateProperty(value,uuid());
528 void OBD2Source::setSupported(PropertyList list)
530 m_supportedProperties = list;
531 m_re->updateSupported(list,PropertyList(),this);
533 /*void OBD2Source::propertySignal(VehicleProperty::Property property,boost::any value)
536 void OBD2Source::checkProperty()
539 void OBD2Source::setConfiguration(map<string, string> config)
541 // //Config has been passed, let's start stuff up.
542 configuration = config;
545 std::string port = "/dev/ttyUSB0";
546 std::string baud = "115200";
547 std::string btadapter = "";
548 m_isBluetooth = false;
551 //printf("OBD2Source::setConfiguration\n");
552 for (map<string,string>::iterator i=configuration.begin();i!=configuration.end();i++)
554 //printf("Incoming setting: %s:%s\n",(*i).first.c_str(),(*i).second.c_str());
555 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << "Incoming setting:" << (*i).first << ":" << (*i).second << endl;
556 if ((*i).first == "device")
560 else if ((*i).first == "baud")
562 if((*i).second != "")
566 else if ((*i).first == "bluetoothAdapter")
568 btadapter = (*i).second;
572 if(port.find(":") != string::npos)
574 m_btDeviceAddress = port;
575 m_btAdapterAddress = btadapter;
576 m_isBluetooth = true;
578 /*DebugOut()<<"bluetooth device?"<<endl;
581 std::string tempPort = bt.getDeviceForAddress(port, btadapter);
584 DebugOut(3)<<"Using bluetooth device \""<<port<<"\" bound to: "<<tempPort<<endl;
589 DebugOut(0)<<"Device Error"<<endl;
591 //throw std::runtime_error("Device Error");
595 //connect(obd, port, baud);
596 CommandRequest *req = new CommandRequest();
597 req->req = "setportandbaud";
598 req->arglist.push_back(port);
599 req->arglist.push_back(baud);
600 g_async_queue_push(commandQueue,req);
604 m_gThread = g_thread_new("mythread",(GThreadFunc)&threadLoop,this);
605 //g_idle_add(updateProperties, this);
606 g_timeout_add(5,updateProperties,this);
609 OBD2Source::OBD2Source(AbstractRoutingEngine *re, map<string, string> config)
610 : AbstractSource(re, config),obd2Connected(Obd2Connected,false)
612 bool success = VehicleProperty::registerProperty(Obd2Connected,[](){ return new Obd2ConnectType(Obd2Connected,false); });
619 clientConnected = false;
626 for(auto itr = obd2amb.supportedPidsList.begin(); itr != obd2amb.supportedPidsList.end(); itr++)
628 m_supportedProperties.push_back((*itr)->property);
631 m_supportedProperties.push_back(Obd2Connected);
633 re->setSupported(supported(), this);
634 /*if (openPort(std::string("/dev/pts/7"),115200))
636 printf("Error opening OBD2 port\n");
638 statusQueue = g_async_queue_new();
639 commandQueue = g_async_queue_new();
640 subscriptionAddQueue = g_async_queue_new();
641 subscriptionRemoveQueue = g_async_queue_new();
642 responseQueue = g_async_queue_new();
643 singleShotQueue = g_async_queue_new();
645 setConfiguration(config);
647 OBD2Source::~OBD2Source()
649 DebugOut() << "OBD2Source Destructor called!!!"<<endl;
650 m_threadLive = false;
651 g_thread_join(m_gThread);
654 PropertyList OBD2Source::supported()
656 return m_supportedProperties;
659 int OBD2Source::supportedOperations()
664 extern "C" AbstractSource * create(AbstractRoutingEngine* routingengine, map<string, string> config)
666 return new OBD2Source(routingengine, config);
669 const string OBD2Source::uuid()
671 return "f77af740-f1f8-11e1-aff1-0800200c9a66";
673 void OBD2Source::subscribeToPropertyChanges(VehicleProperty::Property property)
675 if (property == VehicleProperty::VIN)
677 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "VIN subscription requested... but there's no point!"<<endl;
679 else if (property == VehicleProperty::WMI)
681 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "WMI subscription requested... but there's no point!"<<endl;
685 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
687 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
692 ObdPid *pid = obd2AmbInstance->createPidforProperty(property);
699 //If the pid is currently in the blacklist map, erase it. This allows for applications
700 //to "un-blacklist" a pid by re-subscribing to it.
701 if (m_blacklistPidCountMap.find(pid->pid) != m_blacklistPidCountMap.end())
703 m_blacklistPidCountMap.erase(m_blacklistPidCountMap.find(pid->pid));
707 g_async_queue_push(subscriptionAddQueue,pid);
708 CommandRequest *req = new CommandRequest();
709 req->req = "connectifnot";
710 g_async_queue_push(commandQueue,req);
715 void OBD2Source::unsubscribeToPropertyChanges(VehicleProperty::Property property)
717 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
719 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
723 ObdPid *pid = obd2AmbInstance->createPidforProperty(property);
724 g_async_queue_push(subscriptionRemoveQueue,pid);
728 void OBD2Source::getPropertyAsync(AsyncPropertyReply *reply)
730 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << "getPropertyAsync requested for " << reply->property << endl;
732 VehicleProperty::Property property = reply->property;
735 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
737 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
741 if(reply->property == Obd2Connected)
743 reply->success = true;
744 reply->value = &obd2Connected;
745 reply->completed(reply);
749 propertyReplyMap[reply->property] = reply;
751 ObdPid* requ = obd2AmbInstance->createPidforProperty(property);
752 g_async_queue_push(singleShotQueue,requ);
753 CommandRequest *req = new CommandRequest();
754 req->req = "connectifnot";
755 g_async_queue_push(commandQueue,req);
758 AsyncPropertyReply *OBD2Source::setProperty(AsyncSetPropertyRequest request )
760 AsyncPropertyReply* reply = new AsyncPropertyReply (request);
764 if(request.property == Obd2Connected)
766 propertyReplyMap[reply->property] = reply;
767 reply->success = true;
769 if(request.value->value<bool>() == true)
771 CommandRequest *req = new CommandRequest();
772 req->req = "connectifnot";
773 g_async_queue_push(commandQueue,req);
777 CommandRequest *req = new CommandRequest();
778 req->req = "disconnect";
779 g_async_queue_push(commandQueue,req);
786 reply->success = false;
789 reply->completed(reply);