From ccf35c0dc912e9a29721fa04244fc7f932282d18 Mon Sep 17 00:00:00 2001 From: Arthur Guyader Date: Tue, 27 Aug 2019 11:51:26 +0200 Subject: Remove file socketcan-j1939 that was not removed before Bug-AGL : SPEC-2780 Change-Id: I4ddf9389faffebe0334b0004b69bb7336277c29d Signed-off-by: Arthur Guyader --- docs/4-Installation-ISOTP.md | 50 +++ docs/4-Usage.md | 416 ---------------------- docs/5-Usage.md | 424 +++++++++++++++++++++++ docs/api-services-book.yml | 4 +- low-can-binding/binding/low-can-cb.cpp | 1 + low-can-binding/binding/low-can-subscription.cpp | 125 +++++++ low-can-binding/binding/low-can-subscription.hpp | 4 +- low-can-binding/utils/socketcan-j1939.cpp | 195 ----------- 8 files changed, 605 insertions(+), 614 deletions(-) create mode 100644 docs/4-Installation-ISOTP.md delete mode 100644 docs/4-Usage.md create mode 100644 docs/5-Usage.md delete mode 100644 low-can-binding/utils/socketcan-j1939.cpp diff --git a/docs/4-Installation-ISOTP.md b/docs/4-Installation-ISOTP.md new file mode 100644 index 00000000..c8fc31f6 --- /dev/null +++ b/docs/4-Installation-ISOTP.md @@ -0,0 +1,50 @@ +# Installation isotp for AGL + +## Compilation and installation of module kernel isotp + +##### Clone repository Linux Kernel Module for ISO 15765-2:2016 CAN transport protocol + +```bash +git clone https://github.com/hartkopp/can-isotp.git +``` + +##### Move into the new repository + +```bash +cd can-isotp +``` + +##### Install packages to build + +```bash +sudo apt-get install build-essential linux-headers-$(uname -r) +``` + +##### Compile + +```bash +make +``` + +##### Install + +```bash +sudo make modules_install +``` + +##### Load module + + +```bash +modprobe can +modprobe vcan +sudo insmod ./net/can/can-isotp.ko +``` + + +## Include headers files + + +```bash +sudo cp include/uapi/linux/can/isotp.h /usr/include/linux/can/ +``` \ No newline at end of file diff --git a/docs/4-Usage.md b/docs/4-Usage.md deleted file mode 100644 index 6c6d1380..00000000 --- a/docs/4-Usage.md +++ /dev/null @@ -1,416 +0,0 @@ -# Configure the AGL system - -## Virtual CAN device - -Connected to the target, here is how to load the virtual CAN device driver and -set up a new vcan device : - -```bash -modprobe vcan -ip link add vcan0 type vcan -ip link set vcan0 up -``` - -You also can named your linux CAN device like you want and if you need name it -`can0` : - -```bash -modprobe vcan -ip link add can0 type vcan -ip link set can0 up -``` - -## CAN device using the USB CAN adapter - -Using real connection to CAN bus of your car using the USB CAN adapter -connected to the OBD2 connector. - -Once connected, launch `dmesg` command and search which device to use: - -```bash -dmesg -[...] -[ 131.871441] usb 1-3: new full-speed USB device number 4 using ohci-pci -[ 161.860504] can: controller area network core (rev 20120528 abi 9) -[ 161.860522] NET: Registered protocol family 29 -[ 177.561620] usb 1-3: USB disconnect, device number 4 -[ 191.061423] usb 1-2: USB disconnect, device number 3 -[ 196.095325] usb 1-2: new full-speed USB device number 5 using ohci-pci -[ 327.568882] usb 1-2: USB disconnect, device number 5 -[ 428.594177] CAN device driver interface -[ 1872.551543] usb 1-2: new full-speed USB device number 6 using ohci-pci -[ 1872.809302] usb_8dev 1-2:1.0 can0: firmware: 1.7, hardware: 1.0 -[ 1872.809356] usbcore: registered new interface driver usb_8dev -``` - -Here device is named `can0`. - -This instruction assuming a speed of 500000kbps for your CAN bus, you can try -others supported bitrate like 125000, 250000 if 500000 doesn't work: - -```bash -ip link set can0 type can bitrate 500000 -ip link set can0 up -ip link show can0 - can0: mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10 - link/can - can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0 - bitrate 500000 sample-point 0.875 - tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1 - sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1 - clock 16000000 -``` - -On a Rcar Gen3 board, you'll have your CAN device as `can1` because `can0` -already exists as an embedded device. - -The instructions will be the same: - -```bash -ip link set can1 type can bitrate 500000 -ip link set can1 up -ip link show can1 - can0: mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10 - link/can - can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0 - bitrate 500000 sample-point 0.875 - tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1 - sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1 - clock 16000000 -``` - -## Rename an existing CAN device - -You can rename an existing CAN device using following command and doing so move -an existing `can0` device to anything else and then use another device as `can0` -. For a Rcar Gen3 board do the following by example: - -```bash -sudo ip link set can0 down -sudo ip link set can0 name bsp-can0 -sudo ip link set bsp-can0 up -``` - -Then connect your USB CAN device that will be named `can0` by default. - -# Configure the binding - -The binding reads system configuration file _/etc/dev-mapping.conf_ at start to -map logical name from signals described in JSON file to linux devices name -initialized by the system. - -Edit file _/etc/dev-mapping.conf_ and add mapping in section `CANbus-mapping`. - -Default binding configuration use a CAN bus named `hs` so you need to map it to -the real one, here are some examples: - -* Using virtual CAN device as described in the previous chapter: - -```ini -[CANbus-mapping] -hs="vcan0" -ls="vcan1" -``` - -* Using real CAN device, this example assume CAN bus traffic will be on can0. - -```ini -[CANbus-mapping] -hs="can0" -ls="can1" -``` - -* On a Rcar Gen3 board there is an embedded CAN device so `can0` already exists. So you might want to use your USB CAN adapter plugged to the OBD2 connector, in this case use `can1`: - -```ini -[CANbus-mapping] -hs="can1" -``` - -* You can use this configuration for j1939: - -```ini -[CANbus-mapping] -hs="can0" -ls="can1" -j1939="can2" -``` - -> **CAUTION VERY IMPORTANT:** Make sure the CAN bus\(es\) you specify in your -> configuration file match those specified in your generated source file with -> the `CAN-config-generator`. - - - -## Change name of ECU for J1939 - -To change the name of an ECU to J1939, you must go to the file conf.d/cmake/config.cmake and modify the value at : - - -```cmake -# Define name for ECU -set(J1939_NAME_ECU 0x1239) -``` - - - -# Run it, test it, use it. - -You can run the binding using **afm-util** tool, here is the classic way to go : - -```bash -afm-util run low-can-service@4.0 -1 -``` - -You can find instructions to use afm-util tool -[here](../../reference/af-main/1-afm-daemons.html#using-afm-util), - as well as documentation about Application Framework. - -But you can't control nor interact with it because you don't know security -token that **Application Framework** gaves it at launch. - -So, to test it, it is better to launch the binding manually. In the following -example, it will use port **1234** and left empty security token for testing -purpose: - -```bash -afb-daemon --binding=/var/lib/afm/applications/low-can-service/4.0/lib/afb-low-can.so --rootdir=/var/lib/afm/applications/low-can-service/4.0/ --port=1234 --token=1 -NOTICE: binding [/usr/lib/afb/afb-dbus-binding.so] calling registering function afbBindingV1Register -NOTICE: binding /usr/lib/afb/afb-dbus-binding.so loaded with API prefix dbus -NOTICE: binding [/usr/lib/afb/authLogin.so] calling registering function afbBindingV1Register -NOTICE: binding /usr/lib/afb/authLogin.so loaded with API prefix auth -NOTICE: binding [/var/lib/afm/applications/low-can-service/4.0/libs//low-can-binding.so] calling registering function afbBindingV1Register -NOTICE: binding /var/lib/afm/applications/low-can-service/4.0/libs//low-can-binding.so loaded with API prefix low-can -NOTICE: Waiting port=1234 rootdir=/var/lib/afm/applications/low-can-service/4.0/ -NOTICE: Browser URL= http:/*localhost:1234 -``` - -On another terminal, connect to the binding using previously installed -**AFB Websocket CLI** tool: - -```bash -afb-client-demo ws://localhost:1234/api?token=1 -``` - -You will be on an interactive session where you can communicate directly with -the binding API. - -The binding provides at this moment 2 verbs, _subscribe_ and _unsubscribe_, -which can take argument by a JSON **event** object. - -The argument value is the CAN message **generic\_name** as described in the -JSON file used to generate cpp file for the binding. - -To use the _**AFB Websocket CLI**_ tool, a command line will be like the -following: - -``` - -``` - -Where: - -* API : _**low-can**_. -* Verb : _**subscribe**_ or _**unsubscribe**_ -* Arguments : _**{ "event": "driver.doors.open" }**_ - -## Subscription and unsubscription - -You can ask to subscribe to chosen CAN event with a call to _subscribe_ API -verb with the CAN messages name as JSON argument. - -> **NOTE:** If no argument is provided, then you'll subscribe to all signals -> at once. - -For example from a websocket session: - -```json -low-can subscribe { "event": "doors.driver.open" } -ON-REPLY 1:low-can/subscribe: {"jtype":"afb-reply","request":{"status":"success","uuid":"a18fd375-b6fa-4c0e-a1d4-9d3955975ae8"}} -``` - -Subscription and unsubscription can take wildcard in their _event_ value and are -**case-insensitive**. - -To receive all doors events : - -```json -low-can subscribe { "event" : "doors*" } -ON-REPLY 1:low-can/subscribe: {"jtype":"afb-reply","request":{"status":"success","uuid":"511c872e-d7f3-4f3b-89c2-aa9a3e9fbbdb"}} -``` - -Then you will receive an event each time a CAN message is decoded for the event -named _doors.driver.open_ with its received timestamp if available: - -```json -ON-EVENT low-can/messages.doors.driver.open({"event":"low-can\/messages.doors.driver.open","data":{"name":"messages.doors.driver.open","value":true, "timestamp": 1505812906020023},"jtype":"afb-event"}) -``` - -Notice that event shows you that the CAN event is named -_messages.doors.driver.open_ but you ask for event about -_doors.driver.open_. - -This is because all CAN messages or diagnostic messages are prefixed by the -JSON parent node name, **messages** for CAN messages and -**diagnostic\_messages** for diagnostic messages like OBD2. - -This will let you subscribe or unsubcribe to all signals at once, not -recommended, and better make filter on subscribe operation based upon their type. Examples: - -```json -low-can subscribe { "event" : "*speed*" } --> will subscribe to all messages with speed in their name. Search will be make without prefix for it. -low-can subscribe { "event" : "speed*" } --> will subscribe to all messages begin by speed in their name. Search will be make without prefix for it. -low-can subscribe { "event" : "messages*speed*" } --> will subscribe to all CAN messages with speed in their name. Search will be on prefixed messages here. -low-can subscribe { "event" : "messages*speed" } --> will subscribe to all CAN messages ending with speed in their name. Search will be on prefixed messages here. -low-can subscribe { "event" : "diagnostic*speed*" } --> will subscribe to all diagnostic messages with speed in their name. Search will be on prefixed messages here. -low-can subscribe { "event" : "diagnostic*speed" } --> will subscribe to all diagnostic messages ending with speed in their name. Search will be on prefixed messages here. -``` - -You can also subscribe to an event with the ID or the PGN of the message definition : - - -```json -low-can subscribe {"id" : 1568} -low-can subscribe {"pgn" : 61442} -``` - -And subscribe to all ID or PGN : - -```json -low-can subscribe {"id" : "*"} -low-can subscribe {"pgn" : "*"} -``` - - -You can stop receiving event from it by unsubscribe the signal the same way you did for subscribe - -```json -low-can unsubscribe { "event": "doors.driver.open" } -ON-REPLY 2:low-can/unsubscribe: {"jtype":"afb-reply","request":{"status":"success"}} -low-can unsubscribe { "event" : "doors*" } -ON-REPLY 3:low-can/unsubscribe: {"jtype":"afb-reply","request":{"status":"success"}} -``` - -### Filtering capabilities - -It is possible to limits received event notifications into minimum and maximum -boundaries as well as doing frequency thinning. This is possible using the -argument filter with one or more of the filters available : - -* frequency: specify in Hertz the frequency which will be used to getting - notified of new CAN events for the designated signal. If, during the blocked - time, further changed CAN messages are received, the last valid one will be - transferred after the lockout with a RX_CHANGED. -* min: Minimum value that the decoded value needs to be above to get pushed to - the subscribed client(s). -* max: Maximum value that the decoded value needs to be below to get pushed to - the subscribed client(s) - -Order doesn't matter neither the number of filters chosen, you can use one, two -or all of them at once. - -Usage examples : - -```json -low-can subscribe {"event": "messages.engine.speed", "filter": { "frequency": 3, "min": 1250, "max": 3500}} -low-can subscribe {"event": "messages.engine.load", "filter": { "min": 30, "max": 100}} -low-can subscribe {"event": "messages.vehicle.speed", "filter": { "frequency": 2}} -``` - -## Get last signal value and list of configured signals - -You can also ask for a particular signal value on one shot using **get** verb, like -this: - -```json -low-can get {"event": "messages.engine.speed"} -ON-REPLY 1:low-can/get: {"response":[{"event":"messages.engine.speed","value":0}],"jtype":"afb-reply","request":{"status":"success"}} -``` - -> **CAUTION** Only one event could be requested. - -Also, if you want to know the supported CAN signals loaded by **low-can**, use -verb **list** - -```json -low-can list -ON-REPLY 2:low-can/list: {"response":["messages.hvac.fan.speed","messages.hvac.temperature.left","messages.hvac.temperature.right","messages.hvac.temperature.average","messages.engine.speed","messages.fuel.level.low","messages.fuel.level","messages.vehicle.average.speed","messages.engine.oil.temp","messages.engine.oil.temp.high","messages.doors.boot.open","messages.doors.front_left.open","messages.doors.front_right.open","messages.doors.rear_left.open","messages.doors.rear_right.open","messages.windows.front_left.open","messages.windows.front_right.open","messages.windows.rear_left.open","messages.windows.rear_right.open","diagnostic_messages.engine.load","diagnostic_messages.engine.coolant.temperature","diagnostic_messages.fuel.pressure","diagnostic_messages.intake.manifold.pressure","diagnostic_messages.engine.speed","diagnostic_messages.vehicle.speed","diagnostic_messages.intake.air.temperature","diagnostic_messages.mass.airflow","diagnostic_messages.throttle.position","diagnostic_messages.running.time","diagnostic_messages.EGR.error","diagnostic_messages.fuel.level","diagnostic_messages.barometric.pressure","diagnostic_messages.ambient.air.temperature","diagnostic_messages.commanded.throttle.position","diagnostic_messages.ethanol.fuel.percentage","diagnostic_messages.accelerator.pedal.position","diagnostic_messages.hybrid.battery-pack.remaining.life","diagnostic_messages.engine.oil.temperature","diagnostic_messages.engine.fuel.rate","diagnostic_messages.engine.torque"],"jtype":"afb-reply","request":{"status":"success","uuid":"32df712a-c7fa-4d58-b70b-06a87f03566b"}} -``` - -## Write on CAN buses - -Two modes could be used for that which is either specifying the CAN bus and a -*RAW* CAN message either by specifying a defined signal, **case-insensitively**, -and its value. - -Examples: - -```json -# Authentification -low-can auth -# Write a raw can frame to the CAN id 0x620 -low-can write { "bus_name": "hs", "frame": { "can_id": 1568, "can_dlc": 8, "can_data": [ 255,255,255,255,255,255,255,255]} } -# Write a signal's value. -low-can write { "signal_name": "engine.speed", "signal_value": 1256} -# Write J1939 'single frame' -low-can write { "bus_name": "j1939", "frame": { "pgn": 62420, "length":8, "data": [ 255,255,255,255,255,255,255,255]} } -# Write J1939 'multi frame' -low-can write { "bus_name": "j1939", "frame": { "pgn": 62420, "length":9, "data": [ 255,255,255,255,255,255,255,255,254]} } -``` - -To be able to use write capability, you need to add the permission - ```urn:AGL:permission::platform:can:write``` to your package configuration - file that need to write on CAN bus through **low-can** api. - -Then in order to write on bus, your app needs to call verb **auth** -before calling **write**, to raise its **LOA**, Level Of Assurance, -which controls usage of verb **write**. - -## Using CAN utils to monitor CAN activity - -You can watch CAN traffic and send custom CAN messages using can-utils -preinstalled on AGL target. - -To watch watch going on a CAN bus use: - -```bash -candump can0 -``` - -Or for an USB CAN adapter connected to porter board: - -```bash -candump can1 -``` - -Send a custom message: - -```bash -cansend can0 ID#DDDDAAAATTTTAAAA -``` - -You can also replay a previously dumped CAN logfiles. These logfiles can be -found in _can_samples_ directory under Git repository. Following examples use -a real trip from an Auris Toyota car. - -Trace has been recorded from a CAN device `can0` so you have to map it to the -correct one you use for your tests. - -Replay on a virtual CAN device `vcan0`: - -```bash -canplayer -I trip_test_with_obd2_vehicle_speed_requests vcan0=can0 -``` - -Replay on a CAN device `can0`: - -```bash -canplayer -I trip_test_with_obd2_vehicle_speed_requests can0 -``` - -Replay on a CAN device `can1` (porter by example): - -```bash -canplayer -I trip_test_with_obd2_vehicle_speed_requests can1=can0 -``` diff --git a/docs/5-Usage.md b/docs/5-Usage.md new file mode 100644 index 00000000..9e9841e8 --- /dev/null +++ b/docs/5-Usage.md @@ -0,0 +1,424 @@ +# Configure the AGL system + +## Virtual CAN device + +Connected to the target, here is how to load the virtual CAN device driver and +set up a new vcan device : + +```bash +modprobe vcan +ip link add vcan0 type vcan +ip link set vcan0 up +``` + +You also can named your linux CAN device like you want and if you need name it +`can0` : + +```bash +modprobe vcan +ip link add can0 type vcan +ip link set can0 up +``` + +## CAN device using the USB CAN adapter + +Using real connection to CAN bus of your car using the USB CAN adapter +connected to the OBD2 connector. + +Once connected, launch `dmesg` command and search which device to use: + +```bash +dmesg +[...] +[ 131.871441] usb 1-3: new full-speed USB device number 4 using ohci-pci +[ 161.860504] can: controller area network core (rev 20120528 abi 9) +[ 161.860522] NET: Registered protocol family 29 +[ 177.561620] usb 1-3: USB disconnect, device number 4 +[ 191.061423] usb 1-2: USB disconnect, device number 3 +[ 196.095325] usb 1-2: new full-speed USB device number 5 using ohci-pci +[ 327.568882] usb 1-2: USB disconnect, device number 5 +[ 428.594177] CAN device driver interface +[ 1872.551543] usb 1-2: new full-speed USB device number 6 using ohci-pci +[ 1872.809302] usb_8dev 1-2:1.0 can0: firmware: 1.7, hardware: 1.0 +[ 1872.809356] usbcore: registered new interface driver usb_8dev +``` + +Here device is named `can0`. + +This instruction assuming a speed of 500000kbps for your CAN bus, you can try +others supported bitrate like 125000, 250000 if 500000 doesn't work: + +```bash +ip link set can0 type can bitrate 500000 +ip link set can0 up +ip link show can0 + can0: mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10 + link/can + can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0 + bitrate 500000 sample-point 0.875 + tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1 + sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1 + clock 16000000 +``` + +On a Rcar Gen3 board, you'll have your CAN device as `can1` because `can0` +already exists as an embedded device. + +The instructions will be the same: + +```bash +ip link set can1 type can bitrate 500000 +ip link set can1 up +ip link show can1 + can0: mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10 + link/can + can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0 + bitrate 500000 sample-point 0.875 + tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1 + sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1 + clock 16000000 +``` + +## Rename an existing CAN device + +You can rename an existing CAN device using following command and doing so move +an existing `can0` device to anything else and then use another device as `can0` +. For a Rcar Gen3 board do the following by example: + +```bash +sudo ip link set can0 down +sudo ip link set can0 name bsp-can0 +sudo ip link set bsp-can0 up +``` + +Then connect your USB CAN device that will be named `can0` by default. + +# Configure the binding + +The binding reads system configuration file _/etc/dev-mapping.conf_ at start to +map logical name from signals described in JSON file to linux devices name +initialized by the system. + +Edit file _/etc/dev-mapping.conf_ and add mapping in section `CANbus-mapping`. + +Default binding configuration use a CAN bus named `hs` so you need to map it to +the real one, here are some examples: + +* Using virtual CAN device as described in the previous chapter: + +```ini +[CANbus-mapping] +hs="vcan0" +ls="vcan1" +``` + +* Using real CAN device, this example assume CAN bus traffic will be on can0. + +```ini +[CANbus-mapping] +hs="can0" +ls="can1" +``` + +* On a Rcar Gen3 board there is an embedded CAN device so `can0` already exists. So you might want to use your USB CAN adapter plugged to the OBD2 connector, in this case use `can1`: + +```ini +[CANbus-mapping] +hs="can1" +``` + +* You can use this configuration for j1939: + +```ini +[CANbus-mapping] +hs="can0" +ls="can1" +j1939="can2" +``` + +> **CAUTION VERY IMPORTANT:** Make sure the CAN bus\(es\) you specify in your +> configuration file match those specified in your generated source file with +> the `CAN-config-generator`. + + + +## Change name of ECU for J1939 + +To change the name of an ECU to J1939, you must go to the file conf.d/cmake/config.cmake and modify the value at : + + +```cmake +# Define name for ECU +set(J1939_NAME_ECU 0x1239) +``` + + + +# Run it, test it, use it. + +You can run the binding using **afm-util** tool, here is the classic way to go : + +```bash +afm-util run low-can-service@4.0 +1 +``` + +You can find instructions to use afm-util tool +[here](../../reference/af-main/1-afm-daemons.html#using-afm-util), + as well as documentation about Application Framework. + +But you can't control nor interact with it because you don't know security +token that **Application Framework** gaves it at launch. + +So, to test it, it is better to launch the binding manually. In the following +example, it will use port **1234** and left empty security token for testing +purpose: + +```bash +afb-daemon --binding=/var/lib/afm/applications/low-can-service/4.0/lib/afb-low-can.so --rootdir=/var/lib/afm/applications/low-can-service/4.0/ --port=1234 --token=1 +NOTICE: binding [/usr/lib/afb/afb-dbus-binding.so] calling registering function afbBindingV1Register +NOTICE: binding /usr/lib/afb/afb-dbus-binding.so loaded with API prefix dbus +NOTICE: binding [/usr/lib/afb/authLogin.so] calling registering function afbBindingV1Register +NOTICE: binding /usr/lib/afb/authLogin.so loaded with API prefix auth +NOTICE: binding [/var/lib/afm/applications/low-can-service/4.0/libs//low-can-binding.so] calling registering function afbBindingV1Register +NOTICE: binding /var/lib/afm/applications/low-can-service/4.0/libs//low-can-binding.so loaded with API prefix low-can +NOTICE: Waiting port=1234 rootdir=/var/lib/afm/applications/low-can-service/4.0/ +NOTICE: Browser URL= http:/*localhost:1234 +``` + +On another terminal, connect to the binding using previously installed +**AFB Websocket CLI** tool: + +```bash +afb-client-demo ws://localhost:1234/api?token=1 +``` + +You will be on an interactive session where you can communicate directly with +the binding API. + +The binding provides at this moment 2 verbs, _subscribe_ and _unsubscribe_, +which can take argument by a JSON **event** object. + +The argument value is the CAN message **generic\_name** as described in the +JSON file used to generate cpp file for the binding. + +To use the _**AFB Websocket CLI**_ tool, a command line will be like the +following: + +``` + +``` + +Where: + +* API : _**low-can**_. +* Verb : _**subscribe**_ or _**unsubscribe**_ +* Arguments : _**{ "event": "driver.doors.open" }**_ + +## Subscription and unsubscription + +You can ask to subscribe to chosen CAN event with a call to _subscribe_ API +verb with the CAN messages name as JSON argument. + +> **NOTE:** If no argument is provided, then you'll subscribe to all signals +> at once. + +For example from a websocket session: + +```json +low-can subscribe { "event": "doors.driver.open" } +ON-REPLY 1:low-can/subscribe: {"jtype":"afb-reply","request":{"status":"success","uuid":"a18fd375-b6fa-4c0e-a1d4-9d3955975ae8"}} +``` + +Subscription and unsubscription can take wildcard in their _event_ value and are +**case-insensitive**. + +To receive all doors events : + +```json +low-can subscribe { "event" : "doors*" } +ON-REPLY 1:low-can/subscribe: {"jtype":"afb-reply","request":{"status":"success","uuid":"511c872e-d7f3-4f3b-89c2-aa9a3e9fbbdb"}} +``` + +Then you will receive an event each time a CAN message is decoded for the event +named _doors.driver.open_ with its received timestamp if available: + +```json +ON-EVENT low-can/messages.doors.driver.open({"event":"low-can\/messages.doors.driver.open","data":{"name":"messages.doors.driver.open","value":true, "timestamp": 1505812906020023},"jtype":"afb-event"}) +``` + +Notice that event shows you that the CAN event is named +_messages.doors.driver.open_ but you ask for event about +_doors.driver.open_. + +This is because all CAN messages or diagnostic messages are prefixed by the +JSON parent node name, **messages** for CAN messages and +**diagnostic\_messages** for diagnostic messages like OBD2. + +This will let you subscribe or unsubcribe to all signals at once, not +recommended, and better make filter on subscribe operation based upon their type. Examples: + +```json +low-can subscribe { "event" : "*speed*" } --> will subscribe to all messages with speed in their name. Search will be make without prefix for it. +low-can subscribe { "event" : "speed*" } --> will subscribe to all messages begin by speed in their name. Search will be make without prefix for it. +low-can subscribe { "event" : "messages*speed*" } --> will subscribe to all CAN messages with speed in their name. Search will be on prefixed messages here. +low-can subscribe { "event" : "messages*speed" } --> will subscribe to all CAN messages ending with speed in their name. Search will be on prefixed messages here. +low-can subscribe { "event" : "diagnostic*speed*" } --> will subscribe to all diagnostic messages with speed in their name. Search will be on prefixed messages here. +low-can subscribe { "event" : "diagnostic*speed" } --> will subscribe to all diagnostic messages ending with speed in their name. Search will be on prefixed messages here. +``` + +You can also subscribe to an event with the ID or the PGN of the message definition : + + +```json +low-can subscribe {"id" : 1568} +low-can subscribe {"pgn" : 61442} +``` + +And subscribe to all ID or PGN : + +```json +low-can subscribe {"id" : "*"} +low-can subscribe {"pgn" : "*"} +``` + + +You can stop receiving event from it by unsubscribe the signal the same way you did for subscribe + +```json +low-can unsubscribe { "event": "doors.driver.open" } +ON-REPLY 2:low-can/unsubscribe: {"jtype":"afb-reply","request":{"status":"success"}} +low-can unsubscribe { "event" : "doors*" } +ON-REPLY 3:low-can/unsubscribe: {"jtype":"afb-reply","request":{"status":"success"}} +``` + +### Filtering capabilities + +It is possible to limits received event notifications into minimum and maximum +boundaries as well as doing frequency thinning. This is possible using the +argument filter with one or more of the filters available : + +* frequency: specify in Hertz the frequency which will be used to getting + notified of new CAN events for the designated signal. If, during the blocked + time, further changed CAN messages are received, the last valid one will be + transferred after the lockout with a RX_CHANGED. +* min: Minimum value that the decoded value needs to be above to get pushed to + the subscribed client(s). +* max: Maximum value that the decoded value needs to be below to get pushed to + the subscribed client(s) +* rx_id : For the ISO TP protocol, define the id of source to write a message +* tx_id : For the ISO TP protocol, define the id of emitter to receive message + +Order doesn't matter neither the number of filters chosen, you can use one, two +or all of them at once. + +Usage examples : + +```json +low-can subscribe {"event": "messages.engine.speed", "filter": { "frequency": 3, "min": 1250, "max": 3500}} +low-can subscribe {"event": "messages.engine.load", "filter": { "min": 30, "max": 100}} +low-can subscribe {"event": "messages.vehicle.speed", "filter": { "frequency": 2}} +# ISOTP +low-can subscribe {"id": 273, "filter": {"tx_id" : 562}} +``` + +## Get last signal value and list of configured signals + +You can also ask for a particular signal value on one shot using **get** verb, like +this: + +```json +low-can get {"event": "messages.engine.speed"} +ON-REPLY 1:low-can/get: {"response":[{"event":"messages.engine.speed","value":0}],"jtype":"afb-reply","request":{"status":"success"}} +``` + +> **CAUTION** Only one event could be requested. + +Also, if you want to know the supported CAN signals loaded by **low-can**, use +verb **list** + +```json +low-can list +ON-REPLY 2:low-can/list: {"response":["messages.hvac.fan.speed","messages.hvac.temperature.left","messages.hvac.temperature.right","messages.hvac.temperature.average","messages.engine.speed","messages.fuel.level.low","messages.fuel.level","messages.vehicle.average.speed","messages.engine.oil.temp","messages.engine.oil.temp.high","messages.doors.boot.open","messages.doors.front_left.open","messages.doors.front_right.open","messages.doors.rear_left.open","messages.doors.rear_right.open","messages.windows.front_left.open","messages.windows.front_right.open","messages.windows.rear_left.open","messages.windows.rear_right.open","diagnostic_messages.engine.load","diagnostic_messages.engine.coolant.temperature","diagnostic_messages.fuel.pressure","diagnostic_messages.intake.manifold.pressure","diagnostic_messages.engine.speed","diagnostic_messages.vehicle.speed","diagnostic_messages.intake.air.temperature","diagnostic_messages.mass.airflow","diagnostic_messages.throttle.position","diagnostic_messages.running.time","diagnostic_messages.EGR.error","diagnostic_messages.fuel.level","diagnostic_messages.barometric.pressure","diagnostic_messages.ambient.air.temperature","diagnostic_messages.commanded.throttle.position","diagnostic_messages.ethanol.fuel.percentage","diagnostic_messages.accelerator.pedal.position","diagnostic_messages.hybrid.battery-pack.remaining.life","diagnostic_messages.engine.oil.temperature","diagnostic_messages.engine.fuel.rate","diagnostic_messages.engine.torque"],"jtype":"afb-reply","request":{"status":"success","uuid":"32df712a-c7fa-4d58-b70b-06a87f03566b"}} +``` + +## Write on CAN buses + +Two modes could be used for that which is either specifying the CAN bus and a +*RAW* CAN message either by specifying a defined signal, **case-insensitively**, +and its value. + +Examples: + +```json +# Authentification +low-can auth +# Write a raw can frame to the CAN id 0x620 +low-can write { "bus_name": "hs", "frame": { "can_id": 1568, "can_dlc": 8, "can_data": [ 255,255,255,255,255,255,255,255]} } +# Write a signal's value. +low-can write { "signal_name": "engine.speed", "signal_value": 1256} +# Write J1939 'single frame' +low-can write { "bus_name": "j1939", "frame": { "pgn": 61442, "length":8, "data": [ 255,255,255,255,255,255,255,255]} } +# Write J1939 'multi frame' +low-can write { "bus_name": "j1939", "frame": { "pgn": 61442, "length":9, "data": [ 255,255,255,255,255,255,255,255,254]} } +# Write ISOTP 'single frame' +low-can write {"bus_name": "hs", "filter": {"rx_id" : 562}, "frame": { "can_id": 273, "can_dlc": 8, "can_data": [ 255,255,255,255,255,255,255,255]} } +# Write ISOTP 'multi frame' +low-can write {"bus_name": "hs", "filter": {"rx_id" : 562}, "frame": { "can_id": 273, "can_dlc": 9, "can_data": [ 255,255,255,255,255,255,255,255,25]} } +``` + +To be able to use write capability, you need to add the permission + ```urn:AGL:permission::platform:can:write``` to your package configuration + file that need to write on CAN bus through **low-can** api. + +Then in order to write on bus, your app needs to call verb **auth** +before calling **write**, to raise its **LOA**, Level Of Assurance, +which controls usage of verb **write**. + +## Using CAN utils to monitor CAN activity + +You can watch CAN traffic and send custom CAN messages using can-utils +preinstalled on AGL target. + +To watch watch going on a CAN bus use: + +```bash +candump can0 +``` + +Or for an USB CAN adapter connected to porter board: + +```bash +candump can1 +``` + +Send a custom message: + +```bash +cansend can0 ID#DDDDAAAATTTTAAAA +``` + +You can also replay a previously dumped CAN logfiles. These logfiles can be +found in _can_samples_ directory under Git repository. Following examples use +a real trip from an Auris Toyota car. + +Trace has been recorded from a CAN device `can0` so you have to map it to the +correct one you use for your tests. + +Replay on a virtual CAN device `vcan0`: + +```bash +canplayer -I trip_test_with_obd2_vehicle_speed_requests vcan0=can0 +``` + +Replay on a CAN device `can0`: + +```bash +canplayer -I trip_test_with_obd2_vehicle_speed_requests can0 +``` + +Replay on a CAN device `can1` (porter by example): + +```bash +canplayer -I trip_test_with_obd2_vehicle_speed_requests can1=can0 +``` diff --git a/docs/api-services-book.yml b/docs/api-services-book.yml index 225d67b8..f2909c3c 100644 --- a/docs/api-services-book.yml +++ b/docs/api-services-book.yml @@ -14,5 +14,7 @@ books: name: Installation Guide - url: 3-Installation-J1939.md name: Installation Guide for J1939 - - url: 4-Usage.md + - url: 4-Installation-ISOTP.md + name: Installation Guide for ISOTP + - url: 5-Usage.md name: Usage Guide diff --git a/low-can-binding/binding/low-can-cb.cpp b/low-can-binding/binding/low-can-cb.cpp index 5879b686..fd57867c 100644 --- a/low-can-binding/binding/low-can-cb.cpp +++ b/low-can-binding/binding/low-can-cb.cpp @@ -297,6 +297,7 @@ static int subscribe_unsubscribe_signals(afb_req_t request, /// @param[in] afb_req request : contains original request use to subscribe or unsubscribe /// @param[in] subscribe boolean value, which chooses between a subscription operation or an unsubscription /// @param[in] signals - struct containing vectors with signal_t and diagnostic_messages to subscribe +/// @param[in] event_filter - stuct containing filter on the signal /// /// @return Number of correctly subscribed signal /// diff --git a/low-can-binding/binding/low-can-subscription.cpp b/low-can-binding/binding/low-can-subscription.cpp index df5a8f4d..a472d997 100644 --- a/low-can-binding/binding/low-can-subscription.cpp +++ b/low-can-binding/binding/low-can-subscription.cpp @@ -124,21 +124,44 @@ int low_can_subscription_t::unsubscribe(afb_req_t request) return afb_req_unsubscribe(request, event_); } +/** + * @brief Getter of index of subscription + * + * @return int Index + */ int low_can_subscription_t::get_index() const { return index_; } +/** + * @brief Getter of signal of subscription + * + * @return const std::shared_ptr A shared pointer of the signal + */ const std::shared_ptr low_can_subscription_t::get_signal() const { return signal_; } +/** + * @brief Check if the signal and event are the same that the subscription + * + * @param signal the signal compared + * @param event_filter the event_filter compared + * @return true if they are equal + * @return false if they are not equal + */ bool low_can_subscription_t::is_signal_subscription_corresponding(const std::shared_ptr signal, const struct event_filter_t& event_filter) const { return signal_ == signal && event_filter_ == event_filter; } +/** + * @brief Getter for diagnostic messages of subscription + * + * @return const vector_ptr_diag_msg_t Vector of pointer of diagnostic message + */ const vect_ptr_diag_msg_t low_can_subscription_t::get_diagnostic_message() const { return diagnostic_message_; @@ -201,66 +224,131 @@ const std::string low_can_subscription_t::get_name(uint32_t pid) const return ""; } +/** + * @brief Getter of the frequency of the event_filter + * + * @return float The frequency + */ float low_can_subscription_t::get_frequency() const { return event_filter_.frequency; } +/** + * @brief Getter of the min of the event_filter + * + * @return float The min value filtered + */ float low_can_subscription_t::get_min() const { return event_filter_.min; } +/** + * @brief Getter of the max of the event_filter + * + * @return float The max value filtered + */ float low_can_subscription_t::get_max() const { return event_filter_.max; } +/** + * @brief Getter of the rx_id of the event_filter + * + * @return canid_t The rx_id value + */ canid_t low_can_subscription_t::get_rx_id() const { return event_filter_.rx_id; } +/** + * @brief Getter of the tx_id of the event_filter + * + * @return canid_t The tx_id value + */ canid_t low_can_subscription_t::get_tx_id() const { return event_filter_.tx_id; } +/** + * @brief Getter of the socket of the subscription + * + * @return std::shared_ptr Pointer of the socket object + */ std::shared_ptr low_can_subscription_t::get_socket() { return socket_; } +/** + * @brief Setter for the frequency of the event_filter + * + * @param freq The new frequency + */ void low_can_subscription_t::set_frequency(float freq) { event_filter_.frequency = freq; } +/** + * @brief Setter for the min of the event_filter + * + * @param min The new min + */ void low_can_subscription_t::set_min(float min) { event_filter_.min = min; } +/** + * @brief Setter for the max of the event_filter + * + * @param max The new max + */ void low_can_subscription_t::set_max(float max) { event_filter_.max = max; } +/** + * @brief Setter for the rx_id of the event_filter + * + * @param rx_id The new rx_id + */ void low_can_subscription_t::set_rx_id(canid_t rx_id) { event_filter_.rx_id = rx_id; } +/** + * @brief Setter for the tx_id of the event_filter + * + * @param tx_id The new tx_id + */ void low_can_subscription_t::set_tx_id(canid_t tx_id) { event_filter_.tx_id = tx_id; } +/** + * @brief Setter for the index of the subscription + * + * @param index The new index + */ void low_can_subscription_t::set_index(int index) { index_ = index; } +/** + * @brief Setter for the signal of the subscription + * + * @param signal The new signal + */ void low_can_subscription_t::set_signal(std::shared_ptr signal) { signal_ = signal; @@ -421,6 +509,13 @@ void low_can_subscription_t::remove_last_bcm_frame(struct bcm_msg& bcm_msg) } #ifdef USE_FEATURE_J1939 +/** + * @brief Create a j1939 socket to read message + * + * @param subscription The subscription + * @param sig The signal subscribed + * @return int 0 if ok else -1 + */ int low_can_subscription_t::create_rx_filter_j1939(low_can_subscription_t &subscription, std::shared_ptr sig) { subscription.signal_= sig; @@ -434,6 +529,13 @@ int low_can_subscription_t::create_rx_filter_j1939(low_can_subscription_t &subsc } #endif +/** + * @brief Create an iso tp socket to read message + * + * @param subscription The subscription + * @param sig The signal subscribed + * @return int 0 if ok else -1 + */ int low_can_subscription_t::create_rx_filter_isotp(low_can_subscription_t &subscription, std::shared_ptr sig) { subscription.signal_= sig; @@ -531,6 +633,13 @@ int low_can_subscription_t::create_rx_filter_can(low_can_subscription_t &subscri return create_rx_filter_bcm(subscription, bcm_msg); } +/** + * @brief Create the good socket to read message + * depending on the signal + * + * @param sig The signal subscribed + * @return 0 if ok else -1 + */ int low_can_subscription_t::create_rx_filter(std::shared_ptr sig) { if(!sig->get_message()->is_isotp() && !sig->get_message()->is_j1939()) @@ -666,6 +775,14 @@ int low_can_subscription_t::tx_send(low_can_subscription_t &subscription, messag } #ifdef USE_FEATURE_J1939 +/** + * @brief Allows to open socket j1939 and send j1939 messsage + * + * @param subscription The subscription + * @param message The j1939 message to send + * @param bus_name The bus name where to send message + * @return int 0 if ok else -1 + */ int low_can_subscription_t::j1939_send(low_can_subscription_t &subscription, message_t *message, const std::string& bus_name) { //struct bcm_msg bcm_msg = subscription.make_bcm_head(TX_SEND, cfd.can_id); @@ -689,6 +806,14 @@ int low_can_subscription_t::j1939_send(low_can_subscription_t &subscription, mes #endif +/** + * @brief Allows to open socket isotp and send can messsage + * + * @param subscription The subscription + * @param message The can message to send + * @param bus_name The bus name where to send message + * @return int 0 if ok else -1 + */ int low_can_subscription_t::isotp_send(low_can_subscription_t &subscription, message_t *message, const std::string& bus_name) { //struct bcm_msg bcm_msg = subscription.make_bcm_head(TX_SEND, cfd.can_id); diff --git a/low-can-binding/binding/low-can-subscription.hpp b/low-can-binding/binding/low-can-subscription.hpp index 5abf8550..ac08d265 100644 --- a/low-can-binding/binding/low-can-subscription.hpp +++ b/low-can-binding/binding/low-can-subscription.hpp @@ -35,8 +35,8 @@ struct event_filter_t float frequency; ///< frequency - Maximum frequency which will be received and pushed to a subscribed event. float min; ///< min - Minimum value that the signal doesn't have to go below to be pushed. float max; ///< max - Maximum value that the signal doesn't have to go above to be pushed. - canid_t rx_id; - canid_t tx_id; + canid_t rx_id; ///< rx_id - RX_ID for ISO_TP protocol + canid_t tx_id; ///< tx_id - TX_ID for ISO_TP protocol event_filter_t() : frequency{0}, min{-__FLT_MAX__}, max{__FLT_MAX__}, rx_id{NO_CAN_ID}, tx_id{NO_CAN_ID} {}; bool operator==(const event_filter_t& ext) const { diff --git a/low-can-binding/utils/socketcan-j1939.cpp b/low-can-binding/utils/socketcan-j1939.cpp deleted file mode 100644 index a7938d61..00000000 --- a/low-can-binding/utils/socketcan-j1939.cpp +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2018, 2019 "IoT.bzh" - * Author "Arthur Guyader" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include "./socketcan-j1939.hpp" -#include "../binding/application.hpp" - -namespace utils -{ - /// @brief Connect the socket. - /// @return 0 if success. - int socketcan_j1939_t::connect(const struct sockaddr* addr, socklen_t len) - { - return socket_ != INVALID_SOCKET ? ::connect(socket_, addr, len) : 0; - } - - /// @brief Bind the socket. - /// @return 0 if success. - int socketcan_j1939_t::bind(const struct sockaddr* addr, socklen_t len) - { - return socket_ != INVALID_SOCKET ? ::bind(socket_, addr, len) : 0; - } - - void socketcan_j1939_t::filter(name_t name, pgn_t pgn, uint8_t addr, uint64_t name_mask, uint32_t pgn_mask, uint8_t addr_mask) - { - int filter_on = 0; - struct j1939_filter filter; - memset(&filter, 0, sizeof(filter)); - if (name) - { - filter.name = name; - if(name_mask) - { - filter.name_mask = name_mask; - } else - { - filter.name_mask = ~0ULL; - } - ++filter_on; - } - - if (addr < 0xff) - { - filter.addr = addr; - if(addr_mask) - { - filter.addr_mask = addr_mask; - } else - { - filter.addr_mask = ~0; - } - ++filter_on; - } - if (pgn <= J1939_PGN_MAX) - { - filter.pgn = pgn; - if(pgn_mask) - { - filter.pgn_mask = pgn_mask; - } else - { - filter.pgn_mask = ~0; - } - ++filter_on; - } - if(filter_on) - { - setopt(SOL_CAN_J1939, SO_J1939_FILTER, &filter, sizeof(filter)); - } - } - - int socketcan_j1939_t::open(std::string device_name) - { - return open(device_name,0,0,0); - } - - int socketcan_j1939_t::open(std::string device_name, name_t name, pgn_t pgn, uint8_t addr) - { - - struct ifreq ifr; - socket_ = socketcan_t::open(PF_CAN, SOCK_DGRAM, CAN_J1939); - - // Attempts to open a socket to CAN bus - ::strcpy(ifr.ifr_name, device_name.c_str()); - AFB_DEBUG("ifr_name is : %s", ifr.ifr_name); - if(::ioctl(socket_, SIOCGIFINDEX, &ifr) < 0) - { - AFB_ERROR("ioctl failed. Error was : %s", strerror(errno)); - close(); - } - else - { - tx_address_.can_family = AF_CAN; - tx_address_.can_ifindex = ifr.ifr_ifindex; - - if(addr <= 0 || addr >= UINT8_MAX ) - { - tx_address_.can_addr.j1939.addr = J1939_NO_ADDR; - } - else - { - tx_address_.can_addr.j1939.addr = addr; - } - - if(name <= 0 || name >= UINT64_MAX ) - { - tx_address_.can_addr.j1939.name = J1939_NO_NAME; - } - else - { - tx_address_.can_addr.j1939.name = name; - } - - if(pgn <= 0 || pgn > J1939_PGN_MAX) - { - tx_address_.can_addr.j1939.pgn = J1939_NO_PGN; - } - else - { - tx_address_.can_addr.j1939.pgn = pgn; - } - - if(bind((struct sockaddr *)&tx_address_, sizeof(tx_address_)) < 0) - { - AFB_ERROR("Bind failed. %s", strerror(errno)); - close(); - } - else - { - int promisc = 1; // 0 = disabled (default), 1 = enabled - setopt(SOL_CAN_J1939, SO_J1939_PROMISC, &promisc, sizeof(promisc)); - int recv_own_msgs = 1; // 0 = disabled (default), 1 = enabled - setopt(SOL_CAN_J1939, SO_J1939_RECV_OWN, &recv_own_msgs, sizeof(recv_own_msgs)); - if(tx_address_.can_addr.j1939.pgn != J1939_NO_PGN) - { - filter(J1939_NO_NAME,tx_address_.can_addr.j1939.pgn,J1939_NO_ADDR); - } - } - } - return socket_; - } - - std::shared_ptr socketcan_j1939_t::read_message() - { - std::shared_ptr jm = std::make_shared(); - uint8_t data[128] = {0}; - - struct sockaddr_can addr; - socklen_t addrlen = sizeof(addr); - struct ifreq ifr; - - ssize_t nbytes = ::recvfrom(socket(), &data, sizeof(data), 0, (struct sockaddr*)&addr, &addrlen); - ifr.ifr_ifindex = get_tx_address().can_ifindex; - ioctl(socket(), SIOCGIFNAME, &ifr); - - AFB_DEBUG("Data available: %i bytes read", (int)nbytes); - /*AFB_DEBUG("read: Found on bus %s:\n id: %X, length: %X, data %02X%02X%02X%02X%02X%02X%02X%02X", ifr.ifr_name, frame.can_id, frame.len, - data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);*/ - - struct timeval tv; - ioctl(socket(), SIOCGSTAMP, &tv); - uint64_t timestamp = 1000000 * tv.tv_sec + tv.tv_usec; - jm = j1939_message_t::convert_from_addr(addr, data , nbytes, timestamp); - jm->set_sub_id((int)socket()); - return jm; - } - - - void socketcan_j1939_t::write_message(std::vector>& vobj) - { - AFB_WARNING("Not implemented"); - } - - void socketcan_j1939_t::write_message(std::shared_ptr cm) - { - AFB_WARNING("Not implemented"); - } - -} -- cgit 1.2.3-korg