Wednesday, December 19, 2007

He made Bill Gates a grilled cheese sandwich

Today I had lunch with the man that Bill Gates bought Word from.
And I got fired.

What a day.

Monday, November 26, 2007

The other 40%

It seems Jeff Atwood's post on the Two Types of Programmers, as well is it's mother post, has missed a very important niche in the development community: The Microsoft Fanboys.
These are the ones that jumped at the opportunity to develop with MONO, and competed in the Code Project's Race to Linux. The developers who read the C# 3.0 Language Specification just ot improve the CLS compliance of their code. The developers who where hired on to those measly "internal" jobs with the sole purpose of upgrading the companies legacy code to .NET, and convince the company they need MSDN just so they can come home at night and install nightly builds of Windows Server, SQL Server, and Visual Studio in hopes of keeping that company ahead of the curve.
The detail that both Jeff and Ben leave out is the propensity of many Microsoft developers to be just as passionate and dedicated as any Open Source dev.

Shocking Comment #1: I like programming and really enjoy ASP.NET. I think it's neat and fun and interesting and cool how you can go from literally nothing to having a data-driven web application that can be used by people around the world in an amazingly fast amount of time. Furthermore, I want to spread that enthusiasm to folks.

I have an immense amount of respect for anyone who calls themselves "Open Source" and yet does their best to evangelize a Microsoft product. I think it is especially a great testament to what Microsoft has created in .NET when open source advocates like Jeff actually call for more open source .NET projects like MONO and others.

Shocking Comment #2: [The alpha-geeks] get all excited about the latest Linux distro or AJAX toolkit or distributed SCM system, spend all weekend on it, blog about it… and then are confounded about why they can’t get their office to start using it.

I just think it's a sad reality that many of the best software out there is kept behind closed doors for this same reason. The sad facts are stated clearly above. The open source guys can get their companies to use any open source tools, so the best open source doesn't make it into the enterprise, while the fanboys are constantly innovating in the enterprise, but are bound by law in some cases to keep their technology proprietary.

It's a simple but sad state of affairs that in my own cynical mind has no hope of changing any time soon.

Wednesday, October 31, 2007

VB. It's good for something...

For better or worse, I spend a lot of time trying to explain real life situations in code.
Below is the thought process of Paul McCartney when writing "Let it Be".

Public Class Beatles

Private hardship As New Death(Of Mother)
Protected help As Mother
Private WithEvents life As New Paul()

Public Sub New()
If help Is Nothing Then
life.Experiece(hardship)
life.Experiece(New Hour(Of Darkness))
End If
End Sub

Public Sub Sing() Handles life.TimeExperienced
While life.Contains(hardship)
Me.help = hardship.Help
Whisper.Words(Of Wisdom)(help)
life.RecoverFrom(hardship)
End While
End Sub
End Class

Class Trouble

End Class
Class Death(Of T)
Inherits Time(Of Trouble)

End Class

Class Time(Of T)

Private _help As Mother = New Mary()
Public Property Help() As Mother
Get
Return _help
End Get
Set(ByVal value As Mother)
_help = value
End Set
End Property
End Class

Class Hardship
Inherits Time(Of Trouble)

Public ReadOnly Property Cause() As Time(Of Trouble)
Get
Return New Darkness()
End Get
End Property
End Class

Class Darkness
Inherits Time(Of Trouble)

End Class

Class Hour(Of T As Time(Of Trouble))
Inherits Time(Of Trouble)
End Class

Class Times(Of T)
Inherits List(Of Time(Of T))
End Class


Class Life(Of T)
Private times As New Times(Of T)

Function Contains(ByVal time As Time(Of T)) As Boolean
Return times.Contains(time)
End Function
Sub Experiece(ByVal time As Time(Of T))
times.Add(time)
RaiseEvent TimeExperienced()
End Sub
Sub RecoverFrom(ByVal time As Time(Of T))
times.Remove(time)
End Sub

Default Public ReadOnly Property Time(ByVal index As Integer) As Time(Of T)
Get
Return times(index)
End Get
End Property

Public Event TimeExperienced()
End Class
Class Paul
Inherits Life(Of Trouble)
End Class


Public Class Whisper
Shared Sub Words(Of T)(ByVal m As Mother)
Console.WriteLine(m.Advice)
End Sub
End Class

Public Class Mother

Private _advice As String
Public Property Advice() As String
Get
Return _advice
End Get
Set(ByVal value As String)
_advice = value
End Set
End Property
End Class
Class Mary
Inherits Mother
Public Sub New()
Advice = "let it be"
End Sub
Public Overrides Function ToString() As String
Return Advice
End Function
End Class

Public Class Wisdom
Inherits Knowledge
End Class
Public Class Knowledge

End Class


Public Class Hit
Public Shared Sub Create()
Dim b As New Beatles
End Sub
End Class

Thursday, October 25, 2007

Wanted:

Programmer.
Must work be able to work unreasonable amounts of time, on impossible deadlines, with insufficient resources, inadequate compensation, conflicting orders, and be have ability to read boss's mind and accept responsibility for everything that goes wrong.

Wednesday, October 3, 2007

Finally! Someone with a brain!

This post should be put on a pedestal and bowed down to by millions.
I think every developer on the planet that has had to dip into legacy code has been in the same situation as I was a month ago: "It's my fault that the stored proc sent out 15,000 of the same email? How was i supposed to know it was in a while loop? The loop itself was nested 300 lines deep with 300 lines within it! It took 10 seconds of scrolling to get from the declaration to the end!"

Monday, October 1, 2007

An Eternal Paradox

/*
* Why the Internet cannot stop making references to MC Hammer.
*/
namespace Paradox
{
using System;
using The90s;

static class Program
{
static void Main()
{
Hit og = Hit.Create();
Console.WriteLine(og.StillCool.ToString()); // PM NOTE: fails unit tests.
Hit meme = new Hit(true);
Console..WriteLine(meme.StillCool.ToString()); / PM NOTE: fails unit tests.
}
}

namespace The90s
{
public class
Hit
{
bool _stillCool = false;

public Hit(bool stopUse)
{
_stillCool = stopUse;
}
static bool Stop()
{
return false;
}
static bool Collaborate()
{
return false;
}
static bool Listen()
{
return false;
}
public static Hit Create()
{
if (Stop())
return new Hit(Collaborate() && Listen());
else return new Hit(true);
}
public bool StillCool
{
get { return _stillCool; }
}
}
}
}

Thursday, September 27, 2007

Predilection to (lack of) Implementation

I find it strange, as an enterprise software developer, that I can write 1000+ lines of code on a whim--without so much as building the application once. It's not that I have any particular level of trust in the fact that my code will work, it's just that... Well, here's an example:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using System.Windows.Forms;

namespace Precision.Design
{

#region Enums & Stuff
enum ResizeHandlePosition
{
TopLeft = 0,
TopRight = 1,
BottomLeft = 2,
BottomRight = 3,
Top = 4,
Bottom = 5,
Left = 6,
Right = 7
}
#endregion

public class DesignerControl : UserControl
{
#region Fields
private static readonly int DefaultWidth = 100;
private static readonly int DefaultHeight = 100;

private const int BorderWidth = 6;
private const int BorderHeight = 6;

private const int HandleCount = 4;
private Rectangle[] handles = new Rectangle[HandleCount];
private static readonly int HandleWidth = 5;
private static readonly int HandleHeight = 5;
private static readonly Size HandleSize = new Size(HandleWidth, HandleHeight);

private Pen borderPen = new Pen(Color.Black, 1);
private Pen handlePen = new Pen(Color.Black, 1);

private const int borderOffset = BorderWidth - 4;
private static Rectangle _borderRect = new Rectangle(borderOffset, borderOffset, DefaultWidth - borderOffset, DefaultHeight - borderOffset);
#endregion

public DesignerControl()
{
borderPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
for (int i = 0; i < size =" HandleSize;" backcolor =" System.Drawing.Color.Transparent;" brush =" Brushes.Transparent;" brush =" Brushes.Transparent;" brush =" Brushes.Black;" brush =" Brushes.Black;" i =" 0;" maxx =" BorderWidth" maxy =" BorderWidth" maxwidth =" this.Width" maxheight =" this.Height" left =" e.Control.Left" top =" e.Control.Top" left =" e.Control.Left"> maxX ? maxX : e.Control.Left;
e.Control.Top = e.Control.Top > maxY ? maxY : e.Control.Top;
e.Control.Width = e.Control.Width > maxWidth ? maxWidth : e.Control.Width;
e.Control.Height = e.Control.Height > maxHeight ? maxHeight : e.Control.Height;

base.OnControlAdded(e);
}
protected override void OnResize(EventArgs e)
{
_borderRect.Width = this.Width - BorderWidth;
_borderRect.Height = this.Height - BorderWidth;

handles[ResizeHandlePosition.TopLeft].Location = new Point(0, 0);
handles[ResizeHandlePosition.TopRight].Location = new Point(this.Width - HandleWidth - borderOffset, this.Top);
handles[ResizeHandlePosition.BottomLeft].Location = new Point(0, this.Bottom - HandleHeight - borderOffset);
handles[ResizeHandlePosition.BottomRight].Location = new Point(this.Width - HandleWidth - borderOffset, this.Bottom - HandleHeight - borderOffset);

base.OnResize(e);
}
#endregion
#region Mouse Stuff
protected override void OnMouseHover(EventArgs e)
{
if (!this.ContainsFocus) return;
Point curPos = PointToClient(Cursor.Position);
if (handles[0].Contains(curPos))
{
Cursor = Cursors.SizeNWSE;
}
else if (handles[1].Contains(curPos))
{
Cursor = Cursors.SizeNESW;
}
else if (handles[2].Contains(curPos))
{
Cursor = Cursors.SizeNWSE;
}
else if (handles[3].Contains(curPos))
{
Cursor = Cursors.SizeNESW;
}
else
{
Cursor = Cursors.SizeAll;
}
base.OnMouseHover(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{

base.OnMouseMove(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
}
protected override void OnGotFocus(EventArgs e)
{
if (!this.ContainsFocus) return;
Cursor = Cursors.SizeAll;
base.OnGotFocus(e);
}
#endregion
}
}
namespace Precision.Design
{
public partial class AssemblyDesignerControl : UserControl
{
public AssemblyDesignerControl()
{
InitializeComponent();
TextView.Dispose();
}
public void AddControl(string name, Point loc)
{ AddControl(name, loc.X, loc.Y); }
public void AddControl(string name, int x, int y)
{ DesignSurface.AddControl(name, x, y); }

}
}
namespace Precision.Design.Controls
{
public class Label : System.Windows.Forms.Label
{
System.Windows.Forms.Label _label = new System.Windows.Forms.Label();

public Label(string text)
{
_label.Text = text;
_label.Height = 200;
_label.Width = 200;
_label.BackColor = SystemColors.Control;
_label.Anchor = AnchorStyles.Bottom | AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right;
_label.Click += new EventHandler(_label_Click);

this.Controls.Add(_label);
}

void _label_Click(object sender, EventArgs e)
{
this.Focus();
}
protected override void OnGotFocus(EventArgs e)
{
_label.Focus();
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (!ContainsFocus) return;
if (e.Button == MouseButtons.Left)
this.Location = Cursor.Position;
base.OnMouseMove(e);
}
private NotifiedForm _parent;

public new NotifiedForm Parent
{
get { return _parent; }
set { _parent = value; }
}
}
}
using System;
using System.Drawing;
using Winforms = System.Windows.Forms;
using Precision.Design;
using Precision.Design.Controls;

/* TODO:
* 1. Double-Buffering
* 2. Export to bitmap
* 3. ???
* 4. Profit!
*/

namespace Precision.Design
{
internal class AssemblyDesignSurface : Winforms.Panel
{
private new static readonly Color DefaultBackColor = SystemColors.Window;

//private static Graphics _backbuffer;

public AssemblyDesignSurface()
:base()
{
this.AllowDrop = true;
this.BackColor = DefaultBackColor;
this.AutoScroll = true;
}

protected override void OnDragDrop(Winforms.DragEventArgs e)
{
if (e.Data.GetDataPresent(Winforms.DataFormats.StringFormat))
{
AddControl(
(string)e.Data.GetData(Winforms.DataFormats.StringFormat),
this.PointToClient(new Point(e.X, e.Y))
);
}
base.OnDragDrop(e);
}
protected override void OnDragEnter(Winforms.DragEventArgs e)
{
base.OnDragEnter(e);
}
protected override void OnDragOver(Winforms.DragEventArgs e)
{
if (!e.Data.GetDataPresent(Winforms.DataFormats.StringFormat))
e.Effect = System.Windows.Forms.DragDropEffects.None;
else
e.Effect = System.Windows.Forms.DragDropEffects.Copy;
base.OnDragOver(e);
}
protected override void OnDragLeave(EventArgs e)
{
base.OnDragLeave(e);
}
protected override void OnQueryContinueDrag(Winforms.QueryContinueDragEventArgs e)
{
base.OnQueryContinueDrag(e);
}

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
//_backbuffer = e.Graphics;

base.OnPaint(e);
}

public void AddControl(string name, Point loc)
{ AddControl(name, loc.X, loc.Y); }
public void AddControl(string name, int x, int y)
{
if (name == "Label")
{
Label lbl = new Label("Label1");
lbl.Location = new Point(x, y);
Controls.Add(lbl);
//ControlResizer rzr = new ControlResizer(lbl);
lbl.Focus();
}
else if (name == "TextBox")
{

}
else if (name == "Background")
{

}
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms;

namespace Precision.Design
{
public class AssemblyToolBox : ListBox
{
private new static readonly Color DefaultBackColor = SystemColors.Control;
private static readonly string DefaultText = string.Empty;

private ToolBoxItemCollection _items = new ToolBoxItemCollection();

public AssemblyToolBox()
: this(null) { }
public AssemblyToolBox(IEnumerable items)
{
this.Text = DefaultText;
if (items != null) _items = new ToolBoxItemCollection(items);
}

protected override void OnMouseDown(MouseEventArgs e)
{
if (Items.Count == 0) return;

int index = IndexFromPoint(e.X, e.Y);
DragDropEffects effect = DoDragDrop(Items[index].ToString(), DragDropEffects.Copy | DragDropEffects.Scroll);
}
protected override void OnQueryContinueDrag(QueryContinueDragEventArgs qcdevent)
{
base.OnQueryContinueDrag(qcdevent);
}


}
#region Item Collection
public class ToolBoxItemCollection : IEnumerable, IList
{
private List _items = new List();

public ToolBoxItemCollection()
: this(null) { }
public ToolBoxItemCollection(IEnumerable items)
{
if (items != null) _items = new List(items);
}

#region IEnumerable Members
public IEnumerator GetEnumerator()
{
return _items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
#endregion
#region IList Members

public void Add(ToolBoxItem item)
{
if (!_items.Contains(item))
_items.Add(item);
else throw new InvalidOperationException("Cannot add duplicate item to list");
}

public void Remove(ToolBoxItem item)
{
_items.Remove(item);
}

public int IndexOf(ToolBoxItem item)
{
return _items.IndexOf(item);
}

public void Insert(int index, ToolBoxItem item)
{
_items.Insert(index, item);
}

public void RemoveAt(int index)
{
_items.RemoveAt(index);
}

public ToolBoxItem this[int index]
{
get
{
return _items[index];
}
set
{
_items[index] = value;
}
}

#endregion
#region ICollection Members


public void Clear()
{
_items.Clear();
}

public bool Contains(ToolBoxItem item)
{
return _items.Contains(item);
}

public void CopyTo(ToolBoxItem[] array, int arrayIndex)
{
_items.CopyTo(array, arrayIndex);
}

public int Count
{
get { return _items.Count; }
}

public bool IsReadOnly
{
get { return false; }
}

bool ICollection.Remove(ToolBoxItem item)
{
return _items.Remove(item);
}

#endregion
}
#endregion
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms;

namespace Precision.Design
{

public class EditableControl : DesignerControl
{
private static readonly BufferedGraphics NO_MANAGED_BACK_BUFFER = null;
private static readonly Brush DefaultBrush = Brushes.Black;

BufferedGraphicsContext GraphicsManager;
BufferedGraphics BackBuffer;

private bool _selected;

public EditableControl()
{
this.Height = 100;
this.Width = 100;

SetUpDoubleBuffer();
Invalidate();
}

#region Painting Stuff
private void SetUpDoubleBuffer()
{
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
GraphicsManager = BufferedGraphicsManager.Current;
GraphicsManager.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);
BackBuffer = GraphicsManager.Allocate(this.CreateGraphics(), ClientRectangle);
}
protected override void OnResize(EventArgs e)
{
//if (BackBuffer != NO_MANAGED_BACK_BUFFER)
// BackBuffer.Dispose();

//GraphicsManager.MaximumBuffer = new Size(this.Width + 1, this.Height + 1);

//BackBuffer = GraphicsManager.Allocate(this.CreateGraphics(), ClientRectangle);

//this.Refresh();
base.OnResize(e);
}
protected override void OnPaint(PaintEventArgs e)
{
// TODO: Turn on when fixed.
//BackBuffer.Render(e.Graphics);
base.OnPaint(e);

}
#endregion

#region Moving and Focus
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
}
protected override void OnGotFocus(EventArgs e)
{
_selected = true;
Invalidate();
base.OnGotFocus(e);
}
protected override void OnLostFocus(EventArgs e)
{
_selected = false;
base.OnLostFocus(e);
}

protected override void OnMouseClick(MouseEventArgs e)
{
OnGotFocus(e);
base.OnMouseClick(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
//_selected = true;
////Point pt = Cursor.Position;
//offset = e.Location;
//_beginPoint = new Point(e.X + this.Left, e.Y + this.Top);
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
int x = Cursor.Position.X - 350;
int y = Cursor.Position.Y - 350;
this.Left = x < top =" y" location =" Cursor.Position;" _selected =" false;" pt =" Cursor.Position;" _endpoint =" new" _selected =" value;" _selected =" selected;" _selected =" value;">
(For those of you who are curious, this will probably build, but it won't do much)

All of the above code was done with no building to check syntax, and no form to associate it with.
Funny thing is, when I did build it--and after more than a few bug fixes--I did decide to try it out, and guess what I couldn't figure out? How to get my control to follow the cursor without if flickering all over the place.

What this has taught me is that, despite my slogan (see this blog's subtitle), sometimes a team is just best. Maybe even a team of two, but a team nonetheless. Problem is, this environment I work in isn't exactly conducive to "team-based" development. For the past 20 years this company has had between 2 and 20 developers at any given time, and not once did any of those developers collaborate on a single application, practice, or methodology. This is why I've made it my goal to spend the next few months working my way up the manufacturing line re-writing every application in use, all the while building a framework that, if I can pitch it well enough to the powers that be, will be used for all future application development in this company. (Or at least this division, anyway.)
Now again, I won't even pretend that anything I develop here is "production-quality"--my base data access classes don't even support Transactions (laziness...)--but my goal isn't to get my code out on the floor, it's nothing more than to enforce some good architecture. Any good enterprise developer will tell you that 300 applications, all with their own data access layers is a date with disaster, and this fact has been proven many times over in our company. Problem is, no one, and I mean no one, not the developers, not the management, not even the new head of the development department has a clue what the phrase "software architecture" means in a broad sense.
I blame this ignorance mostly on their experience. the VB6 standard libraries are hardly the most robust things on the planet, it's probably 30Mb total of compile code. Now 175Mb for .NET 2.0 isn't huge, but the tools provided with it and the architecture of the framework itself does more than demonstrate how things should be organized in a production-quality system. Now of course I don't mean to imply that VB6 isn't enterprise ready, after all, many of the largest businesses in the country either have before or still today run entirely on VB6-based systems; I can't help thinking though of what could have come of those very same organizations had something like ASP.NET, Ruby on Rails, or the concept of "AJAX" had been around back during the original .com bubble.

Or what kind of "production-quality" code could be running this company if I weren't so confused as to why my controls aren't gaining focus when I click them...

Thursday, September 6, 2007

A Service-Oriented Enterprise

The following is an idea I pitched to the head of our IS department. It's an SOA-like architecture, with some implementation details thrown in.
Unfortunately, he wasn't impressed.

A Transaction Queue-Based System
· All requests for processing have a strict definition and structure.
· All requests are sent to a request processing service on a central server and saved to a queue on disk.
· A completely different service on the same or different server deserializes these requests in order of priority or time of request, performs the task, and reports back to the user.
· All duplicate requests are logged, and the initial request is always given priority.

Priority for tasks is determined statically by Quality as well as dynamically based on previous TTC (Time To Completion). TTC priority is used mostly for short tasks like "Submit ATT Reading" or "Mark Piece".

Framework:
Functionality is based around a singular, remotable, serializable object representing a request to perform a task.

Base Request object contains:
· Requestor - Network Name, IP, User, etc.
· Request - Definition, Time of submission, TTC (delta)
· Destination - Automatically determined destination service for horizontal scalability.

Base Interface contains:
· Above Signature
· Manual Success Check (Fail-Safe)
· Security Context – Credentials required to access secure sources. This does NOT consist of passwords and the like, rather a security “context” which describes why the information is requested. Actual credentials are handled by the lower layers.

All layers of the system have access to Business Objects for easy translation of messages and objects.

Services:
Service Broker:
· Liaison between a floor application and SQL Server or other back-end.
· Accepts requests and places them in the Queue.
· Reports back to user when completed.
Request Processing Service:
· Sifts through request queue, processing requests by priority.
· Reports back to user when completed.
Request Reporter:
· Reports back to user when completed.
· This may reside in the Secretary or Processing service. The location of said functionality depends on how the completed requests are stored.

Anytime Request Viewer:
· A Windows Event Viewer-type application that allows viewing of pending requests, and after a logon, manual manipulation of priority and On-Demand execution.

Associated Applications:
Service Manager:
· Validates users and provides simple application management capabilities.
· Manages Enterprise/Machine/User-level settings.
Front-End apps:
· All front-end apps will only have access to the Request Secretary, to maintain security.

Scalability:
Vertical:
· Multiple client access is a native capability.
Horizontal:
· Each Service maintains metrics about its load, and can split up processing tasks among its instances.
· Instances are referred to by name, just as in SQL Server.

Fail-Safe Facilities:
· All Requests have a FS/MCS (Fail-Safe/Manual Success Check) in case the reporting service is not available. This contains the steps necessary to validate that the task was completed successfully.
· Tasks will be defined by the Request Library and will not be editable by developers.
· Tasks that take more than 30 seconds are reported on early, tasks taking more than 2 minutes are reported on periodically.
· Failed requests are logged, and reported to the user. Successive failures are reported to a higher authority.


I have a majority of the code completed, but since my boss refuses to read C# for fear of embarrassment over the fact that he has no clue what a delegate is, until I have something substantial, he's clueless.
As soon as I have a working prototype, I'll post some code.

Sunday, August 26, 2007

Letter to my employer

To Whom It May Concern:

This company’s so-called “system” is in an irreparable state.
The how and when of this atrocity’s collapse needs to be
addressed.
Replacement of our current system with foreign methodologies
will only become a determent to the process and speed its inevitable demise.

I place no specific blame on those whose ignorance may have
caused this lack of design, and no amount of meetings and complaining amongst
the IS department will be anything but counter-productive towards the goal of
replacing this system.

In order to save this company, one of two things must occur:

1)Ignore the problems. Let the existing system remain and work with it as long as
possible and wait for all things to grind to a halt. Then move on to step 2.

2) Shut down the plant for a given period, and design a new system from scratch.
Decide on a single, standardized, and strictly enforced
set of processes and methodologies, construct a new system around it, and
“re-boot” the plant with this new system.


Neither of these options sounds in any way conceivable to
anyone with a business-oriented mind, but I honesty think that the former will
occur if nothing is done.

I realize that hearing something like this from someone in
my position may even be considered “crossing the line,” but you must believe
that I only have the best interest of the company in mind.

I could provide you with several examples to prove the above
claims, and even if you were to refuse all possibility of such a dramatic
change, I still want you to be aware of what I believe to be the inevitable.

I want this company to succeed just as much as anyone, and I
believe that in order for that to happen, I must express my fears and concerns
now. If you would like to meet in private about these concerns, I would be more
than happy to elaborate on what I have stated here.

Sincerely,
nibbles&bits

Thursday, August 23, 2007

Object-Oriented Objectivism

A whole collection of blogs have made their statements against Object-Oriented programming.

I say one thing to all of you: It's all about who you are. In my opinion, developers, more than anyone else, reflect their personalities in their work. For example, I'm a very visual person; I always, always, always build my interface first. Now of course like any well designed software it changes over time, but for me, having an interface to go by reminds me of what the goal is--user satisfaction. Which is why I'm so addicted to OO:

Users, no matter how tech-savvy, think in nouns and verbs: "I click the button, I get a report.", "My document must be saved." Even developers, despite their own development practices, if they use an IDE, write code with objects in mind: "I submit a build, which has bugs, that are fixed."

Why should this not be reflected in our code? I was recently tasked with the creation of a simple utility for "admin" users to submit Software Development/Change Requests:

public partial class frmSubmit : WinForms.Form
{
SDCR thisSDCR;

public frmSubmit()
{
InitializeComponent();

thisSDCR = new SDCR(SubmitSDCR.Properties.Settings.Default.Server, Properties.Settings.Default.Database);

lblRequestNo.Text = thisSDCR.RequestNo.ToString();
FillReasonList();

if (System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed)
this.Text += " v" + System.Deployment.Application.ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString();

#if DEBUG
txtRequestor.Text = "Test";
pthExecPath.Text = @"\\mfgvolumes\GDrive\SOFTWARE\ClickOnce\Install\SubmitSDCR\SDCR_Rev 2.xls";
txtRequestDesc.Text = "Request";
lstReasons.SelectedIndices.Add(0);
lstReasons.SelectedIndices.Add(3);
#endif
this.ActiveControl = txtRequestor;
}

private void ClickSubmit(object sender, EventArgs e)
{
if (!ValidateInput()) return;
Submit();
Print();
DisplayInfoBox("Sumitted SDCR", "Your SDCR has been submitted!\n\rCheck your printer for a hard copy for your records.");
ExitApplication(this, new EventArgs());
}

private void FillReasonList()
{
foreach (ChangeReason c in ReasonCollection.GetPossible(SubmitSDCR.Properties.Settings.Default.Server, Properties.Settings.Default.Database))
{
lstReasons.Items.Add(c);
}
}
private void Submit()
{
thisSDCR.EmergencyRequest = chkEmergency.Checked;
thisSDCR.InScope = chkInScope.Checked;
thisSDCR.Requestor = txtRequestor.Text;
thisSDCR.RequestDate = dtpRequestDate.Value;
thisSDCR.ExecutablePath = pthExecPath.Text;
thisSDCR.DescOfRequest = txtRequestDesc.Text;
thisSDCR.DescOfReason = chkOther.Checked ? txtReasonDesc.Text : string.Empty;
int length = lstReasons.SelectedItems.Count;
for (int i = 0; i < valid =" true;" valid =" false;" valid =" false;" valid =" false;"> 0) return true;
if (lstReasons.SelectedItems.Count > 0) return true;
Globals.DisplayInfoBox("Invalid Input", "You must select aReason for Change, or specify a reason in the Description box. Check the form and try again.");
return false;
}

private void chkOther_CheckedChanged(object sender, EventArgs e)
{
txtReasonDesc.Enabled = chkOther.Checked;
lstReasons.Enabled = !chkOther.Checked;
if (!chkOther.Checked)
{
txtReasonDesc.Clear();
this.ActiveControl = lstReasons;
}
else
this.ActiveControl = txtReasonDesc;
}
private void DisableReasonList(object sender, EventArgs e)
{
if (!lstReasons.Enabled)
lstReasons.SelectedIndex = -1;
}
private void ExitApplication(object sender, EventArgs e)
{
this.Close();
}
}


To me, as long as the underlying objects work, and return the data I expect, I could care less about how nested the inheritance hierarchy is. So SDCR inherits DataAccessObject, which inherits DataConnectionObject, which contains a Dataset, which contains a DataTableCollection, which contains a DataTable, which contains a DataRowCollection, which contains a DataRow, which contains an array of objects, which is where my Request Number is stored.
I could either create my own implementation of all of this every time I write a new app, or I could just build an object called SDCR, set it's RequestNo, Name, and Date property can call it's Submit() method, and be done with it.

To me, OO = Reusable. A DLL full of useful common functions is one thing, an object full of ready-made access to a database with a simple "new SDCR("bob","change").Submit()" is a whole other world of usefulness.

Tuesday, August 21, 2007

Threading adventures update

So after speaking with the resident multi-threading guru, I came to the conclusion that, albeit an obvious one, still disappoints me.

See, the problem with my model was that I was trying to "push" the actions in the events onto the form instead of "pulling" the data in and forgetting about it. So unfortunately,

public frmTestBed(NitonXL800 gun)
{
InitializeComponent();

if (gun != null) tester = gun;
else tester = new NitonXL800();

tester.DataReceived += new ComDeviceDataReceivedEventHandler(tester_DataReceived);
tester.PortClosed += delegate(object sender, EventArgs e) { StopProgress(); };
}

void tester_DataReceived(object sender, ComDeviceEventArgs e)
{
txtOutput.Text = tester.Buffer;
}


Is completely out of the question, and must be replaced with:

public frmTestBed(NitonXL800 gun)
{
InitializeComponent();

if (gun != null) tester = gun;
else tester = new NitonXL800();

tester.DataReceived += new ComDeviceDataReceivedEventHandler(tester_DataReceived);
}

void tester_DataReceived(object sender, ComDeviceEventArgs e)
{
GetData updater = delegate(string data) { txtOutput.Text = data; StopProgress(); };
txtOutput.BeginInvoke(updater, e.DataRecieved);

}

My hope with this whole thing was to have the NitonXL800 object do all the marshaling for the developer, allowing the first solution. The problem he helped me realize was that "tester_DataRecieved" will always exist in a separate thread since it actually exists inside "tester". So again, getting the data from the thread is easy, just have BeginInvoke do the marshaling, but the simple truth is that it's just not "nice" to manipulate the form from the child thread, even though it is essentially owned by the form's thread.

But really, this whole problem was nothing but a design issue: Why would you want some random class modifying your form, anyway, even if it does exist in your thread? That's like my form knowing what the Baud Rate of my serial port is!

Now how exactly BeginInvoke pulls off the execution of StopProgress would definitely be an interesting conversation...

...in a galaxy far, far away...

My adventures in .NET land have brought me to an interesting cross-roads.
See, most of the actual code modification I do in any environment is refactoring and re-architecture of existing assemblies; my roots in OO leave me in a moral dilemma every time i see something like:
Public Function GetProperty(Optional propName As String = "Any", _
Optional vIndex As Long = -1, _
Optional vType As PropertyType = MI_Value) As String()
Dim I As Integer
Dim j As Integer
Dim Temp() As String
On Error GoTo GET_PROP_Error
ReDim Temp(0) As String
Temp(0) = ""
ErrorState = 0
'check to see if a string or an index has been passed.
If Not propName = "Any" And vIndex = -1 Then
j = 0
For I = 1 To Properties.Count
If UCase(Properties.Item(I).Key) = UCase(propName) Then
j = j + 1
ReDim Preserve Temp(j) As String
If vType = MI_Key Then
Temp(j - 1) = Properties.Item(I).Key
ElseIf vType = MI_Value Then
Temp(j - 1) = Properties.Item(I).Value
ElseIf vType = MI_Both Then
j = j + 1
ReDim Preserve Temp(j) As String
Temp(j - 2) = Properties.Item(I).Key
Temp(j - 1) = Properties.Item(I).Value
End If
End If
Next I
If (j > 0) Then
ReDim Preserve Temp(j - 1)
End If
GetProperty = Temp
ElseIf Not vIndex = -1 And propName = "Any" Then
On Error GoTo GET_PROP_BY_INDEX_Error
ReDim Temp(0) As String
If vType = MI_Key Then
Temp(0) = Properties.Item(vIndex).Key
ElseIf vType = MI_Value Then
Temp(0) = Properties.Item(vIndex).Value
ElseIf vType = MI_Both Then
ReDim Preserve Temp(1) As String
Temp(0) = Properties.Item(vIndex).Key
Temp(1) = Properties.Item(vIndex).Value
End If
GetProperty = Temp
Else
For I = 1 To Properties.Count
If UCase(Properties.Item(I).Key) = UCase(propName) Then
If j = vIndex Then
ReDim Temp(0) As String
If vType = MI_Key Then
Temp(0) = Properties.Item(I).Key
ElseIf vType = MI_Value Then
Temp(0) = Properties.Item(I).Value
ElseIf vType = MI_Both Then
ReDim Preserve Temp(1) As String
Temp(0) = Properties.Item(I).Key
Temp(1) = Properties.Item(I).Value
End If
GetProperty = Temp
Exit Function
Else
j = j + 1
End If
End If
Next I
ErrorState = -10 'item not found in collection
GetProperty = Null
End If
If Temp(0) = "" Then
ErrorState = -10
GetProperty = Temp
Else
ErrorState = 0
End If
Exit Function

GET_PROP_BY_INDEX_Error:
ErrorState = -10 'item not found in collection
Exit Function

GET_PROP_Error:
ErrorState = -9 'Error accessing collection during search

End Function;

It makes me cringe.
Now granted, the above excerpt is (obviously) VB6, which warrants a whole host of implicit, and often required, architecture problems, but I nonetheless develop a certain pang upon sight of such atrocities.

But recently have have run into an issue in my own little framework, something I've codenamed "Project Cloud". It's a base framework for the future implementation of a new ERP system at my company. If all goes well, I will be pitching the idea to the corporate office, whom for whatever reason has chosen our particular division as the source of the next globally unified system, which they have been calling "AQCS".

The dilemma at hand lies in my implementation of event-base asynchronous IO through a serial port. The design consists of a hierarchy of 3 classes:

HardwareDevice - This simply handles the relevant information about the device being communicated with. Things like manufacturer name and model number are stored here (mostly just for better exception information).
ComDevice : HardwareDevice, System.ComponentModel.ISynchronizeInvoke - Though the name suggests something entirely different, this is a device which communicates though a "COM", or Serial port. "COM", of course, comes from the naming scheme of the ports themselves - "COM1", "COM2", etc.
NitonXL800 : ComDevice - This class, as the name suggests, is specific to a single device, it's constructors fill HardwareDevice with the relevant info, as well as provide ComDevice with port setup options.

The actual implementation of the classes is quite simple:


public abstract class HardwareDevice
{
private string name;
private string manufacturer;

public HardwareDevice()
: this("Unknown Device", "Unknown Manufacturer") { }

public HardwareDevice(string name)
: this(name, "Unknown Manufacturer") { }
public HardwareDevice(string name, string manufacturer)
{
this.name = name;
this.manufacturer = manufacturer;
}

public string Manufacturer
{
get { return manufacturer; }
set { manufacturer = value; }
}

public string Name
{
get { return name; }
set { name = value; }
}

}
}

public class ComDevice : HardwareDevice
{
#region Fields
private SerialPort prt;
private StringBuilder buffer = new StringBuilder();
private bool closeOnRecieve = true;
#endregion
#region Constructors
protected ComDevice()
: this(string.Empty) { }
protected ComDevice(string name)
: this(name, string.Empty) { }
protected ComDevice(string name, string manufacturer)
: this(name, manufacturer, "COM1") { }
protected ComDevice(string name, string manufacturer, string portName)
: base(name, manufacturer)
{
prt = GetDefaultSetup(portName);
prt.DataReceived += new SerialDataReceivedEventHandler(prt_DataReceived);
}
#endregion
#region Util
protected void OpenPort()
{
OpenPort(true);
}
protected void OpenPort(bool close)
{
prt.Open();
OnPortOpened(new EventArgs());
}
protected void ClosePort()
{
prt.Close();
OnPortClosed(new EventArgs());
}
protected void ClearBuffer()
{
buffer = new StringBuilder();
}
public static SerialPort GetDefaultSetup(string port)
{
return new SerialPort(port, 9600, Parity.None, 8, StopBits.One);
}
#endregion
#region Event Handler Pairs
private void prt_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
OnDataReceived(e);
}
protected virtual void OnDataReceived(SerialDataReceivedEventArgs e)
{
string buf = prt.ReadExisting();
buffer.Append(buf);
if (DataReceived != null)
DataReceived(this, new ComDeviceEventArgs(buf));

if (closeOnRecieve) ClosePort();

}
private void OnPortOpened(EventArgs e)
{
if (PortOpened != null)
PortOpened(this, e);
}
private void OnPortClosed(EventArgs e)
{
if (PortClosed != null)
PortClosed(this, e);
}
#endregion
#region Properties
protected string Buffer
{ get { return buffer.ToString(); } }
protected int BaudRate
{
get { return prt.BaudRate; }
set { prt.BaudRate = value; }
}
//etc...
protected StopBits StopBits
{
get { return prt.StopBits; }
set { prt.StopBits = value; }
}
#endregion
#region Events
protected event ComDeviceDataReceivedEventHandler DataReceived;
protected event EventHandler PortClosed;
protected event EventHandler PortOpened;
#endregion
}
#region Event Stuff
public delegate void ComDeviceDataReceivedEventHandler(object sender, ComDeviceEventArgs e);

[System.Diagnostics.DebuggerStepThrough()]
public class ComDeviceEventArgs
{
private string data;
private SerialDataReceivedEventArgs serialData;

public ComDeviceEventArgs()
: this(string.Empty) { }
public ComDeviceEventArgs(string received)
: this(received, null) { }
public ComDeviceEventArgs(SerialDataReceivedEventArgs e)
: this(string.Empty, e) { }
public ComDeviceEventArgs(string received, SerialDataReceivedEventArgs e)
{
if (e != null) serialData = e;
data = received;
}
public string DataRecieved
{
get { return data; }
}
public SerialData EventType
{
get { return serialData.EventType; }
}
}
#endregion

public class NitonXL800 : ComDevice
{
public NitonXL800()
: this("COM1") { }
public NitonXL800(string port)
: base("XL800", "Niton", port)
{
base.Encoding = System.Text.Encoding.ASCII;
base.ReadTimeout = 2000;
base.DataReceived += new ComDeviceDataReceivedEventHandler(NitonXL800_DataReceived);
base.CloseOnRecieve = false;
base.PortClosed += new EventHandler(NitonXL800_PortClosed);
base.PortOpened += new EventHandler(NitonXL800_PortOpened);
}

#region Event Handler Pairs
private void NitonXL800_DataReceived(object sender, ComDeviceEventArgs e)
{
OnDataReceived(e);
}
private void OnDataReceived(ComDeviceEventArgs e)
{
if (DataReceived != null)
DataReceived(this, e);
}
void NitonXL800_PortOpened(object sender, EventArgs e)
{
OnPortOpened(e);
}
private void OnPortOpened(EventArgs e)
{
if (PortOpened != null)
PortOpened(this, e);
}
void NitonXL800_PortClosed(object sender, EventArgs e)
{
OnPortClosed(e);
}
private void OnPortClosed(EventArgs e)
{
if (PortClosed != null)
PortClosed(this, e);
}
#endregion
#region Properties
public new string Buffer
{ get { return base.Buffer; } }
public bool PortOpen
{
get { return base.IsOpen; }
}
public int Timeout
{
get { return base.ReadTimeout; }
set { base.ReadTimeout = value; }
}
#endregion
#region Util
public void Read(bool close)
{
CloseOnRecieve = close;
base.OpenPort();
}
public void Read()
{
Read(true);
}
public void CancelRead()
{
// TDODO: Cancel read
}
#endregion
#region Overrides
public override string ToString()
{
return string.Format("{0} {0} in port {2}", base.Manufacturer, base.Name, base.PortName);
}
#endregion
#region Events
public new event ComDeviceDataReceivedEventHandler DataReceived;
public new event EventHandler PortClosed;
public new event EventHandler PortOpened;
#endregion
}


It's an incomplete implementation to say the least. Criticism aside, my problems arise below when I attempt to post e.DataRecevied to a control.
public partial class frmTestBed : Form
{
NitonXL800 tester;

public frmTestBed()
: this(null) { }
public frmTestBed(NitonXL800 gun)
{
InitializeComponent();

if (gun != null) tester = gun;
else tester = new NitonXL800();

tester.DataReceived += new ComDeviceDataReceivedEventHandler(tester_DataReceived);
tester.PortClosed += delegate(object sender, EventArgs e) { StopProgress(); };
tester.PortOpened += delegate(object sender, EventArgs e) { StartProgress(); };
}

void tester_DataReceived(object sender, ComDeviceEventArgs e)
{
string received = string.Empty;
GetData updater = delegate(string data) { txtOutput.Text = data; StopProgress(); };
txtOutput.BeginInvoke(updater, e.DataRecieved);
}
private void StartProgress()
{
pgReading.Style = ProgressBarStyle.Marquee;
pgReading.Value = 10;
}
private void StopProgress()
{
pgReading.Style = ProgressBarStyle.Blocks;
pgReading.Value = 0;
}

delegate void GetData(string data);
}

Oddly enough, getting the data to a control is easy, BeginInvoke works like a charm. But there in lies my moral dilemma: I'm an architect; a framework developer; I can't force those who write applications that target my framework to understand asynchronous programming, especially if my own knowledge of it is still not fully developed. Ideally, all any developer would have to do is hook up the DataReceived event of the NitonXL800 class, call Read(), and wait for DataReceived to fire so s/he can drop that info into whatever container requires it. I can't have some random threading exception when they don't use BeginInvoke.
As far as I can tell at the moment, I need some sort of liaison--Something that implements ISynchronizeInvoke that can marshal over the data into the executing thread before the event exits, so that the form below could care less about where the data actually came from.
The reason for this is (at least to me) somewhat interesting. As it turns out, even calls that have nothing to do with the data being retrieved by the event throw exceptions. Of course it's mostly assumption, but I have a sneaking suspicion that it's not the data that is owned by another thread, but the method that is executing in another thread. What this means is that with the design lined out above, I can get the data to the TextBox, but the StopProgress throws an InvalidOperationException since it's being called by the DataReceived event's thread.
After slapping myself in the head for my ignorance of such a simple concept, I determined that the assistance of someone more versed in this than me was required.

...And as soon as I find this person, I'll be sure to post the solution right way...

Friday, August 17, 2007

Hello, World.

main() {
printf("hello, world");
}

main( ) {
extrn a, b, c;
putchar(a); putchar(b); putchar(c); putchar('!*n');
}
a 'hell';
b 'o, w';
c 'orld';

public class HelloWorld
{
public static void main( String[] args )
{
System.out.println( "Hello world" );
}
}

... And my favorite...

namespace HelloWorldApp
{
public class HelloWorld
{
public static void main( String[] args )
{
System.Console.WriteLine("Hello world");
}
}
}

Anyone who can tell me the languages used above gets 10 points.

I'm nibbles&bits. I'm a Business Framework Developer (my own title) and self-professed C# geek. I figured my life was interesting enough to blog about.
As far as I can tell at this point, this blog will consists of mostly rants about work, C# code samples, and general news that happens to catch my interest.
My life is just starting: I'm getting a new car, and I'm 3 months away from being promoted at my job and potentially attending MIT soon after.
All I can say now is check back soon, and enjoy!