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;
91 //printf("Reply to reset: %s\n",reply.c_str());
93 if (!sendElmCommand(obd,"ATSP0"))
95 //printf("Error sending echo\n");
96 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error setting auto protocol"<<endl;
98 if (!sendElmCommand(obd,"ATE0"))
100 //printf("Error sending echo\n");
101 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error turning off echo"<<endl;
103 if (!sendElmCommand(obd,"ATH0"))
105 //printf("Error sending headers off\n");
106 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error turning off headers"<<endl;
108 if (!sendElmCommand(obd,"ATL0"))
110 //printf("Error turning linefeeds off\n");
111 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Error turning off linefeeds"<<endl;
113 obd->sendObdRequestString("010C1\r",6,&replyVector,500,5);
116 void threadLoop(gpointer data)
118 GAsyncQueue *privCommandQueue = g_async_queue_ref(((OBD2Source*)data)->commandQueue);
119 GAsyncQueue *privResponseQueue = g_async_queue_ref(((OBD2Source*)data)->responseQueue);
120 GAsyncQueue *privSingleShotQueue = g_async_queue_ref(((OBD2Source*)data)->singleShotQueue);
121 GAsyncQueue *privSubscriptionAddQueue = g_async_queue_ref(((OBD2Source*)data)->subscriptionAddQueue);
122 GAsyncQueue *privSubscriptionRemoveQueue = g_async_queue_ref(((OBD2Source*)data)->subscriptionRemoveQueue);
123 GAsyncQueue *privStatusQueue = g_async_queue_ref(((OBD2Source*)data)->statusQueue);
125 obdLib *obd = new obdLib();
126 OBD2Source *source = (OBD2Source*)data;
128 obd->setCommsCallback([](const char* mssg, void* data) { DebugOut(6)<<mssg<<endl; },NULL);
129 obd->setDebugCallback([](const char* mssg, void* data, obdLib::DebugLevel debugLevel) { DebugOut(debugLevel)<<mssg<<endl; },NULL);
131 std::list<ObdPid*> reqList;
132 std::list<ObdPid*> repeatReqList;
133 ObdPid::ByteArray replyVector;
137 bool connected=false;
139 int timeoutCount = 0;
140 while (source->m_threadLive)
142 //gpointer query = g_async_queue_pop(privCommandQueue);
145 gpointer query = g_async_queue_try_pop(privSingleShotQueue);
146 if (query != nullptr)
148 //printf("Got request!\n");
150 ObdPid *req = (ObdPid*)query;
151 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Got single shot request: " << req->pid.substr(0,req->pid.length()-1) << ":" << req->property <<endl;
152 repeatReqList.push_back(req);
154 query = g_async_queue_try_pop(privSubscriptionAddQueue);
155 if (query != nullptr)
158 ObdPid *req = (ObdPid*)query;
159 //DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Got subscription request for "<<req->req<<endl;
160 reqList.push_back(req);
162 query = g_async_queue_try_pop(privCommandQueue);
163 if (query != nullptr)
165 //ObdPid *req = (ObdPid*)query;
166 CommandRequest *req = (CommandRequest*)query;
167 //commandMap[req->req] = req->arg;
168 //printf("Command: %s\n",req->req.c_str());
169 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Command:" << req->req << endl;
170 if (req->req == "connect")
173 if (source->m_isBluetooth)
175 ObdBluetoothDevice bt;
176 std::string tempPort = bt.getDeviceForAddress(source->m_btDeviceAddress, source->m_btAdapterAddress);
179 DebugOut(3)<<"Using bluetooth device \""<<source->m_btDeviceAddress<<"\" bound to: "<<tempPort<<endl;
185 port = req->arglist[0];
186 baud = req->arglist[1];
188 connected = connect(obd,port,baud);
192 StatusMessage *statusreq = new StatusMessage();
193 statusreq->statusStr = "connected";
194 g_async_queue_push(privStatusQueue,statusreq);
198 StatusMessage *statusreq = new StatusMessage();
199 statusreq->statusStr = "disconnected";
200 g_async_queue_push(privStatusQueue,statusreq);
204 else if (req->req == "connectifnot")
208 if (source->m_isBluetooth)
210 ObdBluetoothDevice bt;
211 std::string tempPort = bt.getDeviceForAddress(source->m_btDeviceAddress, source->m_btAdapterAddress);
214 DebugOut(3)<<"Using bluetooth device \""<<source->m_btDeviceAddress<<"\" bound to: "<<tempPort<<endl;
218 connected = connect(obd,port,baud);
222 StatusMessage *statusreq = new StatusMessage();
223 statusreq->statusStr = "connected";
224 g_async_queue_push(privStatusQueue,statusreq);
228 StatusMessage *statusreq = new StatusMessage();
229 statusreq->statusStr = "disconnected";
230 g_async_queue_push(privStatusQueue,statusreq);
234 else if (req->req == "setportandbaud")
236 port = req->arglist[0];
237 baud = req->arglist[1];
239 else if (req->req == "disconnect")
241 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "Using queued disconnect" << (ulong)req << endl;
243 ObdBluetoothDevice bt;
244 bt.disconnect(source->m_btDeviceAddress, source->m_btAdapterAddress);
246 StatusMessage *statusreq = new StatusMessage();
247 statusreq->statusStr = "disconnected";
248 g_async_queue_push(privStatusQueue,statusreq);
252 query = g_async_queue_try_pop(privSubscriptionRemoveQueue);
253 if (query != nullptr)
255 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Got unsubscription request"<<endl;
256 ObdPid *req = (ObdPid*)query;
257 for (std::list<ObdPid*>::iterator i=reqList.begin();i!= reqList.end();i++)
259 if ((*i)->property == req->property)
264 if (reqList.size() == 0)
270 //reqList.push_back(req->req);
273 if (reqList.size() > 0 && !connected)
275 /*CommandRequest *req = new CommandRequest();
276 req->req = "connect";
277 req->arglist.push_back(port);
278 req->arglist.push_back(baud);
279 g_async_queue_push(privCommandQueue,req);
282 else if (reqList.size() == 0 && connected)
285 if (emptycount < 1000)
291 CommandRequest *req = new CommandRequest();
292 req->req = "disconnect";
293 g_async_queue_push(privCommandQueue,req);
301 for (std::list<ObdPid*>::iterator i=reqList.begin();i!= reqList.end();i++)
303 repeatReqList.push_back(*i);
306 for (std::list<ObdPid*>::iterator i=repeatReqList.begin();i!= repeatReqList.end();i++)
308 DebugOut(10) << __SMALLFILE__ << ":" << __LINE__ << "Requesting pid: " << (*i)->pid.substr(0,(*i)->pid.length()-1) << (*i)->property << endl;
309 if (source->m_blacklistPidCountMap.find((*i)->pid) != source->m_blacklistPidCountMap.end())
311 //Don't erase the pid, just skip over it.
312 int count = (*source->m_blacklistPidCountMap.find((*i)->pid)).second;
322 if(beginsWith((*i)->pid,"AT") || beginsWith((*i)->pid, "ST"))
324 result = obd->sendObdRequestString((*i)->pid.c_str(),(*i)->pid.length(),&replyVector);
326 else result = obd->sendObdRequestString((*i)->pid.c_str(),(*i)->pid.length(),&replyVector,5,3);
330 //This only happens during a error with the com port. Close it and re-open it later.
331 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Unable to send request:" << (*i)->pid.substr(0,(*i)->pid.length()-1) << endl;
332 if (obd->lastError() == obdLib::NODATA)
334 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "OBDLib::NODATA for pid" << (*i)->pid.substr(0,(*i)->pid.length()-1) << " expected property: " << (*i)->property << endl;
335 if (source->m_blacklistPidCountMap.find((*i)->pid) != source->m_blacklistPidCountMap.end())
337 //pid value i not yet in the list.
338 int count = (*source->m_blacklistPidCountMap.find((*i)->pid)).second;
343 source->m_blacklistPidCountMap.erase(source->m_blacklistPidCountMap.find((*i)->pid));
344 source->m_blacklistPidCountMap.insert(pair<std::string,int>((*i)->pid,count));
348 source->m_blacklistPidCountMap.insert(pair<std::string,int>((*i)->pid,1));
350 StatusMessage *statusreq = new StatusMessage();
351 statusreq->statusStr = "error:nodata";
352 statusreq->property = (*i)->property;
353 g_async_queue_push(privStatusQueue,statusreq);
356 else if (obd->lastError() == obdLib::TIMEOUT)
359 if (timeoutCount < 2)
361 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "OBDLib::TIMEOUT for pid" << (*i)->pid << endl;
362 StatusMessage *statusreq = new StatusMessage();
363 statusreq->statusStr = "error:timeout";
364 g_async_queue_push(privStatusQueue,statusreq);
370 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "OBD Other error:" << obd->lastError() << endl;
373 CommandRequest *req = new CommandRequest();
374 DebugOut() << __SMALLFILE__ << ":" << __LINE__ << "Queuing up a disconnect" << (ulong)req << endl;
375 req->req = "disconnect";
376 g_async_queue_push(privCommandQueue,req);
377 i = repeatReqList.end();
381 if (source->m_blacklistPidCountMap.find((*i)->pid) != source->m_blacklistPidCountMap.end())
383 //If we get the pid response, then we want to clear out the blacklist list.
384 source->m_blacklistPidCountMap.erase(source->m_blacklistPidCountMap.find((*i)->pid));
387 //ObdPid *pid = ObdPid::pidFromReply(replyVector);
388 ObdPid *pid = obd2AmbInstance->createPidFromReply(replyVector);
392 DebugOut() << "Invalid reply"<<endl;
397 DebugOut(11) << __SMALLFILE__ <<":"<< __LINE__ << "Reply recieved and queued for:" << (*i)->pid.substr(0,(*i)->pid.length()-1) << endl;
399 for (int i=0;i<replyVector.size();i++)
401 if (replyVector[i] != 13)
403 repstr += (char)replyVector[i];
405 //DebugOut(11) << replyVector[i];
407 DebugOut(11) << "Reply:" << repstr << endl;
409 g_async_queue_push(privResponseQueue,pid);
413 //We had zero non-blacklisted events. Pause for a moment here to keep from burning CPU.
416 repeatReqList.clear();
424 static int updateProperties( gpointer data)
427 OBD2Source* src = (OBD2Source*)data;
429 while (gpointer retval = g_async_queue_try_pop(src->statusQueue))
431 StatusMessage *reply = (StatusMessage*)retval;
432 if (reply->statusStr == "disconnected")
435 BasicPropertyType<bool> val(Obd2Connected,false);
436 src->updateProperty(Obd2Connected,&val);
438 else if (reply->statusStr == "connected")
440 BasicPropertyType<bool> val(Obd2Connected, true);
441 src->updateProperty(Obd2Connected,&val);
443 else if (reply->statusStr == "error:nodata" || reply->statusStr == "error:timeout")
445 if (src->propertyReplyMap.find(reply->property) != src->propertyReplyMap.end())
447 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << reply->statusStr << " on property:" << reply->property << endl;
448 src->propertyReplyMap[reply->property]->success = false;
449 src->propertyReplyMap[reply->property]->completed(src->propertyReplyMap[reply->property]);
450 src->propertyReplyMap.erase(reply->property);
454 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << reply->statusStr << " on unrequested property:" << reply->property << endl;
458 while(gpointer retval = g_async_queue_try_pop(src->responseQueue))
460 ObdPid *reply = (ObdPid*)retval;
463 AbstractPropertyType* value = VehicleProperty::getPropertyTypeForPropertyNameValue(reply->property, reply->value);
464 src->updateProperty(reply->property, value);
472 void OBD2Source::updateProperty(VehicleProperty::Property property,AbstractPropertyType* value)
476 if (propertyReplyMap.find(property) != propertyReplyMap.end())
478 propertyReplyMap[property]->value = value;
479 propertyReplyMap[property]->success = true;
480 propertyReplyMap[property]->completed(propertyReplyMap[property]);
481 propertyReplyMap.erase(property);
485 if(oldValueMap.find(property) != oldValueMap.end())
487 AbstractPropertyType* old = oldValueMap[property];
489 if((*old) == (*value))
497 oldValueMap[property] = value->copy();
499 m_re->updateProperty(property,value,uuid());
503 void OBD2Source::setSupported(PropertyList list)
505 m_supportedProperties = list;
506 m_re->updateSupported(list,PropertyList());
508 /*void OBD2Source::propertySignal(VehicleProperty::Property property,boost::any value)
511 void OBD2Source::checkProperty()
514 void OBD2Source::setConfiguration(map<string, string> config)
516 // //Config has been passed, let's start stuff up.
517 configuration = config;
520 std::string port = "/dev/ttyUSB0";
521 std::string baud = "115200";
522 std::string btadapter = "";
523 m_isBluetooth = false;
526 //printf("OBD2Source::setConfiguration\n");
527 for (map<string,string>::iterator i=configuration.begin();i!=configuration.end();i++)
529 //printf("Incoming setting: %s:%s\n",(*i).first.c_str(),(*i).second.c_str());
530 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << "Incoming setting:" << (*i).first << ":" << (*i).second << endl;
531 if ((*i).first == "device")
535 else if ((*i).first == "baud")
537 if((*i).second != "")
541 else if ((*i).first == "bluetoothAdapter")
543 btadapter = (*i).second;
547 if(port.find(":") != string::npos)
549 m_btDeviceAddress = port;
550 m_btAdapterAddress = btadapter;
551 m_isBluetooth = true;
553 DebugOut()<<"bluetooth device?"<<endl;
554 ObdBluetoothDevice bt;
556 std::string tempPort = bt.getDeviceForAddress(port, btadapter);
559 DebugOut(3)<<"Using bluetooth device \""<<port<<"\" bound to: "<<tempPort<<endl;
564 DebugOut(0)<<"Device Error"<<endl;
566 //throw std::runtime_error("Device Error");
570 //connect(obd, port, baud);
571 CommandRequest *req = new CommandRequest();
572 req->req = "setportandbaud";
573 req->arglist.push_back(port);
574 req->arglist.push_back(baud);
575 g_async_queue_push(commandQueue,req);
579 m_gThread = g_thread_new("mythread",(GThreadFunc)&threadLoop,this);
580 //g_idle_add(updateProperties, this);
581 g_timeout_add(5,updateProperties,this);
584 OBD2Source::OBD2Source(AbstractRoutingEngine *re, map<string, string> config)
585 : AbstractSource(re, config)
587 bool success = VehicleProperty::registerProperty(Obd2Connected,[](){ return new Obd2ConnectType(Obd2Connected,false); });
594 clientConnected = false;
601 for(auto itr = obd2amb.supportedPidsList.begin(); itr != obd2amb.supportedPidsList.end(); itr++)
603 m_supportedProperties.push_back((*itr)->property);
606 m_supportedProperties.push_back(Obd2Connected);
608 re->setSupported(supported(), this);
609 /*if (openPort(std::string("/dev/pts/7"),115200))
611 printf("Error opening OBD2 port\n");
613 statusQueue = g_async_queue_new();
614 commandQueue = g_async_queue_new();
615 subscriptionAddQueue = g_async_queue_new();
616 subscriptionRemoveQueue = g_async_queue_new();
617 responseQueue = g_async_queue_new();
618 singleShotQueue = g_async_queue_new();
620 setConfiguration(config);
622 OBD2Source::~OBD2Source()
624 DebugOut() << "OBD2Source Destructor called!!!"<<endl;
625 m_threadLive = false;
626 g_thread_join(m_gThread);
629 PropertyList OBD2Source::supported()
631 return m_supportedProperties;
634 int OBD2Source::supportedOperations()
639 extern "C" AbstractSource * create(AbstractRoutingEngine* routingengine, map<string, string> config)
641 return new OBD2Source(routingengine, config);
644 string OBD2Source::uuid()
646 return "f77af740-f1f8-11e1-aff1-0800200c9a66";
648 void OBD2Source::subscribeToPropertyChanges(VehicleProperty::Property property)
650 if (property == VehicleProperty::VIN)
652 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "VIN subscription requested... but there's no point!"<<endl;
654 else if (property == VehicleProperty::WMI)
656 DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "WMI subscription requested... but there's no point!"<<endl;
660 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
662 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
667 ObdPid *pid = obd2AmbInstance->createPidforProperty(property);
674 //If the pid is currently in the blacklist map, erase it. This allows for applications
675 //to "un-blacklist" a pid by re-subscribing to it.
676 if (m_blacklistPidCountMap.find(pid->pid) != m_blacklistPidCountMap.end())
678 m_blacklistPidCountMap.erase(m_blacklistPidCountMap.find(pid->pid));
682 g_async_queue_push(subscriptionAddQueue,pid);
683 CommandRequest *req = new CommandRequest();
684 req->req = "connectifnot";
685 g_async_queue_push(commandQueue,req);
690 void OBD2Source::unsubscribeToPropertyChanges(VehicleProperty::Property property)
692 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
694 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
698 ObdPid *pid = obd2AmbInstance->createPidforProperty(property);
699 g_async_queue_push(subscriptionRemoveQueue,pid);
703 void OBD2Source::getPropertyAsync(AsyncPropertyReply *reply)
705 DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << "getPropertyAsync requested for " << reply->property << endl;
707 VehicleProperty::Property property = reply->property;
710 if(!ListPlusPlus<VehicleProperty::Property>(&m_supportedProperties).contains(property))
712 DebugOut(0)<<"obd plugin does not support: "<<property<<endl;
716 propertyReplyMap[reply->property] = reply;
718 ObdPid* requ = obd2AmbInstance->createPidforProperty(property);
719 g_async_queue_push(singleShotQueue,requ);
720 CommandRequest *req = new CommandRequest();
721 req->req = "connectifnot";
722 g_async_queue_push(commandQueue,req);
725 AsyncPropertyReply *OBD2Source::setProperty(AsyncSetPropertyRequest request )
727 AsyncPropertyReply* reply = new AsyncPropertyReply (request);
731 if(request.property == Obd2Connected)
733 propertyReplyMap[reply->property] = reply;
734 reply->success = true;
736 if(request.value->value<bool>() == true)
738 CommandRequest *req = new CommandRequest();
739 req->req = "connectifnot";
740 g_async_queue_push(commandQueue,req);
744 CommandRequest *req = new CommandRequest();
745 req->req = "disconnect";
746 g_async_queue_push(commandQueue,req);
753 reply->success = false;
756 reply->completed(reply);