Fleet Management Demo

From Digi Developer

Jump to: navigation, search

This demos the using a LVDVD-S AutoTap Streamer, GPS and a Digi Gateway to monitor a vehicle.

Contents

Requirements

  • LVDVD-S AutoTap Streamer
  • XBee RS-232 serial adapter
  • ConnectPort X8 or ConnectPort X4
  • NEMA 0183 GPS support on gateway
  • Digi Connectware Manager server (v 3.5 or greater)

Set Up

The Gateway must be associated with the XBee RS-232 serial adapter, as well as be able to make a TCP connection to the Connectware server. To verify association with the XBee RS-232 serial adapter, perform a discovery on the mesh network. The AutoTap Streamer must be connected to the XBee RS-232 adapter. Under Remote Management on the gateways web interface configure it so it will connect to the Connectware server.

Code Overview

AutoTap_Gateway.py provides the driver for the XBee AutoTap Streamer. It sends and parses messages through the socket to the AutoTap Streamer. There is a high level class, AutoTapStreamer, which is intended for external use. The constructor basically takes in the extended address of a node, and then allows you to perform high level interaction with the AutoTap Streamer.

class AutoTapStreamer:
    """Provides high level access to the LVDVD-S AutoTap Streamer.
    It provides simplified methods for ensuring that the device is capable
    of communicating with the vehicle, and then retrieving the desired
    information once communication has been established.
    """
    PID_NAME_MAP = {0x00:"Vehicle Speed", 0x01:"Engine Speed", 
                  0x02:"Throttle Position", 0x03:"Odometer", 
                  0x04:"Fuel Level", 0x05:"Fuel Level Remaining", 
                  0x06:"Transmission Gear", 0x08:"Ignition Status", 
                  0x09:"MIL Status", 0x0A:"Airbag Dash Indicator", 
                  0x0B:"ABS Dash Indicator", 0x0C:"Fuel Rate", 
                  0x0D:"Battery Voltage", 0x0E:"PTO Status", 
                  0x0F:"Seatbelt Fastened", 0x10:"Misfire Monitor", 
                  0x11:"Fuel System Monitor", 0x12:"Comprehensive Component Monitor", 
                  0x13:"Catalyst Monitor", 0x14:"Heated Catalyst Monitor", 
                  0x15:"Evaporative System Monitor", 0x16:"Secondary Air System Monitor", 
                  0x17:"A/C System Refrigerant Monitor", 0x18:"Oxygen Sensor Monitor", 
                  0x19:"Oxygen Sensor Heater Monitor", 0x1A:"EGR System Monitor", 
                  0x1B:"Brake Switch Status", 0x1D:"Cruise Control Status", 
                  0x1E:"Turn Signal Status", 0x1F:"Oil Pressure Lamp", 
                  0x20:"Brake Indicator Light", 0x21:"Coolant Hot Lamp", 
                  0x22:"Trip Odometer", 0x23:"Trip Fuel Consumption"}
 
    PID_UNIT_MAP = {0x00:"MPH", 0x01:"RPM", 
                  0x02:"%", 0x03:"Miles", 
                  0x04:"%", 0x05:"Gallons", 
                  0x06:"", 0x08:"", 
                  0x09:"", 0x0A:"", 
                  0x0B:"", 0x0C:"Gallons per Hour", 
                  0x0D:"Volts", 0x0E:"", 
                  0x0F:"", 0x10:"", 
                  0x11:"", 0x12:"", 
                  0x13:"", 0x14:"", 
                  0x15:"", 0x16:"", 
                  0x17:"", 0x18:"", 
                  0x19:"", 0x1A:"", 
                  0x1B:"", 0x1D:"", 
                  0x1E:"", 0x1F:"", 
                  0x20:"", 0x21:"", 
                  0x22:"Miles", 0x23:"Gallons"}
 
    def __init__(self, addr_extended):
        """Create an instance of the AutoTapStreamer using COM1 for communication"""
        self.parameterCallbacks = set()
        self.frameManager = FrameManager(addr_extended, self.handleTimeBasedParameterUpdate)
 
    def close(self):
        self.frameManager.close()
 
    def forceRedetect(self):
        """Force the AutoTap Streamer to retrieve vehicle information.
 
        The AutoTap Streamer saves information about the vehicle it is connected to
        in order to reduce the time it takes to go from power up to being ready for
        communication.  This command needs to be called when moving the AutoTap Streamer
        to a new vehicle, so it will retrieve the necessary information from the vehicle
 
        Returns True on a successful send, False otherwise.
        """
 
        return bool(self.frameManager.forceRedetect())
 
    def readyForCommunication(self):
        """Return communication state of the AutoTap Streamer.
 
        It may take up to a minute for the AutoTap Streamer to perform
        startup procedures and be ready to communicate with the vehicle.
 
        This method returns True if the device is ready for communication
        with the vehicle, False if it is still pending, or None if we were
        unable to detect the state of the device.
        """
 
        return self.frameManager.getDeviceIsReady()
 
    def getVIN(self):
        """Return VIN of the vehicle.
 
        Returns the VIN of the vehicle, else False on a failure to read
        """
 
        returnValue = self.frameManager.getVIN()
        if returnValue == None:
            return False
        else:
            return returnValue
 
    def getDiagnosticTroubleCodes(self):
        """Return list of diagnostic trouble codes.
        Returns a list of 5 character diagnostic codes, or False on 
        failure to retrieve.
        """
 
        returnValue = self.frameManager.getDTCs()
        if returnValue == None:
            return False
        else:
            return returnValue
 
    def getSupportedParameters(self):
        """Return list of supported parameters for the vehicle
        Returns a list of Parameter IDs that are supported by this vehicle, or False if we
        were unable to retrieve them.
        """
 
        returnValue = self.frameManager.getSupportedParameters()
        if returnValue == None:
            return False
        else:
            return returnValue
 
 
    def getParameterValues(self, parameters):
        """Retrieve current vehicle parameters
        Return a diction mapping the requested parameters to their current value, or False
        on a failure during retrieval.  Up to 11 parameters can be requested per month.
        """
        values = self.frameManager.getParameters(parameters)
        if values == None:
            return False
 
        returnData = {}
 
        for pid,val in zip(parameters, values):
            returnData[pid] = val
 
        return returnData
 
    def enableTimeBasedRetrieval(self, parameter, enable, interval):
        """Configure automatic reporting of given parameter.
        For the given parameter, enable=True will turn on automatic
        updates for the parameter, where enable=False will disable this functionality.
        interval is given as a multiple of 50 ms, ranging from 1 (50 ms update) to 65535
        for an interval of 54.6 minutes.
        Parameter updates will be returned asynchrously through the callback specified
        in the addParameterCallback function.
 
        Returns True on successful configuration, False otherwise.
        """
        return self.frameManager.enableTimeBasedRetrieval(parameter, enable, interval) != None
 
    def enableTimeBasedMode(self, enable):
        """Configure automatic parameter update mode
        This method turns on or off the entire time based
        updating mode.
        Returns True on successful configuration, False otherwise.
        """
 
        return self.frameManager.enableTimeBasedMode(enable) != None
 
    def addParameterCallback(self, callback):
        """Register a callback for parameter updates.
        The function should take two parameters, the first being the
        parameter ID, and the second being the value of the function
        """
        self.parameterCallbacks.add(callback)
 
    def removeParameterCallback(self, callback):
       """Remove a registered parameter callback function.
        Returns True if a parameter was found and removed,
        or False if the function was not registered.
       """
 
        try:
            self.parameterCallbacks.remove(callback)
        except KeyError:
            return False
 
        return True
 
    def handleTimeBasedParameterUpdate(self, paramMap):
        for callback in self.parameterCallbacks:
            callback(paramMap)
 
    def convertValueToReadableFormat(self, pid, incomingValue):
        value = math.floor(incomingValue)
        readableValue = "Invalid Input"
 
        if pid == 0x06: #Transmission
            if value == 0:
                readableValue = "Unknown"
            elif value == 1:
                readableValue = "Park"
            elif value == 2:
                readableValue = "Neutral"
            elif value == 3:
                readableValue = "Drive"
            elif value == 4:
                readableValue = "Reverse"
        elif pid == 0x08 or pid == 0x09 or pid == 0x0A or pid == 0x0B or pid == 0x0E or pid == 0x1D or pid == 0x1F or pid == 0x20 or pid == 0x21:
            if value == 0:
                readableValue = "On"
            elif value == 1:
                readableValue = "Off"
        elif pid == 0x0F:
            if value == 0:
                readableValue = "Yes"
            elif value == 1:
                readableValue = "No"
        elif pid >= 0x10 and pid <= 0x1A:
            if value == 0:
                readableValue = "Complete"
            elif value == 1:
               readableValue = "Not Complete"
        elif pid == 0x1B:
            if value == 0:
                readableValue = "Pressed"
            elif value == 1:
                readableValue = "Not Pressed"
        elif pid == 0x1E:
            if value == 0:
                readableValue = "Left"
            elif value == 1:
                readableValue = "Right"
            elif value == 2:
                readableValue = "Off"
        else:
            readableValue = str(round(incomingValue, 2))
 
        return readableValue

AutoTap_Gateway_Demo.py is the main logic for the application. It opens up a serial port to grab an NMEA stream, passes it to nmea.py for parsing:

threading.Thread(target=gpsListener).start()
# Listener for the NMEA stream, we read and feed it to the
# NMEA parser
def gpsListener():
        print "running..."
        # select vars
        rlist = []
        wlist = []
        xlist = []
        # Create serial connection
        rlist.append(serialfd)
        while True:
            ready = select(rlist, wlist, xlist)
            if serialfd in ready[0]:
                gps.feed(os.read(serialfd, 16384))

then combines that data along with an instantiation of the AutoTapStreamer class to collect all of the data for the demo.

# Grabs the latest data from the GPS and AutoTap Streamer, and then
# formats it into an XML string
def getAutoTapXML():
    pos = gps.position()
    print "Got Pos: " + str(pos)
    time = gps.time()
 
    print "Got Time: " + str(time)
    if time[1] == "000000" or time[1] == "":
        return (False, "<error>Failed to get time from GPS</error>")    
    troubleCodeString = ""
    vin = autoTap.getVIN()
    if vin == False:
        vin = "Not Available"     
 
    troubleCodes = autoTap.getDiagnosticTroubleCodes()
    if troubleCodes == False:
        troubleCodes = ["Not Available"]
    elif len(troubleCodes) == 0:
        troubleCodes = ["None"]
 
    supportedParameters = autoTap.getSupportedParameters()
    if supportedParameters == False:
        supportedParameters = []        
 
    fail_count = 0      
 
    allParamValMap = {}
    params = []
    for pid in supportedParameters:
        vals = autoTap.getParameterValues([pid])
        if vals != False:
            for pidReturned in vals.keys():
                allParamValMap[pidReturned] = vals[pidReturned]
        else:
            fail_count += 1
            if fail_count > 2:
                return (False, "<error>Failed to retrieve at least 3 automotive parameters</error>")
 
    # Creating XML
    doc = Document()
    deviceSampleElement = doc.createElement("device_vehicle_sample")
    deviceSampleElement.setAttribute("sec", str(time[0])[4:6])
    deviceSampleElement.setAttribute("min", str(time[0])[2:4])
    deviceSampleElement.setAttribute("hour", str(time[0])[0:2])
    deviceSampleElement.setAttribute("day", str(time[1])[0:2])
    deviceSampleElement.setAttribute("month", str(time[1])[2:4])
    deviceSampleElement.setAttribute("year", str(time[1])[4:6])
    doc.appendChild(deviceSampleElement)    
    deviceElement = doc.createElement("device")
    deviceElement.setAttribute("id", VEHICLE_NAME)
    deviceSampleElement.appendChild(deviceElement)
    positionElement = doc.createElement("position")
    deviceElement.appendChild(positionElement)
    latElement = doc.createElement("lat")
    latElement.setAttribute("value", str(pos[0]))
    positionElement.appendChild(latElement)
    lonElement = doc.createElement("lon")
    lonElement.setAttribute("value", str(pos[1]))
    positionElement.appendChild(lonElement)   
    automotiveElement = doc.createElement("automotive")
    deviceElement.appendChild(automotiveElement)
    dtcElement = doc.createElement("diagnostic_trouble_codes")
    dtcElement.setAttribute("display", "Diagnostic Trouble Codes")
    dtcElement.setAttribute("value", troubleCodeString)
    dtcElement.setAttribute("units", "")
    automotiveElement.appendChild(dtcElement)
 
    for pid in allParamValMap:
        pidElement = doc.createElement(XML_PID_NAME_MAP[pid])
        pidElement.setAttribute("display", AutoTapStreamer.PID_NAME_MAP[pid])
        pidElement.setAttribute("value", autoTap.convertValueToReadableFormat(pid, allParamValMap[pid]))
        pidElement.setAttribute("units", AutoTapStreamer.PID_UNIT_MAP[pid])
        automotiveElement.appendChild(pidElement)
 
    print doc.toprettyxml(indent="  ")   
 
    return (True, doc.toprettyxml(indent="  "))

At a certain interval it uses these two pieces to push up automotive and location data to a Connectware server.

# Grab the XML and then upload it to the Connectware data service
def uploader():
    count = 0
    while True:
        print "Getting XML"
        (result, xml) = getAutoTapXML()
        if result == False:
            cwm_data.send_cwm_data(xml, "error.xml", secure=False)
            print "Encountered Error: %s" % xml
        else:
            filename = "fleetmanagementsample_%i.xml" % (count)           
 
            try:
                (success, code, msg) = cwm_data.send_cwm_data(xml, filename, secure=False)
            except:
                success = False                
            if success:
                print "Succeeded in pushing sample %i to Connectware" % (count)
                count += 1
                count %= WRAP_NUMBER
            else:
                print "Failed to send sample to Connectware"
 
        print "Sleeping"
        time.sleep(SLEEP_TIME)

Notes

This document does not go into detail on how to send or retrieve data with the Connectware Manager Server.

Source

Media:fleetmanagement.zip

Personal tools
Wiki Editing