Snippets

John Peck Create a pipe table of drill diameters and uses from a PCB drill file

Created by John Peck last modified John Peck
#!/opt/ActiveTcl-8.6/bin/tclsh
# Hey Emacs, use -*- Tcl -*- mode

set scriptname [file rootname $argv0]


# ---------------------- Command line parsing -------------------------
package require cmdline
set usage "usage: [file tail $argv0] \[options] filename"
set options {
    {t.arg none "Comma separated list of tool codes for filled holes"}
}

try {
    array set params [::cmdline::getoptions argv $options $usage]
} trap {CMDLINE USAGE} {msg o} {
    # Trap the usage signal, print the message, and exit the application.
    # Note: Other errors are not caught and passed through to higher levels!
    puts $msg
    exit 1
}

# After cmdline is done, argv will point to the last argument
if {[llength $argv] == 1} {
    set datafile $argv
} else {
    puts [cmdline::usage $options $usage]
    exit 1
}


proc read_file {filename} {
    # Return a list of lines from an input file
    #
    # The file consists of capacitance measurements.
    if { [catch {open $filename r} fid] } {
	    puts "Could not open file $filename"
	return
    }
    set datafile_list [split [read $fid] "\n"]
    return $datafile_list
}


proc get_drill_diameters {raw_line_list} {
    # Returns a dictionary of (tool code) : (tool diameter)
    #
    # Arguments:
    #   raw_line_list -- List of lines from a NC drill file
    set tool_diameter_dict [dict create]
    foreach line $raw_line_list {
	if {[string first % $line] == 0} {
	    # This is the start of the data section
	    break
	}
	if {[string first T $line] == 0} {
	    # This is a tool code line
	    set linelist [split $line C]
	    set diameter [lindex $linelist 1]
	    # Altium has F and S fields in their tool lists
	    set toolstring [regexp -inline {T[0-9]*} $line]
	    # The tool code may contain leading zeros
	    set toolnum [regexp -inline {[1-9][0-9]*} $toolstring]
	    dict set tool_diameter_dict T$toolnum $diameter
	}
    }
    return $tool_diameter_dict
}

proc format_tool_code {tool_string} {
    # Returns a properly formatted tool code
    
}

proc get_drill_units {raw_line_list} {
    # Returns either inch or mm for the drill diameter units
    #
    # Arguments:
    #   raw_line_list -- List of lines from a NC drill file
    set units none
    foreach line $raw_line_list {
	if {[string first % $line] == 0} {
	    # This is the start of the data section
	    break
	}
	if {[string first INCH $line] == 0} {
	    # The dimensions are in inches
	    set units inch
	}
	if {[string first METRIC $line] == 0} {
	    # The dimensions are in mm
	    set units mm
	}
    }
    if {[string equal $units none]} {
	# We could not determine the units
	puts "Error -- could not determine drill diameter units"
	exit
    }
    return $units
}

proc init_drill_count_dict {drill_diameter_dict} {
    # Return an initialized dictionary of (tool code) : (tool count)
    #
    # Arguments:
    #   drill_diameter_dict -- Dictionary of (tool code) : (tool diameter)
    set drill_count_dict [dict create]
    foreach tool [dict keys $drill_diameter_dict] {
	dict set drill_count_dict $tool 0
    }
    return $drill_count_dict
}

proc update_drill_count {drill_count_dict raw_line_list} {
    # Return an updated dictionary of (tool code) : (tool count)
    #
    # Arguments:
    #   drill_count_dict -- Dictionary of (tool code) : (tool count)
    #   raw_line_list -- List of lines from a NC drill file
    set data_started false
    set current_tool none
    foreach line $raw_line_list {
	if {[string first % $line] == 0} {
	    # This is the start of the data section
	    set data_started true
	}
	if {[string first T $line] == 0 && $data_started} {
	    # This is the start of a new tool section
	    set current_tool T[regexp -inline {[1-9][0-9]*} $line]
	}
	if {[string first X $line] == 0 && ![string equal $current_tool none]} {
	    # This is a new drill location
	    dict incr drill_count_dict $current_tool
	}
    }
    return $drill_count_dict
}

proc get_total_drills {drill_count_dict} {
    # Return the total number of drills in the drill count dictionary
    #
    # Arguments:
    #   drill_count_dict -- Dictionary of (tool code) : (tool count)
    set total_drills 0
    foreach tool [dict keys $drill_count_dict] {
	incr total_drills [dict get $drill_count_dict $tool]
    }
    return $total_drills
}



proc write_drill_table {drill_diameter_dict drill_count_dict drill_units} {
    # Write a pipe-delimited drill table
    #
    # Arguments:
    #   drill_diameter_dict -- Dictionary of (tool code) : (tool diameter)
    #   drill_count_dict -- Dictionary of (tool code) : (tool count)
    #   drill_units -- Either inch or mm
    global params
    set drill_diameter_dict [lsort -stride 2 -index 1 -real $drill_diameter_dict]
    set tool_width 10
    set size_width 20
    set count_width 10
    set filled_width 10
    set total_width 7
    set separator [string repeat " " $total_width]
    append separator "|-[string repeat - $tool_width]-"
    append separator "+-[string repeat - $size_width]-"
    append separator "+-[string repeat - $count_width]-"
    append separator "+-[string repeat - $filled_width]-"
    append separator "|"
    puts $separator
    set header [string repeat " " $total_width]
    append header [format "| %-*s | %-*s | %-*s | %-*s | " \
		       $tool_width "Tool" \
		       $size_width "Hole size ($drill_units)" \
		       $count_width "Count" \
		       $filled_width "Filled?"
		  ]
    puts $header
    puts $separator
    foreach tool [dict keys $drill_diameter_dict] {
	set filled ""
	if {[string first $tool $params(t)] != -1} {
	    set filled x
	}
	set table_row [string repeat " " $total_width]
	append table_row [format "| %-*s | %-*s | %-*s | %-*s | " $tool_width $tool \
			      $size_width [dict get $drill_diameter_dict $tool] \
			      $count_width [dict get $drill_count_dict $tool] \
			      $filled_width $filled
			 ]
	puts $table_row
    }
    puts $separator
    set total_row [format "%-*s" $total_width "Total:"]
    append total_row [format "| %-*s | %-*s | %-*s | %-*s | " \
			  $tool_width [llength [dict keys $drill_diameter_dict]] \
			  $size_width "" \
			  $count_width [get_total_drills $drill_count_dict] \
			  $filled_width $filled
		     ]
    puts $total_row
    puts $separator
}

# Start processing the file
set raw_line_list [read_file $datafile]
set drill_diameter_dict [get_drill_diameters $raw_line_list]
set drill_count_dict [init_drill_count_dict $drill_diameter_dict]
set drill_units [get_drill_units $raw_line_list]
set drill_count_dict [update_drill_count $drill_count_dict $raw_line_list]
write_drill_table $drill_diameter_dict $drill_count_dict $drill_units

Comments (1)

  1. Josephine Dawson

    In the fast-paced world of mobile gaming, few titles have captured the attention of players worldwide quite like Geometry Dash. With its addictive rhythm-based gameplay, vibrant visuals, and relentlessly challenging levels, the series has developed a devoted fanbase that spans all skill levels. For those new to the Geometry Dash experience, the aptly named geometry dash lite offers an accessible entry point into this thrilling franchise.

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.