Feb 5, 2011

Paginated Printing of WPF Visuals

I was considerably frustrated when recently researching paginated printing of WPF controls. I own two books on WPF. One mentions pagination but only when discussing FlowDocuments. That's almost useless if you have a large visual that you want to print in a paginated manner.
I found one Stackflow discussion that pointed me in the right direction. There were 3 instructions. Something like:
  1. Set scrollviewer to enabled in both directions.
  2. Implement IDocumentPaginator
  3. Implement the NextPage among others to transform the printed visual and return the page
Sometimes you get really succinct instructions that are exactly what you need from stack, sometimes they're misleading and you have to investigate. In this case I found there is no such interface as IDocumentPaginator. So that was not much help. What I really needed to do was subclass the DocumentPaginator abstract class and do the transforms.

Secondly the scrollviewer instruction is about giving the contained visual element infinite space. So if you have a clipped element and want to print the whole thing, you need to add the scrollviewer container so that scrolling can be enabled in both directions so that its ActualSize is the real size of the control. My control already existed in a scrollviewer so I was already 1/3 done.

Here's the other 2/3.

class ProgramPaginator : DocumentPaginator
{
    private FrameworkElement Element;
    private ProgramPaginator()
    {
    }

    public ProgramPaginator(FrameworkElement element)
    {
        Element = element;
    }

    public override DocumentPage GetPage(int pageNumber)
    {

        Element.RenderTransform = new TranslateTransform(-PageSize.Width * (pageNumber % Columns), -PageSize.Height * (pageNumber / Columns));

        Size elementSize = new Size( 
            Element.ActualWidth, 
            Element.ActualHeight); 
        Element.Measure(elementSize); 
        Element.Arrange(new Rect(new Point(0, 0), elementSize));

        var page = new DocumentPage(Element);
        Element.RenderTransform = null;

        return page;
    }

    public override bool IsPageCountValid
    {
        get { return true; }
    }

    public int Columns
    {
        get
        {
            return (int) Math.Ceiling(Element.ActualWidth/PageSize.Width);
        }
    }
    public int Rows
    {
        get
        {
            return (int)Math.Ceiling(Element.ActualHeight / PageSize.Height);
        }
    }

    public override int PageCount
    {
        get
        {
            return Columns * Rows;
        }
    }

    public override Size PageSize
    {
        set; get;
    }

    public override IDocumentPaginatorSource Source
    {
        get { return null; }
    }
}

This class doesn't handle margins or any fancy printing controls. You have to set the page size (usually like this)

internal void Print()
{
    var paginator = new ProgramPaginator(this_grid);
    var dlg = new PrintDialog();
    if ((bool) dlg.ShowDialog())
    {
        paginator.PageSize = new Size(dlg.PrintableAreaWidth, dlg.PrintableAreaHeight);
        dlg.PrintDocument(paginator, "Program");
    }            
}       

Pass in a framework element and print. It's really handy to cover scrollviewers that can grow to unlimited dimensions.

Jan 14, 2011

Software Haiku

C:\ant buildandtest
C:\me go and get coffee
C:\unit tests fail

Jan 1, 2011

State Machine for embedded systems

This is a bit of a trip in the way-back machine for me. I programmed a bunch of embedded off-highway vehicle controllers using C back in the day.

Most of these systems were based on finite state machines. The goal of the programmer is to contain the machine behaviour into a finite set of behaviours based on state history and present inputs.

The hardest part was tracking down bugs which essentially turned Finite State Machines into Infinite State Machines. Global state variables and monolithic switch statements are notoriously easy to turn into spaghetti this way.

The best implementation for state machine I've seen was handed to me for maintenance when I was a junior. I remember it was wicked hard to understand at first. I gummed my first one up because I did not understand all the beauty that it brought.

Once you get over an innate fear of function pointers, it really is a great and simple state machine implementation. BTW, function pointers used this way are much safer than data pointers because the function pointers are never assigned and cannot be null.

Break this thing into several files when your state machine grows.

// I know this first include isn't very embedded. I need it for Sleeping
#include "windows.h"
#include "stdio.h"

// Declare typedefs for state conditions and actions
typedef unsigned char (*ConditionFunction)(void);
typedef void (*ActionFunction)(void);

// The structure defines an action, a condition and a transition
// If the condition is met, the action is executed and the transition occurs
typedef struct _tagState {
 ConditionFunction Condition;
 ActionFunction Action;
 struct _tagState *nextState;
} StateItem;

// Some sample conditions 
unsigned char falseCondition(void) 
{ return 0; }
unsigned char trueCondition(void)
{ return 1; }

// Some sample actions
void SomeAction(void) 
{ printf ("SomeAction\n"); }

void BootAction (void) 
{ printf ("BootAction\n"); }

void NoAction (void) 
{ printf ("NoAction\n"); }

// The state machine engine.
// Simplicity is bliss
StateItem* ProcessState(StateItem *si) {
 while(1) 
 {
  if(si->Condition() == 0)
  {
   si++;
  }
  else
  {
   si->Action();  
   return si->nextState;
  }
 }
}

// Forward declare states so jumping is unhampered
const StateItem OtherState[];
const StateItem BootState[];

/*
*  The tables represent states, each line is a state item
*  Generally, the last state item performs a state action, 
*  and jumps back to itself. Preceeding items are generally
*  transitions out to other states based on conditions
*/

// Except boot, end all the states with a trueCondition and a jump back to self
const StateItem OtherState[] = {
 {&falseCondition, &SomeAction,  OtherState},
 {&trueCondition, &NoAction,   OtherState}
};


const StateItem BootState[] = {
 {&falseCondition, &SomeAction,  BootState},
 {&trueCondition, &BootAction,  OtherState}
};

// Main just runs the state machine (no inputs or outputs are processed)
// The 'tick' time is 1 second which is slow for an embedded system
// but good for demonstrations
int main(void)
{
 StateItem *StatePtr = BootState;

 while (1) {
  StatePtr = ProcessState(StatePtr);
  Sleep(1000);
 }
  
 return 0;
}

Dec 24, 2010

Macros for the Masses

The past few days I've been automating some chart creation activities using Word and Excel VBA. It was kinda cool. I wrote the macro in Word and added a reference to Excel 12 library in the Word's VBA editor (Alt-F11 -> Tools -> References -> Microsoft Excel 12.0 Object Library).

I wrote some nifty Word -> Excel interop to create the charts in excel, then copy/paste them into Word. Data manipulation in Word was not a priority so paste as picture was the solution rather than creating Word Graphs from the data.

Use Project, not Normal
After that, the cool part ended though. I needed the macros to work with Word 2003, but wrote them with Word 2007 - a major boner. Not only that, I found that the default macro location for Word is in Normal.dot and not in the document itself. It makes sense if you are writing macros for only your own benefit, but transferring macros requires them to be located within the document.

To solve the normal.dot problem, I just copy and pasted the text from the default location into the proper location.

But that was the easy part. To fix the other problem, I had to set up a Virtual Machine using VMWare Server and install Microsoft Office 2003. Then I had to recreate the reference to Microsoft Excel 11.0 Object Library.

That solves it, and it looks like the macro works when run in Microsoft Office 2007. However, when I looked at the referenced libraries from Word 2007, it appeared that the reference was upgraded to Excel 12. Safe to assume that if I make a change and save the document, the macros won't work in 2003 anymore because the reference will be wrong. Ugh.

So I need to maintain a purpose built VM to develop macros for office 2003. Gasp. Isn't there an easier way?

Dec 23, 2010

Optimistic Concurrency with C#

There's lots of ways to get Optimistic Concurrency with your database calls. I like row versions to guarantee exclusivity. You could use a database lock, but that's expensive and shouldn't be necessary where data contention is low. Don't use update times since the system time is rather course.

With this class, we expect that most of the time the optimistic lock will work. However, we will be relying on other data access clients to 'obey the rules'.

This data access class accesses only one table and relies on the version to change when a row modification is made. There's only one modification made here, but this simple class is easily expanded.

The lazy class ConcurrencyObject is just used to shuffle data around. It hides implementation from everyone other than the assembly it's created in. Useful if you separate the data access agents from the other parts of your code with libraries.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;

namespace OcDac
{
    public class OCDataAgent
    {
        private readonly SqlConnection myConnection;
        private readonly SqlCommand selectPendingObjectCommand;
        private readonly SqlCommand updatePendingObjectCommand;
        public OCDataAgent()
        {
            myConnection = new SqlConnection();
            // build the connection string
            SqlConnectionStringBuilder bu = new SqlConnectionStringBuilder();
            bu.DataSource = "tcp:10.0.0.114, 1370";
            bu.InitialCatalog = "ocSample";
            bu.IntegratedSecurity = false; // Sql Server Authentification
            bu.UserID = "sa";
            bu.Password = "youwish";
            myConnection.ConnectionString = bu.ConnectionString;

            // This stmt is used to get the ID and Version of one object that's pending
            selectPendingObjectCommand = new SqlCommand { Connection = myConnection };
            selectPendingObjectCommand.CommandText = "SELECT TOP 1 ID, VERSION FROM OnlyTable WHERE STATUS='PENDING'";

            // Claim this object if it's row number hasn't been modified and has the correct ID
            updatePendingObjectCommand = new SqlCommand { Connection = myConnection };
            updatePendingObjectCommand.CommandText = "UPDATE OnlyTable SET STATUS='IN_PROCESS', VERSION=@new_version WHERE ID=@id AND VERSION=@version";
            updatePendingObjectCommand.Parameters.Add("@id", System.Data.SqlDbType.BigInt);
            updatePendingObjectCommand.Parameters.Add("@version", System.Data.SqlDbType.BigInt);
            updatePendingObjectCommand.Parameters.Add("@new_version", System.Data.SqlDbType.BigInt);

        }

        public void LockPendingObject(ConcurrencyObject co)
        {
            if (myConnection.State == System.Data.ConnectionState.Closed)
                OpenConnection();

            updatePendingObjectCommand.Parameters[0].Value = co.Id;
            updatePendingObjectCommand.Parameters[1].Value = co.Version;            
            updatePendingObjectCommand.Parameters[2].Value = co.Id + 1;
            
            // If the claim succeeded, then we can update our co otherwise exception
            var rowsAffected = updatePendingObjectCommand.ExecuteNonQuery();
            if (rowsAffected != 0)
                co.Id++;
            else 
                throw new Exception("Could not lock object using OC");
        }

        public ConcurrencyObject GetPendingObject()
        {
            if (myConnection.State == System.Data.ConnectionState.Closed)
                OpenConnection();
            var co = new ConcurrencyObject();

            var reader = selectPendingObjectCommand.ExecuteReader();

            while (reader.Read())
            {
                co.Id = reader.GetInt64(0);
                co.Version = reader.GetInt64(1);
            }
            reader.Close();
            return co;
        }

        private void OpenConnection()
        {
            myConnection.Open();
        }
        
    }    

    public class ConcurrencyObject 
    {
        internal Int64 Version { get; set; }
        internal Int64 Id { get; set; }
    }
}

Dec 20, 2010

Coding is Art

Programmers are sometimes a difficult bunch to work with. I'm no better.

We fancy ourselves as uber-logical like Spock and uber-analytical like Data from Start Trek and Star Trek TNG respectively (we also revel in a little nerdification mixed in for good measure).

Truth be told we are much more like van Gogh than we care to admit. Idealistic, anxious, frustrated, and misunderstood.

But honestly, when you think of individuals (real or imagined) with boat loads of talent who comes to mind? Spock or van Gogh? The tragedy of the talented?

Dec 18, 2010

VMWare Server Saves Me Big Bucks

I love VMWare.

My main problem is too many ideas. Not a problem on it's own, but I also find I must take the ideas to implementation. At least a little way to see how well they'll work out.

One problem that I ran into before I discovered VMWare Server was the constant need for more boxes. I always needed experimentation machines. My home office was filled with partly assembled computers that hosted my experiments. It's not that a single machine doesn't have the horsepower, more that during discovery, I hate being careful of existing folders, databases and so on. I'd rather rip and tear. That means empty boxes for every idea.
VMWare Running Windows 2003 Server on my Windows 7 laptop
Another issue that I constantly ran into before discovering virtualization with VMWare was constant re-installation of OS's. Sometimes you want to get a clean OS install so all the dependencies are known when installing software packages. With snapshots on VMWare server, you can install an OS once, then back it up forever. When ready for a new experiment, just dig out the snapshot rather than install from DVD again.

Not only does VMWare easily deal with these problems, but also adds these great benefits.

Unlike most virtualization platforms, the VM's created in VMWare Server are accessible from anywhere on the network through VMWare console. The VM's run on the VMWare server box, but the UI is presented on the client box. I can run VM's on powerful workstations and access them with my ancient laptop and get good performance while sitting on my living room couch. Don't laugh, it happens more often than you think!

Lastly, the VM's are mobile. I can run VMWare Server on my laptop and take my ideas on the road to show off once they are partially incubated. Not only that, but I can move the VM's around to different VMWare Servers without issue. That's important in case a box goes down.

Best of all, it's free. Thanks VMWare.