Sep 27, 2011

Enabling Comments on New Models in LovdByLess

Today a project I've been working on called for enabling comments on a new model that I'd introduced into a lovdbyless application. Took me a couple hours to figure it all out, so I thought I'd write down the steps. Hopefully I won't miss any
  1. Link the new model with the existing comments model. This is easy to do, just add the association with :as => :commentable line to the new model. My final model looked like this:
      
    class Rating < ActiveRecord::Base
      has_many :comments, :as => :commentable, :order => "created_at asc"
      belongs_to :profile
      belongs_to :title
      validates_presence_of :rating, :message => "You didn't supply a rating"
    
      validates_uniqueness_of :title_id, :scope => :profile_id, :message => "You've already told us about that title"
    end
    
  2. Create a route for the comments that belong to the new model. I added this to my routes.rb:
      
      map.resources :ratings do |rating|
        rating.resources :comments
      end
    
  3. I had an existing view/partial. I had to add this code to it to make a link to the existing Thickbox comment form. It's lifted straight from the _blog.html.erb and adapted for use with my model
  4. <%= "Comments (#{rating.comments.size})" %> | <%= inline_tb_link('Add a Comment', "#{dom_id(rating)}_new_comment", :title => "Leaving A Comment") if @p %>
    
  5. Same partial. Here too, code is borrowed from the _blog.html.erb partial to get things moving along. This code provides the summary for the comments related to the model element. It also provides the hidden comment form div.
    <% rating.comments.each do |c| %> <%= render :partial => 'comments/comment', :locals => { :comment => c } %> <% end %>
  6. The special sauce is the last step that I couldn't troubleshoot easily. If you get this far and try to execute, you'll get HTTP status 500 errors from Mongrel when you try to submit the ThickBox form. It's all done in AJAX, so it's not too easy to diagnose. I found the missing step was to add a line or two in comments_controller.rb for my new model The first bit of the protected section had to change. Ultimately, this sets the @parent which is needed in the create method
        def parent; @rating || @blog || @profile || nil; end
        
        def setup
          @profile = Profile[params[:profile_id]] if params[:profile_id]
          @user = @profile.user if @profile
          @blog = Blog.find(params[:blog_id]) unless params[:blog_id].blank?
          @rating = Rating.find(params[:rating_id]) unless params[:rating_id].blank?
          @parent = parent
        end
    
I've modified the css quite a bit, but this is the result I got:

Aug 14, 2011

Beware the yield return

Stumbled on a weird performance issue the other day at work. We were seeing abnormal calls from the middleware to the database, and there were a lot of them.

I fumbled around in the code and found a bunch of yield returns. I was under the impression that the yield return was like a lazy initialize and that returning using yield meant that the IEnumerable would be evaluated once and then cached.

Boy, was I mistaken.

Turns out that the yield return and IEnumerable does not have any built in caching. To cache the results you have to call ToList on the IEnumerable and use the resulting List. If not, the block with yield return will be evaluated every time you traverse the IEnumerable variable. More like a delegate than a lazy initialized variable. For me, the delegate was accessing the database and it was used in more than one place.

Try this code as an example:

using System; 
using System.Collections.Generic; 
using System.Threading; 

namespace ConsoleApplication2 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            var list = IterateRand.GetRandomNumbers(); 

            foreach (var v in list) 
            { 
                Console.WriteLine(v); 
            } 

            // Sleep so the Random is seeded with a new value
            Thread.Sleep(100); 

            Console.WriteLine(); 
            foreach (var v in list) 
            { 
                Console.WriteLine(v); 
            } 
        } 
    } 
    static class IterateRand 
    { 
        public static IEnumerable GetRandomNumbers() 
        { 
            var rand = new Random(); 
            for (int i = 0; i < 10; i++) 
            { 
                yield return rand.NextDouble(); 
            } 
        } 
    } 
} 
You should get two completely different sets of random numbers. It's fine as long as the consuming code is written in such a way that the IEnumerable is only used once. I'll have to be more careful how I use yield return in the future.


May 25, 2011

CakePHP Auth Conventions

CakePHP is something new that I'm picking up. My first exposure to web frameworks was Ruby on Rails. Like Rails, the Cake mantra is convention over configuration. It's an awesome way to program. You can save hours of work if you know what the conventions are and follow them. Even bypassing the conventions is not a superhuman effort, usually a few lines of configuration.

One issue I had with Cake was implementing controller authorization. As you might expect it's because I didn't know and follow convention.

I set up my AppController class to use the Auth module and set the mode to 'controller' in beforeFilter. That meant that each controller had to have isAuthorized function defined.

I knew that most of my controllers were going to be uniform in the rights granted to anonymous users, so I coded the isAuthorized method in the AppController too.

class AppController extends Controller {
    var $components = array('Auth', 'Session');
    var $uses = array('Role');

    function beforeFilter() {
        $this->Auth->authorize = 'controller';
    }

    function isAuthorized() {
        if (strcmp($this->action, "index") == 0 || strcmp($this->action, "view") == 0) {
            return true;
        }
        $user = $this->Auth->user('role_id');
        $admin = $this->Role->findByName('Admin');
        $admin = $admin['Role']['id'];
        if ($user == $admin) {
            return true;
        }
        else {
            return false;
        }
    }
}

I thought I'd allowed access to any "index" and "view" action program wide by always returning true from isAuthorized if those actions were requested. Admins had all CRUD rights and regular users had no additional rights over the anonymous users. Sub classes would further refine these rights if necessary.

The scheme didn't work as I expected though because the anonymous users seemed to be getting blocked from all actions, while regular users had the expected rights. Admins were also behaving as expected.

I had to modify my controller to this

class AppController extends Controller {
    var $components = array('Auth', 'Session');
    var $uses = array('Role');

    function beforeFilter() {
        $this->Auth->authorize = 'controller';
        $this->Auth->allow('view', 'index');
    }

    # defaulting all controllers to allow user access to view and index
    # anonymous access to be allowed by subclasses
    function isAuthorized() {
        $user = $this->Auth->user('role_id');
        $admin = $this->Role->findByName('Admin');
        $admin = $admin['Role']['id'];
        if ($user == $admin) {
            return true;
        }
        else {
            return false;
        }
    }
}

In the beforeFilter I had to specifically enable the actions I wanted open to anonymous use. I guess the Auth component will default to not authorize before checking the isAuthorized function if the session does not contain a user.

Spent a few hours tracking that down using pr($this->Auth->user()) calls and die to trace program flow.

May 18, 2011

WPF Binding not working? Check the Output Window

Took me a long time and hours of frustration troubleshooting WPF binding failures to finally find a silver bullet. Actually, this stuff is all over the place on the net, but for whatever reason, not mentioned in many WPF books.

If you are expecting to see a binding when you start your WPF application, but the value never changes, or is default, or the bound listview is empty when it should have data, check the output window in Visual Studio while you debug.

Bindings in WPF are best-efforts only by the framework. Not really sure why. I sometimes wish a giant unhandled exception error would show up instead of silent failure. Nevertheless...

Here's a sample error message you might find there.

Let's say you have the following XAML layed out and you're trying to bind a listview to a 'Parameters' ObservableCollection member of the control data context. But when you filled out the XAML, it was misspelled.

<DockPanel>
  <Label DockPanel.Dock="Top" Style="{StaticResource LayoutLabel}">Parameter Selection</Label>
  <Border  Style="{StaticResource LayoutBorder}">
    <ScrollViewer DockPanel.Dock="Bottom">
      <ListView View="{DynamicResource ParameterView}" ItemsSource="{Binding Path=Parametrs}"/>
    </ScrollViewer>
  </Border>
</DockPanel>

Now when you debug, the members of the 'Parameters' ObservableCollection don't appear in the listview. Go and check the output window and you might see this in the output window
System.Windows.Data Error: 39 : BindingExpression path error: 'Parametrs' property not found on 'object' ''OrderedEnumerable`2' (HashCode=57007955)'. BindingExpression:Path=Parametrs; DataItem='OrderedEnumerable`2' (HashCode=57007955); target element is 'ListView' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')
Lots of gobbledygook, but clearly in this message WPF is complaining that binding cannot be made with a Parametr property. It shouldn't be too hard to find the trouble with this information.

More often than not, it's not a spelling mistake, but a plain mismatch between the object being searched by WPF and the binding path given.

Example being if the aforementioned control had a datacontext set to 'MyObject' but the class of 'MyObject' doesn't have a 'Parameters' property at all.

May 14, 2011

Cool Places to Build Stuff

A survey of Software developers finds very narrow focus in what motivates. Even the most hardened veteran still gets tingles when building something new and learning.

If you haven't read 'Drive' by Daniel Pink yet, you should get on that. His insights are very true for us software people. Just a fantastic book. If you are feeling unmotivated by your decent paying job, get this book for some possible explanations.

Bottom line, it's very cool to build stuff. Along that vein, I've been visiting this blog a lot in the past few days. It seems to be written directly for me. I'm a shutterbug, a product review addict, and software professional (which they seem to have a requirement for).

I found a job listing on the site and it sounds like a wicked cool place to work. Lots of building stuff, very few meetings.

But the most interesting part isn't the description of themselves in the job listing, but their use of coding challenge to evaluate new candidates. It doesn't appear to be one of those high pressure 'code while we watch' type deals. Rather, you download the challenge data and produce a solution. Email them a link and your resume when you're done.

I might just do that.

Apr 29, 2011

Seth Godin Quote Post

A refreshing outlook on "My boss won't let me". You have to overcome this attitude to do something useful.
When we say, "my boss won't let me," what we're often saying is, "my boss wants great results, but she's not willing to let me take initiative without responsibility."
I'd be shocked if any smart boss took a different approach. Who's going to give you authority without responsibility
Thanks for the daily inspiration Seth.

Apr 22, 2011

Better Than Any Software Developer Compensation Package

I saw this promise in a Software Developer Wanted ad today
We promise you’ll never spend more than an hour a week in meetings.
Heady promise. Still, I know lots of developers who would go for this. At least, they will take a second look at the posting when they read that they aren't really expected to attend too many meetings.


Very clever ad. They definitely know their demographic.

Apr 17, 2011

Promote Your Code: Software Development in Small Environments

Working with a large team of Professional Software Developers is a bit of a dream come true. We take code seriously and we all love to see beautiful structure and design. Code maintainability is utmost on our list of priorities. Well designed and highly maintainable code is the mark of Professional Software Developers.

I used to work in much smaller enterprises where the need for a real Professional Software Developer wasn't recognized. In those places, code was produced by 'affiliated professionals' as I call them. Engineers, Scientists, even Junior Software Developers. In short, people capable of making code, but not trained how to handle software process.

I remember a manager asking us for a code review once. Code reviews should be de rigeur at any company producing code, although in practice is not widely adopted. Anyway, we had no idea what he was talking about. I suspect the manager didn't either. It was probably a due-diligence thing. Papers that needed signing.

We did the best we could. A bunch of us code guys borrowed code guys from other projects to review our code independently. It was a nightmare to gather materials in time for pre-meeting review, prepare a walk-through for the other team, then try to schedule everyone in the meeting.

When we finally got everyone together and started our walkthrough, it was obvious that the other code guys hadn't even cracked open the material we sent. The whole meeting was a bust. We sat for an hour describing basically what our product was supposed to do. Not good use of review time. Although the managers papers got signed!

If we'd had a Professional Software Developer around, I can imagine that the review process would have been a lot more productive. For example, we would've known that reviews that are more frequent and much smaller are far more effective than one huge review of thousands of lines at the end of a development cycle. Who can understand the mess created when code guys are left to their own architectural devices for 6 months? We also would've known how to handle the ego challenges of critical review of a developers artwork.

Still, the Kings Ransom that true professionals are paid is a little off-putting for businesses that don't specialize in software. Maybe there's not really enough there to employ such a professional full-time. It's too bad that a company struggling with software can't bring in Professional Software Developers part time, just to lend their expertise on process and architecture.

Or can they?

Why not drop me a line at the contact page?

Mar 27, 2011

Kerning (Character Spacing) with Microsoft Office Embedded Charts

Note: This probably applies to copy/pasted charts between Excel/Word, I was using VBA, so I had to find the VBA way to fix it.

I like Office VBA automation. Sometimes Microsoft makes it hard for me to remember that fact though.

The usual problem is that the easy stuff is easily automated with VBA. Which is good. But if you want to do something slightly irregular, dare I say even advanced, then you're in for a world of hurt.

When copying and pasting charts between Excel and Word, the traditional wisdom is to copy and paste as a picture because it's the most reliable reproduction of the original chart once pasted.

Fine if all you want is a static chart pasted into a Word document, but if you want to give the Word user the ability to modify the chart or data, then you need to copy and paste a chart object.

Although it's fully supported by the VBA interop runtime, the results are less than fantastic.
Here's some code that pastes an existing Excel Chart Object

Sub PasteChartAsInteractive(chart As Excel.chart1)
    Dim PageWidth As Long
    Dim WidthRatio As Double
    chart.ChartArea.Copy
    Dim myShape As Shape
    
    Selection.Style = ActiveDocument.Styles("Normal")
    Selection.ParagraphFormat.Alignment = wdAlignParagraphCenter

    'Commented out line is pasting as image.
    'Selection.PasteSpecial Placement:=wdInLine, DataType:=wdPasteMetafilePicture

    Selection.PasteAndFormat (wdChart)
    
    Selection.EndOf Unit:=wdParagraph, Extend:=wdMove
    Selection.TypeParagraph
    Selection.ParagraphFormat.Alignment = wdAlignParagraphLeft
    
    Selection.range.ListFormat.ApplyBulletDefault
    Selection.TypeText "..." + vbCr

End Sub

But the problem with the inserted chart is that the kerning is all messed up. Observe the results.

The kerning fault is especially noticeable in the title above. There is virtually no spacing between characters in any letters. Some even appear to overlap.

Google searches mostly turn up the workaround of 'paste as image', which was not an option for me, the chart had to be editable and live.

I had given up and was going to move away from Excel Charts and into using Word Graph Objects This was going to be an ordeal because I had a bunch of existing charts in this macro, pasted as images, and the formatting all needed to be consistent. My impression is that the graph and chart API's are not really that similar.

Then I stumbled on a solution with Clouseau-esque serendipity. Changing the object layout (Format -> Object -> Layout) to use 'Square' rather than 'In-line' made the kerning problem disappear. What's more, I could change the layout back to 'In-line' and kerning was still proper.

There was a minor issue with finding the appropriate commands to do this with VBA. Normally I record a macro of the action I want to reproduce if I'm not totally sure how. With macros, the Layout tab is disabled, so I had to dig around online to find the VBA.

Sub PasteChartAsInteractive(chart As Excel.chart)
    Dim PageWidth As Long
    Dim WidthRatio As Double
    chart.ChartArea.Copy
    Dim myShape As Shape
    
    Selection.Style = ActiveDocument.Styles("Normal")
    Selection.ParagraphFormat.Alignment = wdAlignParagraphCenter
    
    Selection.PasteAndFormat (wdChart)
    Set myShape = Selection.Paragraphs(1).range.InlineShapes(1).ConvertToShape
    myShape.ConvertToInlineShape
    
    Selection.EndOf Unit:=wdParagraph, Extend:=wdMove
    Selection.TypeParagraph
    Selection.ParagraphFormat.Alignment = wdAlignParagraphLeft
    
    Selection.range.ListFormat.ApplyBulletDefault
    Selection.TypeText "..." + vbCr

End Sub
And the result

It's too bad that we have to resort to these cheap tricks when trying to get members of the Office suite to behave together. You would think by now, the integration would be much tighter.

By the way, did you notice that the Y-axis title is truncated in both images? The fix for that is to add superfluous characters to the Y-axis title until all the important characters show.

Mar 23, 2011

Paginated Printing of WPF Visuals - II

Last time I discussed paginated printing of wpf visuals, I mentioned that the pagination didn't support a whole lot of options.

There was no support for margins or headers for example. The print was edge-to-edge which is OK when you're a developer and you're going to throw the paper out right after printing, but not so good if you are the end user and need to put the paper in a binder, or if you've dropped the whole mess on the floor and need to put it back in order.

The easy way to put more visuals with the visuals you have rendered is to use a ContainerVisual object and stack some visuals together. With my previous entry there was a problem because it transformed and rendered the original Visual directly to print. This meant that the Visual could not be part of ContainerVisual as it already had a visual parent.

To solve that problem, I rendered the Visual to bitmap before print. That way I could use ContainerVisual with the bitmap and stack up any number of visuals in the container. Here's the class.

class FrameworkVisualPrint : DocumentPaginator
{
    private readonly FrameworkElement Element;
    private const double XMargin = 1 * 96;
    private const double YMargin = 1 * 96;
    public String Header { set; get; }

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

    private const double ScaleFactor = 0.5;

    private DrawingVisual GetHeader(int pageNumber)
    {
        var header = new DrawingVisual();

        using (var dc = header.RenderOpen())
        {
            var text = new FormattedText("Page " + (pageNumber + 1) + Environment.NewLine +
                Header, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, 
                new Typeface("Arial"), 8, Brushes.Black);
            dc.DrawText(text, new Point(XMargin, 0.5 * 96));
        }

        return header;
    }

    public override DocumentPage GetPage(int pageNumber)
    {
        var tgroup = new TransformGroup();
        tgroup.Children.Add(new ScaleTransform(ScaleFactor, ScaleFactor));
        Element.RenderTransform = tgroup;
        tgroup.Children.Add(new TranslateTransform(-PageSize.Width * (pageNumber % Columns) 
            + XMargin, -PageSize.Height * (pageNumber / Columns) + YMargin));
        Element.RenderTransform = tgroup;

        try
        {
            var area = new Rect(
                new Point((PageSize.Width * (pageNumber % Columns)) / ScaleFactor,
                          (PageSize.Height * (pageNumber / Columns)) / ScaleFactor),
                new Size(PageSize.Width / ScaleFactor,
                         pageSize.Height / ScaleFactor));

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

            // Make a bitmap from the transformed Element
            var bitmap = PngBitmap(Element, (int)area.Width, (int)area.Height);
            var vis = new DrawingVisual();

            // Draw the bitmap into a DrawingVisual object
            DrawingContext dc = vis.RenderOpen();
            dc.DrawImage(bitmap, new Rect(0, 0, area.Width, area.Height));
            dc.Close();

            // Grab a header to apply (with page numbers)
            var header = GetHeader(pageNumber);
            // Container for all the new stuff.
            var cVisual = new ContainerVisual();

            cVisual.Children.Add(header);
            cVisual.Children.Add(vis);
            return new DocumentPage((cVisual));

        }
        finally
        {
            Element.RenderTransform = null;
            Element.Clip = null;
        }

    }

    private RenderTargetBitmap PngBitmap(Visual visual, int width, int height)
    {
        // Render
        var rtb =
            new RenderTargetBitmap(
                (int)(width / ScaleFactor),
                (int)(height / ScaleFactor),
                96.0 / ScaleFactor,
                96.0 / ScaleFactor,
                PixelFormats.Default);
        rtb.Render(visual);


        return rtb;
    }

    private Size pageSize;
    public override Size PageSize
    {
        set
        {
            pageSize = value;
            pageSize.Width -= XMargin;
            pageSize.Height -= YMargin;
        }
        get { return pageSize; }
    }
}

The class isn't actually complete. See the previous article for the initial version. This is just new and changed methods.

The important bits are commented.
  • Make a bitmap from the Visual
  • Draw the bitmap to a new DrawingVisual Object
  • Stick the DrawingVisual and any other visuals needed in a ContainerVisual
  • Bob's your uncle!

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;
}