Merge can-echo-server:use-custom-implementation into can-echo-server:master

Proposed by Jonathan Cave
Status: Merged
Approved by: Jonathan Cave
Approved revision: 5a202b07c8e5b18584fa03f2cf699b11b03d5693
Merged at revision: 5d3503f388b89c707b0d456f66d324f99eec0442
Proposed branch: can-echo-server:use-custom-implementation
Merge into: can-echo-server:master
Diff against target: 224 lines (+174/-6)
4 files modified
snap/hooks/install (+1/-1)
snap/snapcraft.yaml (+5/-4)
src/can_echo_server.py (+163/-0)
src/can_echo_server.sh (+5/-1)
Reviewer Review Type Date Requested Status
Jonathan Cave Pending
Review via email: mp+352320@code.launchpad.net

Description of the change

candump does not seem to support sending FD packets when in bridge mode. Hence switching to own echo implementation.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/snap/hooks/install b/snap/hooks/install
2index 23f32f4..6f5b4f5 100755
3--- a/snap/hooks/install
4+++ b/snap/hooks/install
5@@ -1,4 +1,4 @@
6 #!/bin/bash
7
8 snapctl set interface=can0
9-snapctl set delay=2000000
10+snapctl set delay=2
11diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
12index 8540afb..67a85ea 100644
13--- a/snap/snapcraft.yaml
14+++ b/snap/snapcraft.yaml
15@@ -1,21 +1,22 @@
16 name: can-echo-server
17-version: '0.1'
18+version: '0.2'
19 summary: Creates a deamon that echoes any CAN Packets received
20 description: |
21 The CAN packet is echoed unmodified to the same interface on which it was
22 received.
23
24 grade: stable
25-confinement: devmode
26+confinement: strict
27
28 apps:
29 can-echo-server:
30 command: bin/can_echo_server.sh
31 daemon: simple
32+ plugs:
33+ - can-bus
34+ - network-control
35
36 parts:
37- can-utils:
38- source-tag: v2018.02.0
39 can-echo-server:
40 plugin: dump
41 source: src/
42diff --git a/src/can_echo_server.py b/src/can_echo_server.py
43new file mode 100755
44index 0000000..9d224e8
45--- /dev/null
46+++ b/src/can_echo_server.py
47@@ -0,0 +1,163 @@
48+#!/usr/bin/env python3
49+# This file is part of Checkbox.
50+#
51+# Copyright 2018 Canonical Ltd.
52+# Written by:
53+# Jonathan Cave <jonathan.cave@canonical.com>
54+#
55+# Checkbox is free software: you can redistribute it and/or modify
56+# it under the terms of the GNU General Public License version 3,
57+# as published by the Free Software Foundation.
58+#
59+# Checkbox is distributed in the hope that it will be useful,
60+# but WITHOUT ANY WARRANTY; without even the implied warranty of
61+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
62+# GNU General Public License for more details.
63+#
64+# You should have received a copy of the GNU General Public License
65+# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
66+
67+import argparse
68+import os
69+import socket
70+import struct
71+import sys
72+import textwrap
73+import threading
74+import time
75+
76+
77+class CANSocket():
78+
79+ # struct module format strings for CAN packets
80+ # Normal format:
81+ # < little-endian
82+ # I unsigned int (4) : CAN-ID + EFF/RTR/ERR Flags
83+ # B unsigned char (1) : Data length
84+ # 3x padding (3 * 1) : -
85+ # 8s char array (8 * 1) : Data
86+ FORMAT = "<IB3x8s"
87+ # Flexible Data (FD) rate format:
88+ # < little-endian
89+ # I unsigned int (4) : CAN-ID + EFF/RTR/ERR Flags
90+ # B unsigned char (1) : Data length
91+ # B unsigned char (1) : FD Flags
92+ # 2x padding (2 * 1) : -
93+ # 64s char array (64 * 1) : Data
94+ FD_FORMAT = "<IBB2x64s"
95+
96+ CAN_MTU = struct.Struct(FORMAT).size
97+ CANFD_MTU = struct.Struct(FD_FORMAT).size
98+
99+ # Socket options from <linux/can/raw.h>
100+ CAN_RAW_FILTER = 1 # set 0 .. n can_filter(s)
101+ CAN_RAW_ERR_FILTER = 2 # set filter for error frames
102+ CAN_RAW_LOOPBACK = 3 # local loopback (default:on)
103+ CAN_RAW_RECV_OWN_MSGS = 4 # receive my own msgs (default:off)
104+ CAN_RAW_FD_FRAMES = 5 # allow CAN FD frames (default:off)
105+ CAN_RAW_JOIN_FILTERS = 6 # all filters must match to trigger
106+
107+ def __init__(self, interface=None, fdmode=False, loopback=True):
108+ self.sock = socket.socket(socket.PF_CAN, # protocol family
109+ socket.SOCK_RAW,
110+ socket.CAN_RAW)
111+ self._fdmode = fdmode
112+ self._loopback = loopback
113+ if interface is not None:
114+ self._bind(interface)
115+
116+ def __enter__(self):
117+ return self
118+
119+ def __exit__(self, *args):
120+ self.close()
121+
122+ def close(self):
123+ self.sock.close()
124+
125+ def _bind(self, interface):
126+ self.sock.bind((interface,))
127+ if self._fdmode: # default is off
128+ self.sock.setsockopt(socket.SOL_CAN_RAW, self.CAN_RAW_FD_FRAMES, 1)
129+ if not self._loopback: # default is on
130+ self.sock.setsockopt(socket.SOL_CAN_RAW, self.CAN_RAW_LOOPBACK, 0)
131+
132+ def send(self, can_id, data, id_flags=0, fd_flags=0):
133+ can_id = can_id | id_flags
134+ if self._fdmode:
135+ can_pkt = struct.pack(self.FD_FORMAT, can_id, len(data), fd_flags,
136+ data)
137+ else:
138+ can_pkt = struct.pack(self.FORMAT, can_id, len(data), data)
139+ self.sock.send(can_pkt)
140+
141+ def recv(self):
142+ can_pkt = self.sock.recv(self.CANFD_MTU)
143+ nbytes = len(can_pkt)
144+ if nbytes == self.CANFD_MTU:
145+ can_id, length, fd_flags, data = struct.unpack(self.FD_FORMAT,
146+ can_pkt)
147+ else:
148+ can_id, length, data = struct.unpack(self.FORMAT, can_pkt)
149+ can_id &= socket.CAN_EFF_MASK
150+ return (can_id, data[:length])
151+
152+
153+def echo_server(args):
154+ while True:
155+ recv_id_i = None
156+ recv_data_b = None
157+
158+ print('Opening read socket on {}'.format(args.interface))
159+ with CANSocket(args.interface, fdmode=True,
160+ loopback=False) as recv_s:
161+ recv_id_i, recv_data_b = recv_s.recv()
162+
163+ print('Received packet')
164+ print(' ID : {:x}'.format(recv_id_i))
165+ print(' Data: {}'.format(recv_data_b.hex()))
166+
167+ time.sleep(args.delay)
168+
169+ if recv_id_i > 2047:
170+ id_flags = socket.CAN_EFF_FLAG
171+ else:
172+ id_flags = 0
173+
174+ if len(recv_data_b) == 64:
175+ fdmode = True
176+ else:
177+ fdmode = False
178+
179+ print('Opening send socket on {}'.format(args.interface))
180+ # Open socket, will raise OSError on failure
181+ with CANSocket(args.interface, fdmode=fdmode,
182+ loopback=False) as send_s:
183+ print('Sending data...', flush=True)
184+ try:
185+ send_s.send(recv_id_i, recv_data_b, id_flags=id_flags)
186+ except OSError as e:
187+ print(e, file=sys.stderr)
188+ if e.errno == 90:
189+ raise SystemExit(
190+ 'ERROR: interface does not support FD Mode')
191+ else:
192+ raise SystemExit('ERROR: OSError on attempt to send')
193+
194+
195+def main():
196+ parser = argparse.ArgumentParser(
197+ formatter_class=argparse.RawDescriptionHelpFormatter,
198+ description='CAN Packet Echoer')
199+ parser.add_argument('interface', type=str,
200+ help='Interface name e.g. can0')
201+ parser.add_argument('delay', type=int,
202+ help='Delay before echoing packets in seconds')
203+ parser.set_defaults(func=echo_server)
204+
205+ args = parser.parse_args()
206+ args.func(args)
207+
208+
209+if __name__ == "__main__":
210+ main()
211diff --git a/src/can_echo_server.sh b/src/can_echo_server.sh
212index 48ee03e..c441d15 100755
213--- a/src/can_echo_server.sh
214+++ b/src/can_echo_server.sh
215@@ -3,4 +3,8 @@
216 INTERFACE=$(snapctl get interface)
217 DELAY=$(snapctl get delay)
218
219-candump "$INTERFACE" -B "$INTERFACE" -u "$DELAY"
220+ip link set "$INTERFACE" down
221+ip link set "$INTERFACE" type can bitrate 1000000 dbitrate 2000000 fd on
222+ip link set "$INTERFACE" up
223+
224+ $SNAP/bin/can_echo_server.py "$INTERFACE" "$DELAY"

Subscribers

People subscribed via source and target branches

to all changes: