| |
I was listening to one of Scott Hanselman's podcasts this morning and Scott made a comment about people thinking that agile methodologies were and excuse for sloppy project management (NOTE: Scott wasn't of this opinion, he just stated that some other people thought this).
I am fully bought into SCRUM, and whilst we don't implement if fully at work (we have our own handcrafted way of doing it that really works well for us) I think it brings great benefits. One of the real stumbling blocks with people adopting SCRUM / Agile is getting over the mind set that they can determine the delivery dates and that they can force the development team into delivering everything they want by that date. I truly do not believe that it is possible to fix both scope (requirements) and timescale (delivery dates) in a software project, even having done lots of design up front. I have written before on the perils of this approach...
Giving up all control of dates is something that takes quite a psychological leap of getting used to, but when you are there, you see all the benefits. Let me give you an example...
Think of The Beatles. Their 'products' were (obviously) their songs, they were all passionate about what they were doing (providing great music to the masses) and they did it really well - everyone loved their product. When Lennon and McCartney (and Harrison) sat down to write a song, if they'd had a limited time then the quality would have suffered. They were not limited in time and they produced great things.
That is one of the things you have to accept in agile - crafting great software products is an skill and an art, for the developers sometimes their creative juices flow in abundance, sometimes they don't. To meet strict timescales, something has to give, and that something is scope (or quality, but you don't really want that do you ??...)
GEO 51.4043197631836: -1.28760504722595
Originally I had my website set up to default to my blog (so dasBlog was installed at the website root level instead of in a /blog subfolder). Recently I have been writing some Web Applications and wanted to restructure my website so that there is a subfolder for each app / main area (blog, projects, etc). The problem being that all the links out there point to http://www.kapie.com/blog/somefile.aspx and these would all be dead link when moved to http://www.kapie.com/blog/somefile.aspx, so I needed to respond with a HTTP 302 to the original links and tell the client that it should temporarily redirect to the new location (changing to a HTTP 301 when I am happy that it is all working). This called for a HttpModule... namespace KSL
{
public class BlogRedirector : IHttpModule
{
private EventHandler onBeginRequest;
public BlogRedirector()
{
onBeginRequest = new EventHandler(this.HandleBeginRequest);
}
void IHttpModule.Dispose()
{
}
void IHttpModule.Init(HttpApplication context)
{
context.BeginRequest += onBeginRequest;
}
private void HandleBeginRequest( object sender, EventArgs evargs )
{
HttpApplication app = sender as HttpApplication;
if ( app != null )
{
HttpContext context = app.Context;
string host = app.Request.Url.Host;
string requestUrl = app.Request.Url.PathAndQuery;
if (requestUrl.Contains(@"/blog/")
|| requestUrl.Contains(@"/foo1/")
|| requestUrl.Contains(@"/foo2/")
|| requestUrl.Contains(@"/foo3/")
|| requestUrl.Contains(@"/foo4/")
|| (requestUrl.ToLower() == app.Request.Url.Scheme + @"://" + host + @"/default.aspx"))
{
return;
}
else
{
Uri newURL = new Uri(app.Request.Url.Scheme + @"://" + host + @"/blog" + requestUrl);
context.Response.RedirectLocation = newURL.ToString();
context.Response.StatusCode = 302;
context.Response.End();
return;
}
}
}
}
}
This basically allows me to redirect any url that does not contain /blog/ or /foo*/ or is not /default.aspx by sending a 302 status code and adding /blog/ to the url to generate the redirect url. The compiled DLLs were stored in the bin folder off the root and the web.config in the root needed the following adding... <?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<httpModules>
<add type="KSL.BlogRedirector,BlogRedirector" name="BlogRedirector" />
</httpModules>
</system.web>
</configuration>
This seemed to work fine some of the time, but was a bit hit and miss.... Further investigation pointed me at the web.config files in the subfolders and that they would inherit the sections from the root web.config. So all the subfolder web.config files got the following added. <?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<httpModules>
<remove name="BlogRedirector" />
</httpModules>
</system.web>
</configuration>
GEO 51.4043197631836: -1.28760504722595
Just recently we have been interviewing for a new developer.
I have found it incredulous, the amount of people with considerable experience that cannot do hex / dec conversions. One of the things we do during an interview is to have people write code - this is normally a short 10 minute test where we give them an app with one function that converts a hex string to decimal, we ask them to refactor the function so that it no longer using the API call to do the conversion (they have to do it themselves in code).
We go to great lengths to make sure we have explained the task correctly, sometimes we prompt them earlier in the interview with a questions about converting - 'What is 16 represented in Hex, what is 30 hex in decimal etc). We even provide a working application and allow them to play with it for a few minutes to familiarize themselves with it's intent and operation.
The task is designed to show us the coding style of the candidate, but we've found that instead of looking at their code we're looking at blank pages - there seems to be a glut of people (at least, that I am interviewing) that don't even know how to start. We've had people simply stare at us blankly, people just sit and twiddle a pencil, people get flustered and simply say 'I cannot do it, sorry'
Are we just getting below average or unsuitable candidates or have people forgotten this skill (working in number systems other than base 10). Have today's development tools abstracted all this low level stuff away so much that people just don't need it anymore (and therefore have teaching institutes stopped covering it) ??
GEO 42.2814047728377:-71.5726983547211
I have completed stage one of GeoRss enabling dasBlog.
In the config page I added some options for enabling GeoRss, specifying a default lat/long and enabling ‘integration’ with Google maps. There is also an option to use the default lat/long for any non geocoded posts.
If GeoRss is enabled then the edit entry screen provides textboxes to allow specifying lat/long (populated with defaults from config page).
If the google maps integration is enabled then you’ll get the ‘Show Map’ button and clicking it will display a map which you can move around until you find the location and then click on the location to get the lat/long texboxes populated.
If you have existing non geocoded posts then you can have the default lat/long added to those if you wish.
I puzzled around for ages when trying to display the georss in google (http://maps.google.com/maps?q=yourfeedaddress) – it kept telling me that the feed was invalid. I eventually found that feedburner was adding <atom10:link blah blah /> to the xml which for some reason google maps thinks is invalid. The only way I could find to prevent feedburner adding the atom link was to turn OFF the ‘Browser Friendly’ feature in feedburner.
So – stage 2...
The work I still want to do with this is basically to add macros to get lat/long - fairly easy I guess, and then some way to specify lat/long from Windows Live Writer (and other offline blog clients) – a little more complex. Scott mentioned a geo microformat and from my initial looks seems to be a good route to take - watch this space...
Now it is simply a case of retrofitting the geo info into all my old posts...
I registered for Mix:UK a few weeks ago and today got an email from the Mix team about a social networking tool they are using to help people make the most of the conference.
They are using backnetwork, which allows creation of profiles, blogging, photos, adding friends / colleagues etc (all the usual social networking stuff). It also allows you to add any colleagues / friends automagically from any previous backnetwork events you have been to.
This is certainly a step in the right direction, but it's missing an 'auto-suggest relationship' feature that could analyze your profile (or tags) and suggest other people who have similar interests.
So... my profile is here, anyone interested in any of my tags feel free to contact me and set up a meeting/chat/lunch/coffee/whatever...
Some of the code I have for importing data (from ACT! 2000) to MS CRM creates new 'PhoneCall' activities / objects. The problem is, that it seems MSCRM does not allow you to programmatically modify the 'create date'.
Here is the code I use...
CrmDateTime start = new CrmDateTime();
start.Value = DateTime.Parse("10/08/2005 12:30");
pc.actualstart = start;
pc.scheduledstart = start;
CrmDateTime end = new CrmDateTime();
end.Value = DateTime.Parse("10/08/2005 14:30");
pc.actualend = end;
pc.scheduledend = end;
pc.subject = "Phone call regarding sales of Widgets Q2/2005"); ***
string desc = "Start : " + pc.actualstart.Value + "\n";
desc += "End : " + pc.actualstart.Value + "\n\n"; ***
desc += "The details of the phone call go in here");
pc.description = desc;
pc.regardingobjectid = new Lookup();
pc.regardingobjectid.type = EntityName.contact.ToString();
Guid contactGuid = new Guid(guidOfContactWeTelephoned);
pc.regardingobjectid.Value = contactGuid;
The actualstart and scheduledstart (and ends) get populated with the current datetime (this seems to happen if the time they are set to is in the past).
Note the two lines between the ***'s - this is my solution / workaround and simply include the start/end times as text in the body/description of the phone call object.
So dasBlog 2.0 (running under AS.NET 2.0 and medium trust) is now released. Scott has a great write up on it. I have been running this web site on it for a few weeks now and it's been fine. We sneaked in a couple of last minute features (one of mine was a new macro for rendering the navigation links as an Unordered List instead of a table with one cell per row). I've been spending a fair bit of time on dasBlog recently - extending activity Rss feed and working on georss integration. I have a georss version working with a simple UI allowing the user to specify Lat/Long for each post and then a link to view the feed items in Google maps. I have not checked it in yet as I want to provide a slicker UI for specifying the location (maybe a map that allows you to drag/select the location or enter a zip code and view the location then use that). I also want to provide options for getting the location in when using blogging clients (Windows Live Writer etc), not just the web based 'add entry' UI. There is also some great work going on with Clemens Vaster and other really revamping some of the architecture bits and bringing dasBlog to the next level. Watch this space...
I'm in Western Mass (Westboro, MA) again this week and while that normally means 16 hour days (there is nothing much else to do but sit around a hotel room so why not...), this week I decided to work on a personal project for a bit.
Part of it was some code to check a machines public IP address and update it to DynDNS. Luckily they have an excellent developers / API section that explains everything you need to do this. There are two sections to it, detecting the machines public IP address and updating the hostnames associated with your account, both of which they provide a service for.
It's pretty easy to follow and within an hour or so I had come up with a class to do both tasks. Here is a snippet for the 'CheckIP' function:
public static string CheckIP()
{
// check the current public IP address
string ipString = string.Empty;
WebClient wc = new WebClient();
try
{
string result = wc.DownloadString("http://checkip.dyndns.com");
return ParseCheckResult(result);
}
catch (WebException)
{
ipString = string.Empty;
}
return ipString;
}
Just call a URL (http://checkip.dyndns.com) and it returns your public IP address, there was some parsing of the return text but it is pretty simple. Click the link and see for yourself.
Next was the 'UpdateIP' function, here's the snippet:
public static string UpdateIP(string username, string password, string ipaddr, string hosts)
{
// check the current public IP address against what we want to update to
string updateurl = "http://members.dyndns.org/nic/update?hostname";
string result = string.Empty;
WebClient wc = new WebClient();
string url = string.Format(@"{0}={1}&myip={2}&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG", updateurl, hosts, ipaddr);
try
{
wc.Credentials = new NetworkCredential(username, password);
wc.Headers.Add("User-Agent", "KSL - WHS Updater - 1.0");
It is basically just a case of passing a querystring with all the details to http://members.dyndns.org/nic/update , ensuring you set the credentials to your DynDNS account username and password and specifying a unique User-Agent.
The complete project files (including a small 'user' application to test it) can be found here (61K).
So, this is something I learned on the Certified ScrumMaster course by Danube Technologies.
It really clarified for me that any sense of control over software deadlines is, pretty much, only a 'perceived' sense of control.
Yes, we can strongarm, use peer pressure or rigid authority to make developers work longer hours, but then they spent more time idling during the day. Someone, somewhere has written a great article on it (Note, after spending an hour looking at Joel On Software thinking the link I wanted was there: Link it here when found)
Anyway, the graph - let me explain....
A development team has a normal work rate (given all things are equal, interruptions, bug fix workload, training , yadda yadda...) in this case the work rate is 30 'units' in a 15 day period (the red line). Supposing we 'force' them to up their productivity to 40 units in a 15 day period (dotted green line). They will work at their normal rate initially and then soon recognise that this will not meet the deadline - at that point they 'speed up' (this is the change in the solid green line at the 7 day point).
What you have actually done (most likely) is generated a 'technical debt' - because the way the developers 'sped up' (wrote more code) was by sacrificing some other aspect of the development (quality, test coverage, peer reviews etc).
So you have a 'technical debt' that needs to be made up during the next period. Even if we go back to the normal rate of work we are not going to get 30 units done in the 15 days, oh no, first we have to make up the debt, then start on the 30 units - which will likely need 'speeding up' to meet deadline.
What is worse though, is that now you have set the expectation that the team can deliver 40 units in the 15 day period, so your next schedule had 40 units factored in, and the next, and the next until ..... well, it's like spending more on your credit card every month than you are paying off, at some point you can go no further .
The moral of all of this ? Who knows... get a card with a long 0% balance transfer rate ??
So I just spent two days in London on a 'Certified ScrumMaster' training course run by Danube Technologies.
The instructor was a Dan Rawsthorne and although he liked to reference things to the military, he covered a great deal of topics and had an incredible wealth of experience - some of the real world example really struck home with me.
It seems that a common trait is people just not grasping the whole concept of (basically) handing over control to the development team. There were many questions about where the project manager fitted in, who really managed the team etc etc.
It is tough to grasp if you currently have the perception of 'having control of the process at the moment'. It suddenly hit home for me as I was traveling into London on the train for the second day and thinking about why this current round of QA release testing was taking so long.
According to the QA guys, the build they had been given originally for this release was pretty poor quality (thankfully sorted out now). I was wondering why and thinking back to when this release was being coded and realized that at the time I was pressuring he developers because we were 100 odd hours behind schedule. We managed to get back on track and I 'thought' the reason was because I had beaten/pushed/cajoled/whatever the guys into increasing their work rate.
In reality, what had happened was that I had forced them to deliver early and the way this was done was by sacrificing quality (you cannot change the laws of dynamics - right). The fact was we did not get visibility of that until further down the line in the testing stage.
So, have a think about whether you do actually have control, or are you just exerting force on one particular aspect of the 'whole' that is squeezing/reducing another aspect of the same 'whole'.
When this clicked in my head, it all made sense - the SCRUM methodology became clearer, what needing doing to get it working, why it worked (especially, why it isn't a bad thing to hand 'control' over to the team).
One of the slide deck was about 'technical debt' and it really drives this point (sacrificing something for earlier delivery) home - I'll blog about that later..
Recently I was reviewing the progress I had made with the development team (at work) since to took over responsibility for them around 8 months ago. It is actually pretty impressive (even if I do say so myself):
- Agreed and published a set of common / shared values and mission statement (gives shared ownership and a sense of responsibility)
- Upgraded to Visual Studio 2005 - providing them with the best tools to do their jobs (improves productivity).
- Migrated from Visual SourceSafe to Team Foundation Server which is much more stable and has better branching and merging facilities (improves productivity).
- Implemented an overnight build process and some ownership/responsibility for 'breaking' the overnight build (improves quality).
- Implemented a system that allows each developer to build in one click (improves productivity).
- Implemented automated testing of the overnight builds and any build version that leaves the building to go to customers (improves quality).
- Provided them all with dual monitor configurations and larger LCD screens (improves productivity).
- Automated the build/version number change (improves quality and tracking).
- Implemented new processes for how we ensure bug fixes are rolled into future version (improves productivity and quality).
- Automated most of the help / manual / documentation build process (improves productivity).
- Provided them with 64 bit servers for testing / virtualization (improves productivity).
- Implemented SCRUM (more like a watered down flavour of it) thus giving more visibility to deadlines/timescales and goals/objectives (gives shared ownership and a sense of responsibility)
- Documented a technology vision statement that outlines the technologies, processes and our 'mantra' at a high level while allowing the individuals to interpret the implementation at the next level down. (gives shared ownership and a sense of responsibility)
- Implemented a real bug tracking database (improves quality)
There are two things about this though:
- When I say 'implemented' or 'upgraded' etc what I really mean is the team did it, my involvement in most of these items has been to question if we had it, how could we do it (better), did we need it, how did other people in the industry solve those kind of problems.
- When I say 'improves productivity' this is purely based on a 'gut feel' measurement (and often general consensus from the guys working in/with it), some of the items I know for sure (like the time SourceSafe corrupted on us and two guys spent hours with print outs and pens ticking off the files as we manually merged them, and reducing the several hour version change process where we manually edited the version is 2976429865264264 files and the kicked off a build, to 20 minutes)
This is not me blowing my own trumpet; I didn't really do much of this except ask the questions that kicked of the activity.
When was the last time you questioned what you are doing or compared it to what / how others in the industry are doing it ?
I use dasBlog as the vehicle/technology for this site and currently have it configured at the root of the website (so you go to http://www.kapie.com/blog and you get directly into this blog). This is kind of an unusual setup as most people put it in a /blog subdirectory or virtual directory. I have plans to put a fair bit more static content up so I wanted to change to the standard of /blog subfolder - the problem is that all my current links will fail when I move the content along with all the content google has cached / indexed about the site - Not Good. Scott Hanselman suggested in response to a question I posed on the dasBlog mailing list that writing a HttpModule with a HTTP temporary redirect (HTTP response code 302) might be the way to go and reckoned it might only be a handful of lines of code.... I was pleasantly surprised at how easy it was... I have included the code below in case anyone else might find it useful. It is pretty specific at the moment (takes the requested URL and if it does not include /blog/ then it insert it) but it could easily by made generic (RegEx or the like). Happy to share the actual source files if anyone wants them - drop me a mail/comment. using System; using System.Web; using System.Text; namespace KH { public class Redirector : IHttpModule { private EventHandler onBeginRequest; public Redirector() { onBeginRequest = new EventHandler(this.HandleBeginRequest); } void IHttpModule.Dispose() { } void IHttpModule.Init(HttpApplication context) { context.BeginRequest += onBeginRequest; } private void HandleBeginRequest(object sender, EventArgs evargs) { HttpApplication app = sender as HttpApplication; if (app != null) { string host = app.Request.Url.Host; string requestUrl = app.Request.Url.PathAndQuery; if (requestUrl.IndexOf("/blog/") == -1) { Uri newURL = new Uri(app.Request.Url.Scheme + "://www." + host + "/blog" + requestUrl); app.Context.Response.RedirectLocation = newURL.ToString(); app.Context.Response.StatusCode = 302; app.Context.Response.End(); return; } else { return; } } } } }
This week (so far) has been good - in terms of completing things, productivity and new products. First off, Microsoft finally released PowerShell for Vista. No more having to 'play' on my old lab machine to get to grips with this stuff. There seem to be a number of people reporting failed installs(due to EFS encryption being disabled), just read the comments of the PowerShell blog announcement. Next, we're just coming to the final couple of days of a 'Supporting Exchange 2007, Office 2007 and Vista SPRINT' at work (we use a form of SCRUM as our development process) - all is looking good and we have beta sites lined up. Then, I noticed Eileen's (the most communicative Microsoft employee on the planet) post about Office 2003 to Office 2007 command references. An interactive demo from Microsoft when you can click the toolbars and menus of an Office 2003 application and it tells you how to find the equivalent command/function in Office 2007. I spent some time finding the 10 or so commands I'd been having difficulty with and increased my productivity. Here's her post : http://blogs.technet.com/eileen_brown/archive/2007/01/31/old-to-new-reference-guides.aspx Then late last night (again at work) we just completed our internal testing before sending our Archive One product for Microsoft Platform testing. We are testing against 5 of the 6 platform tests (we don't fit into the 'Managed Code' test category as we make extensive use of MAPI which basically requires C++ / Unmanaged code)
One of the difficulties I have encountered whilst trying to change the mindset of our development team is getting buy in for the concept of 'Committing to backlog items'. This is where the development team get together with the Product Manager(s) in a time boxed (8 hour) meeting (split into 2 sets of 4 hours, the first one to discuss requirements and the second one to discuss / agree what they will commit to doing in the 30 day Sprint). I've been thinking about this a lot recently - I really want the guys to fully embrace SCRUM (and they want to also) but this particular concept is proving a difficult hurdle to overcome. I think that the root of this is that the team had been working in a classic Waterfall methodology (and had been burnt a number of times on late delivery of features / releases). The word commit seems to be the problem. - Their current understanding / interpretation of commit is : "it must be done or we're going to take heat over late delivery".
- Whereas the agile understanding / interpretation of commit is : "we will do everything we reasonably can to deliver the feature / release"
It's actually quite a big step / leap of faith if you have only been used to "it must be done by dd/mm/yy". I like it's going to require me forcing the issue so that we do one Sprint in this manner and then demonstrates to everyone that there is no blamestorming if we're late or drop features The reason (I suspect) that it works is that with SCRUM you get continual and accurate feedback from the daily Sprint meetings, so if things do go off track then it's visible and corrective action can be taken earlier.
At work I have been the champion of an Integration Framework for our Archive One product. This is a set of API's, events and open file formats embedded into the Archive One products that allow customization 'around the edge'. The kind of thing that can be done with the Integration Framework is running searches, interworking with the AD schema, running archive policies, integration into Network Management Systems or Document Management Systems and the like - it really opens the product to other business systems as either the slave (other systems controlling Archive One) or the master (other systems being controlled by Archive One). We have been writing up the API documentation recently and had initially started this as a (Word) document we would publish to customers and developers. I looked into NDoc which now seems dead (at least for VS2005 and .NET2.0) and then came across Sandcastle. Sandcastle is Microsoft's answer to NDoc, it allows the collation and generation of documentation from the XML comments embedded in your code Right now it is at CTP stage and is command line driven - a few projects have sprung up which provide a UI for it (backing on to the CLI). One is a VS2005 add in (which I installed but it wasn't immediately obvious how to use it, so I didn't really pursue) and other is SandcastleGUI - this provides a single form where you choose the options, point it to the folders containing the Sandcastle app, the code DLLs and where you want the docs to end up and then simply kick it off. When you hit 'Start Documenting' it closes the form and fires off a command prompt window (batch files) which in turn fires off other command prompt windows until it's collated everything and built the documentation. It allows you to generate html documents - a whole website, multiple pages, images etc or a compiled help file (.chm) or even both if you wish. Another of the options is 'Online MSDN Links' this provides a MSDN link for every object type it can find in the base class library. Be warned enabling this option means it has to reflect through about 14500 classes / objects in the framework so that they can be indexed and used if found in your app / library. Enabling this option increases the time to generate from about 1 minute (on my Dual Core laptop) to about 15 minutes - I'd recommend disabling this option whilst developing the app / library and only enable it when you are doing the final or release build. The resulting documentation is really good quality - pretty much the same format as MSDN documentation. For the effort of a few extra XML comment lines in your code this provides great value and a very comprehensive resulting document. The easiest way I have found to integrate this into VS2005 is to set it up as an External Tool. I create a folder under the solution folder named 'Documentation', I run SandcastleGUI manually the first time around, do all the required configuration and then save the settings to a file in the solution directory (don't save it in the 'Documentation' folder as that is cleared out with each Sandcastle run. I have an 'External Tool' configured which points to SandcastleGUI with parameters of : /document $(ProjectDir)\settings.SandcastleGUI (I called the settings file 'settings.SandcastleGUI') and called the tool : Sandcastle This Project This allows me to simply click this tool and it will go away and create all the documentation (with my chosen parameters) for the project. As I mentioned this is CTP at the moment - I look forward to the release version, hopefully fully integrated into Visual Studio and stylesheets that exactly mimic MSDN.
At work we are heavily into MAPI (we provide email archiving solutions). MAPI is Microsoft's mainstream supported and recommended protocol for accessing Exchange. There are currently two main versions of MAPI - 'Exchange MAPI' and 'Outlook MAPI'. Exchange MAPI is the version that is installed when you install Exchange System Manager (ESM) and is the recommended version to use for enterprise applications that work with Exchange. Outlook MAPI is the version that is installed when you install Outlook - it is NOT recommended for enterprise applications. Now, there are also two types of PST files - the original Outlook 97-2002 version and the newer Outlook 2003 Unicode version (Outlook 2003 supports both versions, but no other version of Outlook supports the Unicode version). The problem is that the ESM version of MAPI does not support the 2003 / Unicode version of PST files either. This is real pain, meaning that we have to use the ESM version (if we want to go with Microsoft's best practices) for our application, but if we want to support the Unicode PSTs we need to somehow use the Outlook version (NOTE: Microsoft absolutely do NOT support both versions of MAPI installed on the same machine). Apparently the new MAPI 2007 version (that will be downloadable for use with Exchange 2007) was initially slated to support both, but, I've since heard this will not be the case. So currently (and possibly in Exchange / Outlook 2007), the only way to work with Unicode PSTs is via Outlook MAPI.
In the first version of my GPS Library software (written in VB6) I had a 'Protocol' property that indicated which protocol we were using (for example Garmin, Magellan, NMEA etc). Then, every time we needed to do something at the protocol layer (Sending or Receiving data) we had an if statement :
If gps.Protocol = GpsProtocol.Garmin Then ' Do it this way EsleIf gps.Protocol = GpsProtocol.Magellan Then ' Do it that way Else ' Do it the other way
Horrible and messy !! Adding a new protocol meant going back to every function and amending it.
In the new .NET version, it's much more object oriented and I've used a number of software design patterns.
To solve the above problem I used a Factory pattern. The Factory pattern allows you to create objects but let the client decide what type those objects should be. So, I can basically let the client set the Protocol property and then, depending on that setting create a different object for each protocol.
I start with a base class that all the 'codec' classes inherit from, then in the 'creation' (when the client creates the object) I look at the Protocol property and choose which concrete class to return:
GpsCodec is the base class and the concrete classes are GarminCodec, MagellanCodec etc. I have a static method in the base class called 'CreateCodec' that takes a 'Protocol' parameter. So it looks like this
public static GpsCodec CreateCodec(GpsProtocol protocol) { switch (protocol) { case GpsProtocol.Garmin: return new GarminCodec(); break; case GpsProtocol.Magellan: return new MagellanCodec(); break; default: return null; // error break; } }
So now I can simply create a codec class based on the Protocol property and use that for all the communication to / from the device. Adding a new protocol is simply a case of creating the new class and modifying the CreateCodec method - much simpler.
Watch this space for the free .NET GPS Library (including source).
In the first version of my GPS Library (written in VB6) I had a big horrible multiple nested if statement that handled the intelligence behind what to do when a new frame of data was received - if we were sending Waypoints to the GPS and the frame was an Acknowledgement (ACK) then we would send the next one, if it was a Not Acknowledgement (NACK) then we'd retransmit the last one - everything that happened depending on the current context (what we are currently doing). As there were about 20 different things we could be doing at the time and about 20 odd frame types, this was real messy and probably had a Cyclometric Complexity of 9292736592 (or more)...
My .NET version of the GPS Library makes extensive use of Software Design Patterns - to solve the problem above I used a 'State' pattern.
The State pattern is used where your software has multiple states and various actions or stimulations that make it change between states (but it is always going to be in one of the predefined states). I guess it a pretty common pattern for communication systems.
I have a base class called GpsState, that contains all an (overrideable) method for each action and stimulation and each method has a default implementation of something like
throw new Exception("Invalid action for state :" + this.StateName);
All the state classes inherit from this base state class.
Lets take (much simplified) sending Waypoints as the example:-
The GPS object has a reference to the current state class. This starts off in the stateIdle (doing nothing). We then call the stateIdle.WriteWaypoints() function. In this function we send the first Waypoint and change the state to stateWritingWaypoints. The stateWritingWaypoints overrides the 'GotACK' method and provides it own implementation (which is simply to send the next Waypoint). If any of the other methods are called whilst in the stateWritingWaypoints then the implementation from the base class is called and the caller receives an exception. When I've sent the last Waypoint then I change state back to stateIdle so that other actions can be called again
So my class for stateWritingWaypoints handles writing Waypoints and nothing else - this makes the code really clean and simple. The downside is that you end up with many more (small) classes in your code.
Watch this space for the source to the free .NET GPS Library.
About 4 or 5 years ago I wrote a GPS ActiveX control in VB6.0. Although it work really well (and fast), it was difficult to maintain - to be honest it was pretty messy, a large monolith with some real horrible 283654276354 line functions. I sold it as Kapie Systems for a while and had some good success with it, it was mentioned at a couple of GIS conferences etc... Anyway, I had been meaning to update it (to .NET) for sometime, but I wanted to do it right, make it easy to extend (to plug-in other GPS protocols) and easy to maintain. I just got a basic .NET implementation of it working last night (at last !!). It's Garmin propriety protocol only at the moment, but the whole framework is there for extending to other protocols (which I'll be adding shortly). I also added import and export to GPX format, which is a great portable standard - the GIS / Mapping community has been crying out for this for ages. I expect to have something releasable in a few weeks (it will be open source) but if anyone wants an early view or wants to discuss my plans ideas then let me know.
This is the forth (and last for the moment) article on Writing Supportable Software, helping all those customers and support engineers work with our software products and troubleshoot them themselves (i.e. going as far as possible down the troubleshoot path before referring it back to you). You can the first article here... and the second article here... and the third article here... This final article is all about paranoia and the fact that customers (as much as we love them, and need them to pay our salaries) are not to be trusted one little bit... We've all been there, troubleshooting a problem, ask the customer what parameter X is set to, go off down a whole path of investigation, find nothing, scratch our head, check parameter X for ourselves and find it's set to something different (and the whole problem resolution falls into place). It's not that customer purposely try to be difficult, simply that they don't know the system / software as well as you do - quickfire questions about the Primary Master Lookup Port and the Lookup Master Access Port get them all confused - they're probably under pressure for their boss to get it fixed, they've tried a whole load of things, the last time they looked it was set to 5465ABBG_Master but in the confusion they forgot they changed it to Lets_Just_Try_This_Option. They best way to determine what variable / options / configuration your system is using, is to write it out to a trace file, on start up, before a processing run, before you use it (whenever is appropriate - and that means, if it could have been changed between now and last time we wrote it to the log then write it again) If ever there has been a thing that wastes the time of support people it is working on the assumption that, the value the customer told them it was set to was the actual value that it was set to.
This is the third article on Writing Supportable Software, helping all those customers and support engineers work with our software products and troubleshoot them themselves (i.e. going as far as possible down the troubleshoot path before referring it back to you). You can the first article here... and the second article here... This article is all about identifying when and how you are using external systems and services. Most enterprise application work with external technologies or services, for example the company I work for (C2C) build applications that work with Microsoft Exchange Server, maybe you consume a particular web service or make LDAP calls. Whilst you can reasonably expect the customer to provide a stable working environment for your system / software, life is not always that rosy. I've worked with Enterprise systems that have been dependant on SQL Server, Exchange, LDAP, Web Services, VoIP gateways and many other systems... For experience, there is nothing that irks a customer more than a vendor who points the finger blindly at some part of the environment or system infrastructure. As a support guy for a vendor you have to take the pain and go that extra mile to hold the customers hand and demonstrate how / why / where the environment is going wrong. Developers can make this considerably easier by identifying (in a log file or the like) exactly what services your calling out to - and how long it's taking. I can't tell you how many times I've been troubleshooting an issue only to eventually end up some service the software was expecting to just work was not just working - but that's only half the battle, when you've found that you then need to demonstrate to the customer that it's the service that is not working (and if it was working then your system would be working also) A great example of this is customers have some kind of screwed up AD implementation, so we'll make a LDAP call thinking it's going to the local DC but in reality it finding some old dusty DC in Alaska on the end of a Dial Up connection and it will time out. Another would be a system making calls to a SQL server but the machine making the calls cannot connect to the SQL Server, maybe because there is a firewall inbetween (and the SQL network port 1433 ?? is locked down) So, when writing your code, identify these external service and provide the verbosity in your log files or your application status screen that allow the support engineers to determine which services are failing (and to demonstrate the fact to the customer) I haven't even touched on how often we find that our systems / software stops working, customer calls and you spend hours troubleshooting only to find that the customer took a service offline, or decommissioned a server, or added a firewall - you get the idea...
| | |