Closed josemic closed 4 years ago
I assume your first call should be:
var SocketCAN_j1939_send_data_to_result send_data_to_result
var J1939_hdr v_j1939_destination := {
name := omit,
pgn := J1939_PGN_REQUEST,
addr:= J1939_NO_ADDR}
send_data_to_result := f_j1939_send_data_to(
v_socket_id,
v_ifr.if_index,
v_j1939_destination,
{ 0, 0xee, 0, })
Did you get some kernel log on this request? It would say some thing starting with "tx address claim wit ...", it should be a hint, what exactly went wrong.
Thanks for the hint. The kernel log shows:
Dec 11 18:42:56 michael-ThinkPad-T430 kernel: [ 2221.739290] vcan0: tx address claim with different name
When now turning the order of bytes p_j1939_name_source in the sendto function around to '8877665544332211'O compared to how it was used in the bind command '1122334455667788'O. It works and candump shows:
vcan0 18EEFFFE [8] 88 77 66 55 44 33 22 11
Which is according to my understanding the Cannot Claim Source Address command.
The type of your c_j1939_name_source
source is probably wrong. It's not an octet string it's the name in little endian.
Not sure if I understand you right. For address claim, the name is used in the bind command as big endian and in the sentto command as little endian. However the sentto command is usually used to then the PDU, which is an octetstring. That is why I have in later versions changed the sentto as following:
var J1939_NAME v_j1939_name_source_reverse :=
p_j1939_name_source[7]& p_j1939_name_source[6]& p_j1939_name_source[5]&
p_j1939_name_source[4]& p_j1939_name_source[3]& p_j1939_name_source[2]&
p_j1939_name_source[1]& p_j1939_name_source[0]
send_data_to_result := f_j1939_send_data_to(
v_socket_id,
v_ifr.if_index,
v_j1939_destination,
v_j1939_name_source_reverse /*p_pdu_send*/)
Not sure if I understand you right. For address claim, the name is used in the
bind()
command as big endian
No, the name
in the bind()
is host native endianess. It's addressing information you pass to the kernel. So it's native.
and in the
sentto()
command as little endian.
It's data you send over the network. Look into the j1939 specs for address claiming how the message should look like.
However the
sentto()
command is usually used to then the PDU, which is an octetstring. That is why I have in later versions changed the sentto as following:
On the lower level it's an octet string you send over the net, but how do you encode a 8 byte value into a series of 8 single bytes? It's defined by the j1939 standard to be little endian for the address claiming message.
Regarding your example code, what you call reverse
is usually called little endian. I'm sure that TTCN-3 has a proper way to do this.
No the name in the bind() is host native endianess. It's addressing information you pass to the kernel. So it's native.
The TTCN-3 has message encoding in little and big endian (encoding (msb/lsb)), however this applies only to messages. If there is a native encoding, we have to do it in the test port. This is the way the test port for bind looks like: Note sure what you mean. This is the way the bind code looks like.
addr.can_ifindex = bindu.j1939().if__index();
if (bindu.j1939().j1939__source().addr().is_present()) {
const OPTIONAL<J1939::J1939__ADDR> &addr_ =
bindu.j1939().j1939__source().addr();
addr.can_addr.j1939.addr = oct2int(addr_);
}
if (bindu.j1939().j1939__source().name().is_present()) {
const OPTIONAL<J1939::J1939__NAME> &name =
bindu.j1939().j1939__source().name();
addr.can_addr.j1939.name = (__u64) oct2int(name).get_long_long_val();
}
if (bindu.j1939().j1939__source().pgn().is_present()) {
const OPTIONAL<J1939::J1939__PGN>& pgn =
bindu.j1939().j1939__source().pgn();
addr.can_addr.j1939.pgn = oct2int(pgn);
}
log("SocketCAN: Binding socket J1939 to addr %x, name %llx, pgn %x",
addr.can_addr.j1939.addr, addr.can_addr.j1939.name, addr.can_addr.j1939.pgn);
One question that comes to my mind is that test suites have to be rewritten if the endianess of the kernel changes (intel vs. arm processor used)?
and in the sentto() command as little endian.
It's data you send over the network. Look into the j1939 specs for address claiming how the message should look like.
I do not have access to the j1939 specification. I have only the book from Wilfied Voss "A comprehensible Guide to J1939".
Here is the way the sendto looks like in the test port:
SocketCAN__Types::SocketCAN__j1939__send__data__to__result result;
int sock;
int cn = send_par.id();
if ((cn < sock_list_length)) {
struct sockaddr_can destaddr;
long nrOfBytesSent, nrOfBytestoSend, nrOfBytesRemaining;
sock = sock_list[cn].fd;
destaddr.can_ifindex = send_par.if__index();
destaddr.can_family = AF_CAN;
destaddr.can_addr.j1939.addr = J1939_NO_ADDR;
destaddr.can_addr.j1939.name = J1939_NO_NAME;
destaddr.can_addr.j1939.pgn = J1939_NO_PGN;
if (send_par.j1939__destination().addr().is_present()) {
const OPTIONAL<J1939::J1939__ADDR> &addr =
send_par.j1939__destination().addr();
destaddr.can_addr.j1939.addr = oct2int(addr);
}
if (send_par.j1939__destination().name().is_present()) {
const OPTIONAL<J1939::J1939__NAME> &name =
send_par.j1939__destination().name();
//destaddr.can_addr.j1939.name = oct2int(name);
destaddr.can_addr.j1939.name = (__u64) oct2int(name).get_long_long_val();
}
if (send_par.j1939__destination().pgn().is_present()) {
const OPTIONAL<J1939::J1939__PGN> &pgn =
send_par.j1939__destination().pgn();
destaddr.can_addr.j1939.pgn = oct2int(pgn);
}
log("SocketCAN: Sending J1939 message to addr %x, name %llx, pgn %x",
destaddr.can_addr.j1939.addr, destaddr.can_addr.j1939.name,
destaddr.can_addr.j1939.pgn);
//logOctet("containing data: ", send_par.pdu());
nrOfBytestoSend = send_par.pdu().lengthof();
nrOfBytesRemaining = nrOfBytestoSend;
do {
nrOfBytesSent = sendto(sock, (const void*)((const unsigned char*)send_par.pdu()+nrOfBytestoSend-nrOfBytesRemaining),
nrOfBytesRemaining, 0, (struct sockaddr*) &destaddr, sizeof(destaddr));
nrOfBytesRemaining = nrOfBytesRemaining - nrOfBytesSent;
} while ((nrOfBytesSent >= 0) and (nrOfBytesRemaining != 0));
log(
"Sent J1939 message with send of size %d to addr %x, name %llx, pgn %x",
nrOfBytesSent, destaddr.can_addr.j1939.addr,
destaddr.can_addr.j1939.name, destaddr.can_addr.j1939.pgn);
Do you mean that I have to turn around all bytes from the octetstring before before sending in the test port?
(const void*)((const unsigned char*)send_par.pdu()+nrOfBytestoSend-nrOfBytesRemaining)
So if I understand you right I do not have to reverse in TTCN-3 code, but I have always to send the pdu in little endian in TTCN-3 test port?
addr.can_addr.j1939.name = (__u64) oct2int(name).get_long_long_val();
I'm not familiar with TTCN-3, but I assume this produces a native uint64_t
. So the tests don't have to be re-written for BE system. Although we haven't tested the j1939 stack on BE yet. However this looks wrong. What's the data type of name
here? Is it possible to define a uint64_t
equivalent in TTCN-3?
BTW: Your write loop misses error handling.
Do you mean that I have to turn around all bytes from the octetstring before before sending in the test port?
No.
So if I understand you right I do not have to reverse in TTCN-3 code, but I have always to send the pdu in little endian in TTCN-3 test port?
No.
The sendto()
takes an octet string. According to the standard the name
is send as a little endian encoded over the network.
See Table 3. Structure of the Name. Compare to this graphical representation of the j1939 name, where the MSB is on the left and the LSB on the right.
This means first byte of the data in the address claim message is the LSB of the identity number, which is the LSB of the name
.
When you write a uint64_t
in C
(uint64_t foo = 0x1122334455667788;
) the LSB is 0x88
and the MSB is 0x11
.
When you send an octet string, e.g. '1122334455667788'O
byte wise it's:
11
22
33
44
55
66
77
88
. An octet string is just an arbitrary number of bytes (octets) grouped together.
If you are on a little endian system and have a uint64_t foo=0x1122334455667788
and send it byte wise it's:
88
77
66
55
44
33
22
11
contrary the same uint64_t
on a big endian system send byte wise:
11
22
33
44
55
66
77
88
.
This means your data type for name is wrong. It must be something similar to an uint64_t
not a group of 8 single bytes, which is called octet string in TTCN-3. When generating the address claim message you have to convert the native name
into LE representation and treat this as an octet string.
BTW: Your write loop misses error handling. The error handling for sentto with the return value nrOfBytesSent < 0 is written below the code shown (not shown here).
addr.can_addr.j1939.name = (__u64) oct2int(name).get_long_long_val(); However this looks wrong. What's the data type of name here?
name is of data type __u64
addr is defined as following:
struct sockaddr_can addr = { };
where sockaddr_can is defined in can.h:
struct sockaddr_can {
__kernel_sa_family_t can_family;
int can_ifindex;
union {
/* transport protocol class address information (e.g. ISOTP) */
struct { canid_t rx_id, tx_id; } tp;
/* J1939 address information */
struct {
/* 8 byte name when using dynamic addressing */
__u64 name;
/* pgn:
* 8 bit: PS in PDU2 case, else 0
* 8 bit: PF
* 1 bit: DP
* 1 bit: reserved
*/
__u32 pgn;
/* 1 byte address */
__u8 addr;
} j1939;
/* reserved for future CAN protocols address information */
} can_addr;
};
I made a test:
In Terminal 1:
Sending with TTCN (were the with sendto the reveresed c_j1939_name_source is send:
const J1939_NAME c_j1939_name_source := '1122334455667788'O
In Terminal 2:
./candump -t d vcan0
(000.000000) vcan0 18EEFF80 [8] 88 77 66 55 44 33 22 11
and with jacd:
In Terminal 3:
./jacd -r 100,80-120 -c /tmp/1122334455667780.jacd 1122334455667788 vcan0
In Termianl 4:
./candump -t d vcan0
(000.000000) vcan0 18EAFFFE [3] 00 EE 00
(001.250174) vcan0 18EEFF50 [8] 88 77 66 55 44 33 22 11
Thus the bind looks for intel PCs ok (I do not completly understand why), however the sendto needs to convert name of type J1939_NAME
to little endian before sending as octetstring.
This should be handled seamlessly by TTCN after declaring the J1939_NAME type as little endian.
The question is, what's the datatype of the name
in the TTCN-3 universe here?
oct2int(name).get_long_long_val()
As said earlier, the name
in bind()
is in native endianess, while the name in the data of the sendto()
has to be converted to little endian.
j1939.hh:
namespace J1939 {
typedef OCTETSTRING J1939__NAME;
..
}
It is the C++ OCTETSTRING class.
Octetstring for the j1939 name is probably wrong. It should be a native unsigned 64 bit value.
Ericsson confirmed that oct2int(name).get_long_long_val() is a native 64-bit unsigned integer.
The titan TTCN-3 test port titan.TestPorts.SocketCANasp with support for J1939 had been released here: Titan test ports
For J1939 support see the directory doc/Readme.md
This issue is solved. It can be closed.
@marckleinebudde: The Titan SocketCAN test port had made much progress. Here is found an introduction: Getting Started with the SocketCAN J1939 Test Port on the example of Isobus https://www.eclipse.org/forums/index.php/t/1104980/
I am doing an address claim as described in j1939.rst
However I get an protocol error. The TTCN code looks as following:
The log file is attached. log_merged.txt