CAN-Report is the CAN analyzing tool of port. It provides plugins for higher layer protocols CanOpen, DeviceNet and SaeJ1939. It also has an interface for user applications using the TclTk scripting language. Thus it serves as a multifunctional tool.
Receive a predefined number of messages in every supported bitrate. To switch bitrates some magic has to be present in your device. For example you could send a message with id 0x1 and the new bitrate as data content.
# application.tcl # # write text into main window InsertText? {} "# application.tcl loaded #" stdout #This procedure is called by the ResetTime? button in the user window. proc resettime {} { # not needed } #This procedure is called by the Help item in the Help menu. proc do-help.usr {} { InsertText? .usr "# No help available." stderr } # # application -- # # called everytime a message is received # # Arguments: # msg - string from horch server # # Result: # proc application { msg } { # get id set id [get_can_id? $msg] # data length code set dlc [get_dlc? $msg] # get data set data [get_data? $msg] if {$id == $::data(rx,id)} { # count number of received messages incr ::data($::data(curBitrate),msgCnt) # all messages received ? if {$::data($::data(curBitrate),msgCnt) == $::data(rx,maxMsg) } { # write text in user window InsertText? .usr "Test complete" set ::data(testState) "Ok" } else { # do whatever is needed } } } # # digi.init -- # # initialize internal variables # # Arguments: # # Result: # # proc digi.init {} { foreach bitrate [list 10 20 50 100 125 250 500 800 1000] { set ::data($bitrate,msgCnt) 0 } set ::data(rx,id) 0x100 set ::data(rx,maxMsg) 1000 set ::data(tx,id) 0x101 set ::data(tx,maxMsg) 1000 set ::data(timeout) 5000 } # # digi.startTest # # run tests # # Arguments: # # Result: # # proc digi.startTest {} { digi.init foreach bitrate [list 10 20 50 100 125 250 500 800 1000] { # set new bitrate and reset CAN horch "b$bitrate" horch "thR" set ::data(curBitrate) $bitrate set ::data(testState) "" InsertText? .usr "Start test at bitrate $::data(curBitrate) KBit/s" stdout # insert code to tell the device # to send its test sequence # in case not all messages are received # stop test after 5 seconds after $::data(timeout) {set ::data(testState) "timeout"} # wait for messages or timeout tkwait variable ::data(testState) if { $::data(testState) ne "Ok" } { InsertText? .usr "Failed." stderr } else { InsertText? .usr "Success." stdout } # some magic to switch to a new bitrate set b0 [expr {$bitrate >> 8}] set b1 [expr {$bitrate && 0xff}] wr 0x1 $b0 $b1 } InsertText? .usr "Test finished" stdout } digi.startTest
# set of values set values { 0 0 2 0 0 5 1 0 10 1 0 14 2 0 20 2 0 25 3 0 50 3 0 70 4 0 80 5 0 90 9 0 100 10 0 130 11 0 160 15 0 200 16 0 220 20 1 0 25 1 200 30 2 0 45 5 0 30 0 200 20 0 200 20 1 0 20 1 0 20 1 0 10 0 0 0 0 0 } # number of values set max [llength $values] set stop 0 set cnt 0 while { $stop == 0 } { # 3 values are sent in one CAN message with the id 0x183 wr 0x183 [lindex $values $cnt]\ [lindex $values [expr $cnt + 1]]\ [lindex $values [expr $cnt + 2]] incr cnt 3 # the following 3 lines are a non-blocking wait # for 500 ms set waitvar 0 after 500 [list set ::waitvar 1] vwait ::waitvar # start again at the beginning of the list if { $cnt >= $max } { set cnt 0 } }
proc listPdoCobIds {{start 0} {end 512}} { # read RPDO Cob-ID for { set i $start } { $i < $end } { incr i } { set index [expr 0x1400 + $i] puts "COB-ID RPDO $i: [format 0x%x [r $index 1 u32]]" } puts "" # read TPDO Cob-ID for { set i $start } { $i < $end } { incr i } { set index [expr 0x1800 + $i] puts "COB-ID TPDO $i: [format 0x%x [r $index 1 u32]]" } } proc listPdoMapping {{start 0} {end 512}} { # read RPDO mapping for { set i $start } { $i < $end } { incr i } { set index [expr 0x1600 + $i] set pdoMapCnt [r $index 0 u8] puts "RPDO $i" for {set j 1} { $j <= $pdoMapCnt } { incr j } { puts " Mapping $j: [format 0x%x [r $index $j u32]]" } } puts "" # read TPDO mapping for { set i $start } { $i < $end } { incr i } { set index [expr 0x1A00 + $i] set pdoMapCnt [r $index 0 u8] puts "TPDO $i" for {set j 1} { $j <= $pdoMapCnt } { incr j } { puts " Mapping $j: [format 0x%x [r $index $j u32]]" } } }
proc setNodeguarding { guardtime lifetime } { w 0x100c 0 u16 $guardtime w 0x100d 0 u8 $lifetime } proc setHeartbeat { hbttime } { w 0x1017 0 u16 $hbttime }
Works with CAN-REport 3.3 and later.
This example uses the on message command from the embbeded scripting language. Whenever a message with the given id is received the script is executed. Within the script there is the array this which contains the elements id, data and type.
A global variable co_state? is used to store the current NMT state of the device. This variable has to be set before any guarding message was received.
for {set i 1} { $i <127} { incr i } { set ::co_state?($i) 5 } on message 0x721 { set nid [expr $this(id) & 0x7f] set ::co_state?($nid) [expr $::co_state?($nid) ^ 0x80] wr $this(id) $::co_state?($nid)
Tested with CAN-REport 3.4.
This example makes use of the "User Plugin" of the CAN-REport.
# application.tcl # # # InsertText? {} "# application.tcl loaded #" stdout #This procedure is called by the ResetTime? button in the user window. proc resettime {} { InsertText? .usr "# this function is not implemented yet" stderr ;#reset internal time } #This procedure is called by the Help item in the Help menu. proc do-help.usr {} { InsertText? .usr "# No help available." stderr } variable _colors set _colors(nmtstate,bootup) blue set _colors(nmtstate,preop) orange set _colors(nmtstate,op) green set _colors(nmtstate,stopped) red set _colors(nmtcmd,resetappl) "" set _colors(nmtcmd,resetcomm) "" set _colors(nmtcmd,Bootup) "" set _colors(nmtcmd,Op) green4 set _colors(nmtcmd,Preop) orange3 set _colors(nmtcmd,Stopped) red4 set _colors(nmtcmd,resetappl) white set _colors(nmtcmd,resetcomm) yellow variable _nodes for {set i 1} {$i < 128} {incr i} { set _nodes($i,nmtstate) 0xff } proc application { msg } { variable _widgets variable _colors variable _nodes # get id set id [get_can_id? $msg] # data length code set dlc [get_dlc? $msg] # get data set data [get_data? $msg] if { ($id == 0) } { set cmd [lindex $data 0] set node [format %d "0x[lindex $data 1]"] set tag node$node switch $cmd { "80" { set color $_colors(nmtcmd,Preop) } "01" { set color $_colors(nmtcmd,Op) } "81" { set color $_colors(nmtcmd,resetappl) set _nodes($node,nmtstate) $cmd } "82" { set color $_colors(nmtcmd,resetcomm) InsertText? .usr $msg stdout set _nodes($node,nmtstate) $cmd } default { set color $_colors(nmtcmd,Stopped) } } set bbox [$_widgets(canvas) bbox $tag] $_widgets(canvas) create rect $bbox -fill $color -tag state$node $_widgets(canvas) raise $tag return } # puts $id # EMCY monitoring if { ($id == 0xff) } { set errCode "0x[lindex $data 1][lindex $data 0]" set node [format %d "0x[lindex $data 3]"] switch $errCode { "0xff80" { set index "0x[lindex $data 5][lindex $data 4]sub[lindex $data 6]" set ::emcyText "Configuration write failed for node $node: $index" } "0xff81" { set index "0x[lindex $data 5][lindex $data 4]sub[lindex $data 6]" set ::emcyText "Configuration check failed for node $node: $index" } "0xff82" { set ::emcyText "Configuration finished for node $node" } } return } # heartbeat monitoring if { ($id > 0x700) && ($id < 0x780)} { set node [expr {$id & 0xff}] set tag node$node set state [lindex $data 0] switch -- $state { "00" { set color $_colors(nmtstate,bootup) } "7f" { set color $_colors(nmtstate,preop) } "05" { set color $_colors(nmtstate,op) } default { set color $_colors(nmtstate,stopped) } } # update only on state change if { $_nodes($node,nmtstate) ne $state } { set _nodes($node,nmtstate) $state set bbox [$_widgets(canvas) bbox $tag] $_widgets(canvas) create rect $bbox -fill $color -tag state$node $_widgets(canvas) raise $tag } return } } proc initUI {} { variable _widgets variable _colors set w .desy toplevel $w set lf [labelframe $w.f -text " NMT States "] set c $lf.c canvas $c -bg [$w cget -bg] -width 380 -height 410 set column 0 set columnMax 12 set x 0 set y 0 set dx 30 set dy 30 for {set nodes 1} {$nodes < 128} {incr nodes} { if { $column % $columnMax == 0 } { incr y $dy set x 0 } incr column set x [incr x $dx] $c create text $x $y \ -text [format %3d $nodes] -tag node$nodes } set x $dx set x [expr $dx +80] set dy 20 set letterWidth 3 set bheight 10 set bwidth 10 incr y $dy set column 0 $c create text $dx [expr {$y + $bheight -4}] -text State foreach nmtColor [lsort [array names _colors nmtstate,*]] { set descr [string totitle [lindex [split $nmtColor ,] 1]] set xlen [expr {[string length $descr] * $letterWidth + 55}] if { $_colors($nmtColor) ne "" } { set bbox [list $x $y [expr {$x + $bwidth}] [expr {$y + $bheight}]] $c create rect $bbox -fill $_colors($nmtColor) $c create text [expr {$x + $bwidth + 30}] [expr {$y + $bheight -4}] -text $descr } set x [expr {$x + $xlen}] incr column } set x [expr $dx +80] incr y $dy set column 0 $c create text $dx [expr {$y + $bheight -4}] -text Command foreach nmtColor [lsort [array names _colors nmtcmd,*]] { set descr [string totitle [lindex [split $nmtColor ,] 1]] set xlen [expr {[string length $descr] * $letterWidth + 55}] if { $_colors($nmtColor) ne "" } { set bbox [list $x $y [expr {$x + $bwidth}] [expr {$y + $bheight}]] $c create rect $bbox -fill $_colors($nmtColor) $c create text [expr {$x + $bwidth + 30}] [expr {$y + $bheight -4}] -text $descr } set x [expr {$x + $xlen}] incr column if {$column % 4 == 0 } { incr y $dy set x [expr $dx +80] } } pack $c -expand 1 -fill both -padx 5 -pady 5 pack [button $lf.b -text "Reset" -command [list resetHbtState $c]] pack $lf -padx 5 -pady 5 -expand 1 -fill both set ::emcyText " " set lf [labelframe $w.f2 -text " EMCY CANopen Manager "] set _widgets(emcy) [label $lf.l -textvariable ::emcyText -width 50] pack $_widgets(emcy) -padx 5 -pady 5 -expand 1 -fill x -anchor w pack $lf -padx 5 -pady 5 -expand 1 -fill x set _widgets(win) $w set _widgets(canvas) $c return } proc resetHbtState { c } { variable _nodes for {set i 1} { $i < 128 } { incr i } { set _nodes($i,nmtstate) x $c delete state$i } } proc reload {} { variable _widgets destroy $_widgets(win) source application.tcl } # initialize UI initUI
Tested with CAN-REport 4.0
This example makes use of the "User Plugin" of the CAN-REport.
proc application { msg } { set time [get_time? $msg] # get id set id [get_can_id? $msg] # data length code set dlc [get_dlc? $msg] # get data set data [get_data? $msg] set bdata [binary format H* [string map {{ } {}} $data]] # The CAN message contains data in form of: # 1 Byte # 3 Unsigned 16 Bits values in Big Endian byte order # The variables temp1, temp2, temp3 contain the values binary scan $bdata cSSS channel temp1 temp2 temp3 }
Sometimes the Data in a CAN Frame contains bit encoded data. Displaying the data would need several lines. For a better overview it is desirable to only show some important bits in a single line and provide a detailed view on request, ie. the detailed analysis is shown on a button press. The following code shows how to do it.
image create photo uparrow -data R0lGODlhDAAMAJEAAP///9TQyAAAAAAAACwAAAAADAAMAAACEIyPqcudAqNQcq7orNu8qwIAOw== image create photo downarrow -data R0lGODlhDAAMAJEAAP///9TQyAAAAAAAACwAAAAADAAMAAACEoyPqcsobcRrcq5qU6VZdQgmBQA7 # # # ## proc gk::InsertTextEx? {msg {detail {}}} { variable id upvar ::widgets(.usr,text) t $t insert end $msg\n stdout1 if { $detail ne "" } { lassign [split [$t index end] "."] L C set startIdx $L incr L -2 set C [$t count -displaychars $L.0 $startIdx.0] set tag b[incr id] $t tag configure $tag -background #fafbf0 -lmargin1 50 -lmargin2 60 -font {Consolas 8} set b $t.$tag button $b -text "+" -command [list ElideToggle? $t $tag] \ -image downarrow -cursor hand2 \ -borderwidth 0 -relief flat \ -pady 0 $t window create $L.$C -window $b $t insert end ${detail}\n $tag set endIdx [$t index end] $t tag config $tag -elide 1 } } # # # ## proc ElideToggle? {w tag} { set b $w.$tag if {[$b cget -text] eq "-"} { $b config -text + -image downarrow $w tag config $tag -elide 1 } else { $b config -text - -image uparrow $w tag config $tag -elide 0 } }
more lines within the main window (CR 4.0.3)
set ::ST(line_limit?) 100000