#!/usr/bin/env tclsh # @file Generate a motor configuration file from CSV files of name value pairs. # # Input: List of CSV files. # Output files: # generated_motor_configuration.tcl # genmotconf_report.log # genmotconf_errors.log: Lists missing attributes if a spec is incomplete. # missing_attlist.csv # # TODO # Optionally split configuration accross multiple files for cases where # axes are swapped out, eg Eulerian cradles, sample-stick rotation. # This could be done by supplying a file which contains lines as follows, # CFG_NAME1,m1 m2 m3 # CFG_NAME2,m4 m5 m6 # Where CFG_NAMEn is the name of a config file and mn's are motor names. # In this case generated_motor_configuration.tcl will define the header and asyncqueues. # The motor_configuration.tcl file would then be hand-coded to fileeval the CFG_NAMEn files as required. # source [file dirname $argv0]/genmotconf_procs.tcl set ERRCNT 0 set MOTCFG_CNT 0 set FAILED_MOTCFG_CNT 0 # MOT_ATTLIST: Attributes required to configure an axis without an encoder # ENC_ATTLIST: Attributes required to describe an encoder. # NOTE Encoder readings for the limit switch positions are required. # If the encoder "home" reading is not supplied it is set equal to rev_enc_lim # ENCMOT_ATTLIST: Attributes which describe an axis which has both a motor and encoder. # REQ_ATTLIST: List of attributes required to generate a configuration for a motor object. # SICS_CFG_ATTLIST: Extra attributes required to configure a motor object in SICS. # ALL_ATTRIBUTES: List of all attributes recognised by this program. # # Attributes which are not in these lists are assumed to define the encoder # readings for each position on an axis which has a set of meaningful positions # such as apertures or multi-sample tables. set MOT_ATTLIST [lsort {axis mc steps_per_x}] set ENC_ATTLIST [lsort {absenchome cnts_per_x fwd_enc_lim rev_enc_lim}] set ENCMOT_ATTLIST [lsort [concat $MOT_ATTLIST ENCMOT_ATTLIST]] set SICS_CFG_ATTLIST [lsort {home fwd_lim rev_lim maxspeed maxaccel maxdecel part units}] set REQ_ATTLIST [lsort [concat $MOT_ATTLIST $SICS_CFG_ATTLIST]] set ALL_ATTRIBUTES [lsort [concat $ENCMOT_ATTLIST $SICS_CFG_ATTLIST]] set scriptname [file tail [file rootname $argv0]] set file_list $argv set fhr [open ${scriptname}_report.log "w"] set fhe [open ${scriptname}_errors.log "w"] # @brief Generate a default value for missing attributes. # @param mot motor name # @param att attribute name # @return value for attribute or NOATT if no attribute should be generated. proc gen_attval {mot att} { switch $att { "absenchome" { if [info exists ::${mot}_encatts(rev_enc_lim)] { return [set ::${mot}_encatts(rev_enc_lim)] } else { return "NOATT" } } "maxspeed" {return 1} "maxaccel" {return 1} "maxdecel" {return 1} "rev_lim" {return 0} "home" {return 0} "fwd_lim" { if { [info exists ::${mot}_encatts(fwd_enc_lim)] && [info exists ::${mot}_encatts(cnts_per_x)] } { set fwd_enc_lim_val [set ::${mot}_encatts(fwd_enc_lim)] set cnts_per_x_val [set ::${mot}_encatts(cnts_per_x)] if [info exists ::${mot}_encatts(rev_enc_lim)] { set rev_enc_lim_val [set ::${mot}_encatts(rev_enc_lim)] } else { set rev_enc_lim_val 0 } if [info exists ::${mot}_encatts(home)] { set home_val [set ::${mot}_encatts(home)] } else { set home_val 0 } return [expr {($fwd_enc_lim_val - $rev_enc_lim_val) / $cnts_per_x_val + $home_val}] } else { return 1 } } "part" {return "instrument"} "units" {return "xxx"} } } ################################################################################ # Parse all files and generate ::mn(matt) and ::controllers(cn) foreach f $file_list { parse_file $f $fhr $fhe } ################################################################################ # GENERATE MOTOR CONFIGURATION FILE puts $fhr "GENERATE MOTOR CONFIGURATIONS" puts $fhe "GENERATE MOTOR CONFIGURATIONS" puts $fhe "Required attributes: $REQ_ATTLIST" set fhmc [open "generated_motor_configuration.tcl" "w"] # Write configuration file header and make asyncqueues mk_cfg_header $fhmc puts $fhmc "" foreach mn [lsort [array names motor_attcnt]] { set mot_attlist [lsort [array names $mn]] set mot_encattlist [lsort [array names ${mn}_encatts]] set num_encatts [llength $mot_encattlist] set missing_enc_atts [setdiff $ENC_ATTLIST $mot_encattlist] set num_missing_encatts [llength $missing_enc_atts] set posnum 0 # Decide if a motor configuration should be generated. if [subset $REQ_ATTLIST $mot_attlist] { set mk_config 1 } else { if {$num_missing_encatts == 0} { set mk_config 1 } else { set mk_config 0 } } # Does this motor have an absolute encoder? if {$num_missing_encatts == 0} { set absenc 1 } else { set absenc 0 } # Decide if a list missing attributes with default values should be written. if [subset $ENCMOT_ATTLIST $mot_attlist] { set mk_missing_atts 1 } elseif [subset $MOT_ATTLIST $mot_attlist] { set mk_missing_atts 1 } else { set mk_missing_atts 0 } # Assume that the values of any attributes we don't recognise are encoder # readings for a "posit" motor. set posit_list {} foreach att [setdiff $mot_attlist $ALL_ATTRIBUTES] { incr posnum lappend posit_list "posit_${posnum} \$${mn}_$att" } # Generate a motor configuration and/or a list of missing attributes. if ${mk_config} { mk_motconf $mn $fhmc $absenc $posnum posit_list puts $fhr "Configured $mn" incr MOTCFG_CNT } else { set missing_atts [lsort [concat $missing_enc_atts [setdiff $::REQ_ATTLIST [array names $mn]]]] puts $fhe "$mn attributes missing: $missing_atts" incr FAILED_MOTCFG_CNT incr ERRCNT if {$mk_missing_atts} { set undef_atts [setdiff [lsort "absenchome $SICS_CFG_ATTLIST"] $mot_attlist] foreach att $undef_atts { set attval [gen_attval $mn $att] if {$attval != "NOATT"} { lappend missing_attlist "${mn}_$att,$attval" } } } } } # If there are any missing attributes then write them to a file. if [info exists missing_attlist] { set fh [open "missing_attlist.csv" "w"] foreach attval [lsort $missing_attlist] { puts $fh $attval } close $fh puts "Generated list of missing attributes in missing_attlist.csv. You should rename this if you want to keep it." } # The SICS init code calls motor_set_sobj_attributes. It can be redefined in # the motor_configuration.tcl file. puts $fhmc "proc motor_set_sobj_attributes {} {}" puts stderr "Generated $MOTCFG_CNT motor driver configurations" puts $fhr "Generated $MOTCFG_CNT motor driver configurations" puts stderr "Found $FAILED_MOTCFG_CNT incomplete motor configurations. See ${scriptname}_errors.log and ${scriptname}_report.log" puts $fhe "Found $FAILED_MOTCFG_CNT incomplete motor configurations" close $fhmc close $fhr close $fhe if {$ERRCNT > 0} { puts stderr "Finished with $::ERRCNT errors" }