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.