Small script integrates DesignSpark and VeeCAD

For various reasons, I often use stripboard and through-hole components for my one-off prototypes. After trying different tools for laying out the stripbard, I've settled on a great, free niché product called VeeCAD.

Unlike most layout tools, VeeCAD is optimized for stripboards, i.e. producing a stripboard layout is pretty fast. Since VeeCAD relies on external schematic editors, so you need to import your completed netlist into VeeCAD before the fun can begin. In fact, every time you make a change to the netlist you must tell VeeCAD about it, by importing the new, improved netlist.

Now, if you're like me, your schematics drawings evolve over time. You tweak them, fix a netlist bug, replace a component, etc. And with VeeCAD, every time the netlist changes, you must click through a series of windows to get VeeCAD onto the same page as your schematic editor. ..... Hmmm......this quickly gets old.
There must be a less boring way to keep VeeCAD on top of my changes?

If your schematic editor is DesignSpark PCB (a free tool from RS Components), and you don't mind installing yet another great, free tool (AutoIt (see Wikipedia), the answer is now Yes!

Below is a small AutoIt script I made, which will click its way through DesignSparks' menus to export your (currently open) DesignSpark schematic to a temporary file, and then click its way through VeeCAD's menus to import the temp file as a netlist. Taks approximately 2 seconds.

All in one go, and without any setup or configuration :-)

The script assumes DesignSpark and VeeCAD are both running on your PC when the script starts.
Simply copy the script below into a text editor, and save it with the file extension "au3". Run it by double-clicking it.

And as always with scripts: Remember to save your work before running the script.


#cs --------------------------------------------------------------------------------------

    Are you tired of the repetetive mouse clicking required to export a DesignSpark netlist
    into VeeCad?
   
    This AutoIt script exports the currently open DesingSpark schematics (in Generic Netlist
    format) to a file on your hard disk. The script then imports this file into the current
    board in VeeCad (in Easy-PC format).

    (The file will be created in the same directory as this script runs from).
   
    The script assumes you have DesignSpark running and displaying a schematic,
    and VeeCad displaying the board you want to import the netlist into.
   
    Tested with Windows XP, AutoIt version 3.3.6.1, SparkDesign PCB 3.0, and VeeCAD 2.26
    Should be compatible with most versions, if not you can easily adapt it. Have fun :-)
    Standard disclaimer: Comes with no guarantee of fitness for any purpose.
       
    Copyright: www.leifove.com        License: GPL version 3.0
   
#ce ---------------------------------------------------------------------------------------

AutoItSetOption("MustDeclareVars", 1)       ; To avoid spelling mistakes in variable names.

; ----------------------------- Define some useful variables ------------------------------

Dim $netlist_filename          = "netlist_from_designspark.net"        ; Filetype must be .net
Dim $full_path_name_to_netlist = @ScriptDir & "\" & $netlist_filename  ; Same dir as this script

Dim $timeout_period             = 3   ;  The longest this script is willing to wait. In seconds.

Dim $veecad_window_title_start      = "VeeCAD :"     ; Try changing this if the two programs are not found,  
Dim $designspark_window_title_start = "DesignSpark"  ; but don't make the titles longer than necessary! 

Dim $veecad_windows, $designspark_windows

; --------------------- Define a handy functions we are going to need ----------------------

Func ExitIfWindowNeverAppears($window_title)
   
    If (0 = WinWait($window_title, "", $timeout_period)) Then
        MsgBox(48, "Script terminated.", _
            "Lost my patience waiting for the " & $window_title & " window to appear.")
        Exit
    EndIf
   
    WinActivate($window_title)    ; Just in case, probably not necessary.
   
EndFunc


; ---------- Verify that DesignSpakr and VeeCAD are running, and in the correct state ------

$designspark_windows = WinList($designspark_window_title_start) ; Count the number of open DesignSpark windows

If $designspark_windows[0][0] <> 1 Then
    MsgBox (48, "Script terminated.", _
            "There must be one (and only one) open DesignSpark window!. I found " & $designspark_windows[0][0]  & ".")
    Exit
EndIf   

If WinExists("Reports") Then
    MsgBox (48, "Script terminated.", "Can't run when DesignSpark's Reports window is open.")
    Exit
EndIf   

If WinExists("Output Netlist") Then
    MsgBox (48, "Script terminated.", "Can't run when DesignSpark's Output Netlist window is open.")
    Exit
EndIf   

$veecad_windows = WinList($veecad_window_title_start)   ; Count the number of open VeeCAD windows

If $veecad_windows[0][0] <> 1 Then
    MsgBox (48, "Script terminated.", "There must be one (and only one) open VeeCAD window! I found " &     $veecad_windows[0][0] & ".")
    Exit
EndIf   

If WinExists("Import Netlist") Then
    MsgBox (48, "Script terminated.", "Can't run when VeeCAD's Import Netlist window is open.")
    Exit
EndIf   


; ----------------- Export a netlist from the current DesignSpark window --------------------

ExitIfWindowNeverAppears( $designspark_window_title_start )
   
Send( "!o" )   ; "Output"
Send( "r" )    ; "Reports ..."

;   Work in the Reports window

ExitIfWindowNeverAppears ( "Reports")
ControlFocus  ( "Reports", "", 1100)

; I could not make ControlCommand work consitently when trying to
; select 'Generic Netlist' in the Reports' listbox:
;       ControlCommand( "Reports", "", "ListBox1" , "SelectString", 'Generic')
; ,so the next two lines are a work-around:
ControlSend ("Reports", "", 1100, "{UP}{UP}{UP}{UP}{UP}{UP}{UP}{UP}{UP}{UP}{UP}{UP}{UP}{UP}{UP}{UP}")
ControlSend ("Reports", "", 1100, "{DOWN}{DOWN}{DOWN}{DOWN}")  ; 4th item from the top.

Send( "!r" )    ; "Run" button

;   Work in the Output Netlist window

ExitIfWindowNeverAppears( "Output Netlist" )
ControlCommand( "Output Netlist", "", 1030 , "Check")     ; Include components
ControlCommand( "Output Netlist", "", 1031 , "Check")     ; Include nets
ControlCommand( "Output Netlist", "", 1033 , "UnCheck")   ; Don't want to view netlist file   
ControlSetText( "Output Netlist", "", 1035 , $full_path_name_to_netlist )  ; Where to save file.

WinWaitActive ( "Output Netlist" )                      ; Since we have been focused on panels
ControlClick  ( "Output Netlist", "", 1)                ; Click "Generate". This also closes window.

; Close the Reports window

WinActivate ( "Reports")
ControlClick( "Reports", "", 2)                         ; Click "Close"   

; ----------------------------- Import netlist into VeeCad -----------------------

ExitIfWindowNeverAppears( $veecad_window_title_start )
Send( "!n" )   ; "Netlist"
Send( "i" )    ; "Import"

ExitIfWindowNeverAppears( "Import Netlist")
ControlCommand( "Import Netlist", "", "TComboBox1" , "SelectString", 'Easy-PC')  ; Netlist Format
ControlSetText( "Import Netlist", "", "TEdit1" , $full_path_name_to_netlist )    ; Netlist Filename

WinActivate( "Import Netlist")                    ; Focus on window, since we have been focused on panels
ControlClick( "Import Netlist", "", "TButton5")   ; Click "Import"   

; ------------------ Good, we're all done with the export and import -------------

MsgBox(0, "Done!", "Netlist export and import finished.")  ; Comment out this line to skip confirmation.

Exit




The above script works by pretending to be a human operator clicking a mouse and selecting menu items by name reference. Should the developers behind DesignSpark or VeeCAD in the future decide to rename the controls in their GUIs, the script may stop working. In that case, have a look inside AutoIt's excellent Help function and I'm sure you'll figure out how to tweak my script to make it run again :-) 

Have fun!

Beautify your legacy C code layout for "free"

When I read code, it often strikes me how an unexpected code layout can slow me down and waste my time. I quickly develop a rash when I come across C code formatted like this:

void command_Ping(Transaction_t* RequestIn,
    Transaction_t* RequestOut)
{

    SomeStruct_t *Input;
    VeryDifferrentStruct_t* Output   ;
    DataIn_t DataInput;
    MyData_t* DataTest;
   int SampleLoop=1, OuterLoop, InnerLoop;
    static Bit16_t  SampleNumber  =0;
    Input=(SomeStruct_t*)&RequestIn->Param;
    Output = (VeryDifferrentStruct_t*) &RequestOut->Param;
   if ((RequestIn->Param2 & HIGH)== HIGH)
        {
#ifdef SIM /* Applicable for simulator */
    CallSim(&RequestIn->Param,&OuterLoop,LOOP&8);
#else   
    memcpy(Output,Input,PACKET_PARAM_SIZE/2);
#endif
    } else {
   if ((RequestIn->Param2 & LOW) ==LOW)
    {
    ChLoop++;
  }
    }
    RequestOut->Length=RequestIn->Length-6;
}


But if you work with a legacy system, you can't improve the above code, because *any* code change in a system without automated tests comes with a severe penalty: extensive, manual regression testing.
So we don't fix it, and the ugly code stays in the system forever, awaiting the next unsuspecting developer that comes along.  Over time, both you and the system become increasingly miserable.

But wait, there is hope!! The reason we don't change ugly code layout by hand is the risk of introducing new bugs, right? What if there was a guaranteed bug-proof way to change source code layout? Then we could safely drop the expensive manual regression test, and legacy systems would see a brighter future :-) Yeah!

As you may have guessed by now, the secret is to let an automated "indenter" tool make the layout changes, through whitespace removal/adding. Assuming that the tool itself is bug-free, the code that comes out of the tool will look unchanged to the C compiler, i.e. containing exactly the same bugs as the original code, and no new ones. But to you and me, the code will look much more in line with our expectations for cleanly written C code:

void command_Ping(Transaction_t *RequestIn,
                  Transaction_t *RequestOut){

    SomeStruct_t           *Input;
    VeryDifferrentStruct_t *Output;
    DataIn_t               DataInput;
    MyData_t               *DataTest;
    int                    SampleLoop   = 1, OuterLoop, InnerLoop;
    static Bit16_t         SampleNumber = 0;

    Input  = (SomeStruct_t *) &RequestIn->Param;
    Output = (VeryDifferrentStruct_t *) &RequestOut->Param;

    if ((RequestIn->Param2 & HIGH) == HIGH) {
#ifdef SIM /* Applicable for simulator */
        CallSim(&RequestIn->Param, &OuterLoop, LOOP & 8);
#else
        memcpy(Output, Input, PACKET_PARAM_SIZE / 2);
#endif
    }
    else {
        if ((RequestIn->Param2 & LOW) == LOW) {
            ChLoop++;
        }
    }

    RequestOut->Length = RequestIn->Length - 6;
}



(OK,  maybe your expectations are different from mine, but the code is easier to read now)

Spoiler: The above code transformation was produced by the open-source tool Uncrustify, using a settings file I generated after some hours of tweaking 350+ Uncrustify settings in UniversalIndentGUI. (Wikipedia)

The beautifier tools I've looked into typically spend a fraction of a second on improving a large C program. So basically, if you point the tool to your large C/C++ system, all .c, .cpp, .hh and .h files will be beautified in less time than it takes you to finish lunch. All that is left for you is to check the improved sources back into your CVS. No regression testing required!

And it gets better: regardless of your platform (Linux, Mac,Windows), you will find high-quality open-source C/C++ beautifiers on the www. To cut your chase short, I strongly recommend you to download and install the "meta tool" UniversalIndentGUI, as it will enable you to try out the 20+  different beautifiers it comes bundled with (e.g. Uncrustify, GreatCode, BCPP, GNU Indent, and others).
And most importantly, UniversalIndentGUI will allow you to see the immediate effect on source code layout as you tweak the numerous settings offered by the different tools. The interactive process UniversalIndentGUI offers for exploring settings will save you hours, as you work on the settings for your preferred layout.

When you are happy with the results in UniversalIndentGUI, simply export the settings file, and point your selected beautifier to it. (Or you can freely grab my Uncrustify settings file here. Tested with Uncrustify 0.53)

No code layout or style will please everyone in a team of developers, but with an automated beautifier, you can significantly improve source code readabilty in legacy systems, (almost) for free.

Happy beautifying!

Continuous Integration trades fear for instant gratification

Automated build and test (a.k.a. Continuous Integration) has rapidly become the way to develop IT systems. There are many reasons for this (a maturing IT industry, availability of C.I. tools, etc), but I think the most powerful driver is the sheer enjoyment a developer can derive from the C.I. process, in particular:
  • Instant feedback on how good you code is (as measured by unit tests), determined in a risk-free environment
  • The ability to fix a bug (found by unit testing or other automated testing methods) while the program flow, class structures, etc are fresh in your mind.
In short, Continuous integration is a perfect match for a world where instant gratification is the default expectation.

Now, contrast this to the working environment for anyone maintaining a complex IT system built 10-15 years ago, i.e. a legacy system without automated testing facilities:
  • Every code change is associated with fear ("Will this change have any unwanted side effects?", "Will I lose my job if a destructive side effect sneaks past the fallible, manual testing stages into Production?")
  • A high percentage of the bugs reported from Production will be of the "Duh! How could we have missed this bug before deployment?" category, quite simply because a completely manual test regime is bound to have incomplete coverage. And that's exactly the category of bugs which make the poor maintainer look incompetent.
If you've tried both worlds, you are not going back. Continuous integration is here to stay.

Why I use Perltidy, or Why coding style matters

There are Few.
areas of programming that-are more likely to, cause religious wars than! coding: standards some developer's


       consider. coding style? to be an area where personal freeDom should reign supreme unrestricted by team project; or company standards but as
these:
few
    lines
Of
text hopefully    demonstrate punctuation-indentation-etc is important WheN conveying ideas and thou'ghts in a written form.
........

OK, if you made it this far down into my post, I promise to stick to the standard rules for punctuation and capitalization of English. By now you probably agree that reading speed and comprehension is affected when the writer ignores commonly accepted practices. Only when the text format matches your expectations can you focus your attention fully on the message, instead of the format. Of course, the same principle applies to reading source code.

One of many, many gems the Perl community has produced is Perltidy, a great source code formatter for Perl code. The details are here:
http://search.cpan.org/perldoc?perltidy

Simply by using this as my .perltidyrc parameter file:

#----------------  PBP options, as listed on page 35 of PBP  ----------------
-l=78   # Max line width is 78 cols
-i=4    # Indent level is 4 cols
-ci=4   # Continuation indent is 4 cols
-vt=2   # Maximal vertical tightness
-cti=0  # No extra indentation for closing brackets
-pt=1   # Medium parenthesis tightness
-sbt=1  # Medium square bracket tightness
-bt=1   # Medium brace tightness (for non-code blocks)
-bbt=1  # Medium block brace tightness (for code blocks)
-nsfs   # No space before semicolons
-nolq   # Don't outdent long quoted strings
        # Break before all operators:
-wbb="% + - * / x != == >= <= =~ < > | & **= += *= &= <<= &&= -= /= |= >>= ||= .= %= ^= x="
# Note that the PBP book has a typo on page 35 when defining the above -wbb
# option, see http://oreilly.com/catalog/perlbp/errata/perlbp.confirmed
# The above string is correct.

#----------  Perltidy default options (i.e. in line with PBP)  --------------
-asc    # Add any missing optional semicolon at the end of a line
-aws    # Add certain whitespace to improve code readability
-tqw    # Default handling of multi-line 'qw' quotes
-ibc    # Indent block comments to same level as the code
-msc=4  # Minimum 4 spaces between code and same-line comments
-fpsc=0 # Don't try to line up all comments to a fixed column
-hsc    # Align hanging side comments
-sbc    # Enable static block comments
-nssc   # No special handling for static side comments
-dnl    # Delete old newlines
-bbc    # Ensure a blank line before every full-line comment
-bbs    # Ensure a blank line before every sub definition (except one-liners)
-bbb    # Ensure a blank line before code blocks (for, while, if, ....)
-lbl=8  # Minimum number of lines between each -bbb inserted blank line

#-------------  Additional options, based on PBP recommendations  ------------
-bar    # K&R style code braces
-nolc   # Long comments indented, even when this make the total line length "too long"
-noll   # Long lines indented, even when this make the total line length "too long"
-nola   # Don't treat labels as special cases when indenting

#----------  Options concerning Perltidy's input and ouput files  -----------
-nst    # Do NOT output to STDOUT (since we want to use -b)
-b      # Backup the input file to .bak and perform all tidying in the original file
-se     # Write errors to STDERR
-ple    # Preserve the EOL character(s). E.g. in case Perltidy runs
        # on Windows, but processes a Perl file for Unix (and vice versa)

#-----------  Some other Perltidy options, intentionally not used   ----------
# The following Perltidy options are NOT consistent with PBP and should NOT be used:
# -lp, -icb, -fws, -nwls, -nwrs, -sfp, -sak/-nsak, -csc/dcsc, -fnl,
# -ce, -bl, -sbl, -bli, -blil, -bbvt, -otr, -sct, -boc, -kis,
# -pbp (because the -wbb list typo in the PBP book carried over to Perltidy (!))
# The following Perltidy options are not used, for other reasons:
# -sot, -mft, -cab, -bol, -bok/-nbok, -bot/-nbot, -iob, -isbc


I am free to sputter butt ugly code like this:

   sub  manipulate_some_data{

my $self   =  shift; my $data = shift;
# Sometimes add to the data we received
if($data  eq 'good data'  ){
$data.= ' is now even better.'
;}return $data;}

via my keyboard, and have it instantly transformed to this:

sub manipulate_some_data {

    my $self = shift;
    my $data = shift;

    # Sometimes add to the data we received
    if ( $data eq 'good data' ) {
        $data .= ' is now even better.';
    }
    return $data;
}

by Perltidy. Without wasting a single of my overworked brain cells.
(I have configured my IDE to run Perltidy every time I save my source file to disk)

(Click here to download the above .perltidyrc file, if your're too lazy for a copy&paste operation)

Perltidy cleans up my code while I develop, which is great, but the true benefit of Perltidy becomes apparent when you or someone else tries to understand your code 4 weeks later: Since all code you and your teammates produce have the same layout, everyone reads code a little faster and get a quicker understanding of what is intended.

This positive effect is particularly evident when fixing a bug or adding some small feature to existing code, because in these situations you typically spend most of your time skimming code, and only a small portion of your time on the few, new lines.

Perl has crossed oceans and climbed mountains to make hard things possible, don't sacrifice this hard-earned property by leaving messy, sloppy, ugly code behind. Clean it up as you go along, by running perltidy from your IDE, and/or as part of your versioning system's commit process.

Free lunches are hard to come by. Perltidy is one of very few I've come across.