linklayer / pyvit

pyvit: Python Vehicle Interface Toolkit
Other
506 stars 149 forks source link

Using ISO-TP Dispatcher functionality #42

Open mattie47 opened 5 years ago

mattie47 commented 5 years ago

Hi,

I was hoping someone here might be able to help with an example of how to use the dispatcher in a loop and if a condition is met, send a relevant response.

Currently I've just been interacting with Pyvit in a simple while loop with dev.recv. However I'm not sure how to interact with the dispatcher given things are threaded.

Basically, I have a simple program using Pyvit acting as an ECU to send things such as RPM and Speed when requested by a connected device.

As an example:

#!/usr/bin/env python3

from pyvit import can
from pyvit.hw import socketcan

import os,sys,termios,tty,time

SupportedPIDsRequest = "0201000000000000"

RPMRequest = "02010C0000000000"
VINReq = "0209020000000000"

RPMResp = 'cansend vcan0 7E8#04410C1094000000'
VINResp = 'cansend vcan0 7E8#1014490201355445 && sleep 0.1 && ' \
          'cansend vcan0 7E8#214D5535324E3636 &&' \
          'cansend vcan0 7E8#225A323236373433'

def resp_function():
    resp = can.Frame(0x7E8)
    resp.data = frameData
    dev.send(resp)
    print(resp)
    return resp

dev = socketcan.SocketCanDev("vcan0")
dev.start()

while True:
    CanFrameRecv = dev.recv()

    if CanFrameRecv.arb_id == int(0x7DF):
        if (bytes(CanFrameRecv.data)) == bytes.fromhex(SupportedPIDsRequest):
            print('\nSupported PIDs Request  : ' + SupportedPIDsRequest)

            frameData = [0x06, 0x41, 0x00, 0x00, 0x18, 0x30, 0x01]
            response = resp_function()
            print('Supported PIDs Response : ' + str(resp_function().data))

        elif (bytes(CanFrameRecv.data)) == bytes.fromhex(RPMRequest):
            print('\nRPM Request Received    : ' + RPMRequest)
            print('RPM Response            : ' + RPMResp)
            os.system(RPMResp)    
        elif (bytes(CanFrameRecv.data)) == bytes.fromhex(VINReq):
            print('VIN Req          : ' + VINReq)
            print(VINResp)
            os.system(VINResp)

As you can see, I'm "cheating" with the VIN, and just sending the frames out without actually checking if a frame control message is received. I want to improve this, and actually use the ISO-TP functionality.

My issue is I've struggled to get ISO-TP messages working nicely.

Using the isotp_test I was able to get an idea on how to send an ISO-TP frame, provided you send a frame control message.

#!/usr/bin/env python3

import threading
from multiprocessing import Queue

from pyvit.hw import socketcan
from pyvit.dispatch import Dispatcher
from pyvit.proto.isotp import IsotpInterface

dev = socketcan.SocketCanDev("vcan0")
disp = Dispatcher(dev)
recv_queue = Queue()
disp.add_receiver(recv_queue)

# set up an isotp interface Sender/Receiver arb id
sender = IsotpInterface(disp, 0x7DF, 0x7E8)
receiver = IsotpInterface(disp, 0x7E8, 0x7DF)
disp.start()

""" ISOTP transmission and reception of a multi-frame message"""
print("payload creation")
payload = [0x49, 0x02, 0x01, 0x31, 0x41, 0x31, 0x4A, 0x43, 0x35, 0x34, 0x34, 0x34, 0x52, 0x37, 0x32, 0x35, 0x32, 0x33, 0x36, 0x37]

# we need to run this in a thread so that the flow control frame is
# sent and received, as IsotpInterfaces block
print("starting tx_thread")
tx_thread = threading.Thread(target=sender.send, args=(payload,))
tx_thread.start()

# we'll receive data in this thread
resp = receiver.recv()

# wait for the transmitting thread to finish, then verify the result
tx_thread.join()

## I'm really not too sure what the above " .join"actually does....

print("finished")

Example:

pi@rpi-hos:~/repos/pyvit $ python3 print_all_frames.py
payload creation
starting tx_thread
[16, 20, 73, 2, 1, 49, 65, 49] <SEND CONTROL FRAME>
[33, 74, 67, 53, 52, 52, 52, 82]
[34, 55, 50, 53, 50, 51, 54, 55]
finished

I'm just not sure how to interact with the Dispatcher send and receive methods/threads within a while loop.

I'm starting to get the impression my main loop should also be a thread but again not sure how to do this.

Sorry if this is an annoying question - I'm still pretty new to python.

Thanks,

Matt

mattie47 commented 5 years ago

So I had another go:

#!/usr/bin/env python3

import threading
from multiprocessing import Queue

from pyvit.hw import socketcan
from pyvit.dispatch import Dispatcher
from pyvit.proto.isotp import IsotpInterface

dev = socketcan.SocketCanDev("vcan0")
disp = Dispatcher(dev)
recv_queue = Queue()
disp.add_receiver(recv_queue)

# set up an isotp interface Sender/Receiver arb id
sender = IsotpInterface(disp, 0x7DF, 0x7E8)
receiver = IsotpInterface(disp, 0x7E8, 0x7DF)
disp.start()

def vin():
    payload = [0x49, 0x02, 0x01, 0x31, 0x41, 0x31, 0x4A, 0x43, 0x35, 0x34, 0x34, 0x34, 0x52, 0x37, 0x32, 0x35, 0x32, 0x33, 0x36, 0x37]
    tx_thread = threading.Thread(target=sender.send, args=(payload,))
    tx_thread.start()
    resp = receiver.recv()
    print(resp)
    # resp
    # tx_thread.join()
    resp = []
    print("finished")

while True:
    CanFrameRecv = IsotpInterface.recv(receiver) # This works
    print(CanFrameRecv)

    if CanFrameRecv == [0x09, 0x02]:
        print("It is!")
        vin()

So the above "sort of" works, but still a tad buggy.

If I have tx_thread.join() in the function, and send VIN Req but no Control Frame, then it holds up the while loop from all other received frames until the thread control frame timeout.

The other issue, is that if you send a control frame, followed by the Vin Req, it'll immediately send the follow up frames. I tried adding resp = [] into the function in hope that it would clear the queue, but after playing around, I see this doesn't appear to make a difference.

Anyone able to give me a hand with this?

Thanks,

Matt