Sunday, May 27, 2007

My Piaggio MP3

I am now the proud owner of a Piaggio MP3. At the time of this writing, I've had my MP3 for three weeks now, and I've clocked over 900 miles on it.



It was November 2006 when one of my coworkers piqued my interest in the Piaggio MP3. He owns a 200cc Vespa scooter, and with gas prices on the climb I started asking questions. One morning he pulled up a web site to "show me something cool." As you've probably already guessed it was a 3 wheeled scooter--the Piaggio MP3! As soon as I saw it, I knew I had to have one. I printed a picture and stuck it to my bulletin board at home. I had no idea they were coming to the states, but I just loved to look at it and imagine having one. I even picked out a place in the garage that could be it's home.

Piaggio announced the MP3 open house starting April 19th. As soon as the announcement was made, I went looking for dealers in the area. The BMW dealer sent a double sided brochure with technical specs. This I stuck to my cube in my office.

I actually visited the dealer three times to look at the scooter in the showroom, dragging any and every friend I could coerce to go to look at it with me. The dealer had two MP3's at the time, one graphite and the other silver. Of the two, the graphite was the more attractive of the two, but neither was particularly colorful. What I really wanted was the red scooter I had seen on the web and in the brochure. I asked about getting a red one. The dealer wasn't sure when one might come in, but he'd let me know.

On the way home the thought occurred to me that there must be another dealer somewhere with a red MP3 I could get a look at. A quick Google search revealed a dealer in Aberdeen, NC: Steve Jones Motorsports. After a quick call, I made arrangements to haul a friend with me to Aberdeen to go look at their red scooter the following Saturday.

Steve Jones Motorsports

As soon as I walked into the Steve Jones dealership in Aberdeen, I liked it. The showroom was bright and inviting, and everyone greeted me with a warm welcoming smile.

Tamilla introduced me to David, the manager of the dealership, and brought me out back to see the red MP3 they had just finished putting together. As soon as I saw it, I grimaced. This was not even close to the shade of red shown in the brochure or shown on the Kneeslider web article.

The actual color is really more of a burgundy with some purple in it. My friend agreed, this was not an attractive color. What were those Italians thinking? I considered getting one of these red scooters anyway and painting it later to a better shade, but I just couldn't get over the fact that I would have to ride around on a scooter that hideous red color until I could get it painted.

Oddly enough, one of the candy blue Suzuki SV1000's in the showroom caught my eye. David, the manager, told me the color was called "Plasma Blue." This color really appealed to me, and now the idea of getting the graphite scooter and painting it candy blue was starting to blossom. I'd seen photos of MP3s that were blue and if this had been an available color, I would have gone with it:




David had one graphite scooter, and it was still in the box. The dealer in Raleigh also had a graphite scooter that was already put together, but it already had 250 miles on it. I decided that if I was going to drop seven grand on a scooter, I wanted a new one. The following weekend my coworker drove me down to Aberdeen with his trailer to help me haul the scooter back to Cary.

I have to say that the entire Steve Jones Motorsports staff (Tamila, David, Janet, and their head mechanic Jay) all made this purchase experience very pleasant. They make a great team, and as much as this is going to sound like a plug, I really like these people and recommend this dealership to anyone who is considering buying an MP3. Even though I live an hour away, they always make me feel at home. I really like these guys. You will too.

I got most of my gear (including my Shoei RT-1000 helmet, and FieldSheer jacket, pants and gloves) from Triangle Cycles in Durham. If you need outfitting, Michael in the parts department is your man.

I've had my MP3 for three weeks now, and having gotten to know the bike a little better, I have some observations to offer.

Some Things I've Noticed About the MP3

  1. At slow speeds (at 5 to 10 mph) you don't counter steer--you just steer; however, as you speed up (anything over about 20 mph) counter steering kicks in.

  2. Cornering is a breeze (and a lot of fun). You can slow corner at speeds and radii that would drop an ordinary scooter.

  3. My fuel consumption is right at 63 mpg.

  4. After doing some controlled tests to get a feel for the brakes, I really laid into both the front and rear brakes a time or two. This thing REALLY stops.

  5. Under the seat is enough storage for my backpack, my extra shield, and a few other items. In addition to that there's a trunk (yes, a trunk :) big enough to store my helmet.

  6. On the highway I can generally keep up with traffic without any problems (much to the surprise of some automobiles).

  7. Maybe I just need to get used to it, but the seat becomes a little uncomfortable on rides longer than an hour.

  8. The speedometer registers about 8% higher than the actual speed. I've had it confirmed by multiple sources that this is typical for most motorcycles. For example, when the speedometer registers 75 mph, you are actually going 69 mph.

Some Things I've Noticed About Scootering in General
  1. When the outside temp drops below 80 degrees Fahrenheit, I need the jacket liner to stay warm.

  2. Jeans do not offer a lot in the way of of insulation. I always wear protective pants.

  3. It's hard to hear myself think at 60 mph without earplugs (even with my Shoei RT 1000 helmet).

  4. A bug hit my hand at 65 mph while I was wearing gloves. I can only imagine what it would have felt like without them.

  5. I like the reflective shield on my Shoei RT 1000 helmet. Looks like some kind of alien riding a bike :)

  6. My throttle hand cramps during long rides.

My First Road Trip

The first service for the MP3 is at 620 miles. Getting back to Steve Jones Motorsports was to be my first "long" trip on the scooter. The itinerary included visiting my parents in South Carolina. While there I planned to stop by Junior Freeman's shop (Chesterfield Auto Body and Paint) to get an estimate for a candy blue paint job on the MP3 and do some work with Dad in his shop.

I started out at 6pm Friday from Cary. I filled up a camel pack with water, partially frozen, it so I could stay hydrated on the road. I distinctly remember stopping at the side of the road just prior to taking the entrance ramp to Hwy 1, gathering up some courage and saying to myself, "Well, here we go..."


The trip down was really very nice. For Memorial Day weekend, I was surprised that the traffic was no worse than any other weekend. I do remember having to shift in my seat a few times to while trying to find a comfortable sitting position, and also the cramping in my right hand while holding the throttle open. I vowed that I would never do another trip like this without a cruise control. Once in Aberdeen (my half-way point), I traded my iridium shaded shield (now full of bugs) for the clear one, took a bio break, and put on my jacket liner. The rest of the trip was in darkness and I loved it.

The next morning, I woke at my parent's house and found a note written on a paper towel from my dad: "Robert, I'm down at the shop. Like the bike. Dad."

Dad and I did some work in the shop, and then I headed over to Junior Freeman to see about getting the MP3 painted. Junior showed me some of the Kosmic Kandy colors, and I found a blue that resembled the Suzuki. He said he could even add some extra flake to make it really stand out in the sun. He painted his son's Mustang candy apple red and silver and it was beautiful. He said that if I could get all the pieces removed for him to paint, he could do the job for 350 to 400 dollars, but add 75 for the Kosmic Kandy paint.

By noon, I had made it back to Aberdeen for the first service. More cramping on the throttle hand, so I asked about adding a cruise control. Jay looked at the throttle and concluded that the cheaper 40 dollar unit couldn't be made to work. The more expensive device could be installed but he would need to fabricate a part to get it to work. I told him to go for it. When it was all done, Jay said it was the first cruise control he'd ever put on a scooter :)

The remaining trip home was so much nicer! The throttle still needs adjustments to account for speed variations due to varying inclinations, but all I have to do now is adjust the throttle a few clicks one way or the other, and there it stays.

This afternoon I took the scooter to the car wash to knock off the bulk of the bugs and debris. At home I put the finishing touches with a good wax, Armor All, and tire black. It cleans up nicely :)

I've decided that I'm going to continue riding the heck out of the scooter during the summer months, and then dismantle the shrouding to get painted when it's too cold to ride in the Winter.


In closing, I just have some advice for Piaggio:

Gli Americani hanno bisogno dei colori bellissimi.

Ciao,
Roberto



Wednesday, May 16, 2007

Creating a Custom NAnt Task with Nested Elements

NAnt has built in tasks (e.g., the copy task) that utilize nested elements, and sometimes a custom task utilizing this feature is needed. While it's certainly possible to take advantage of some of the built in nested types already provided by Nant, the bulk of the standard nested types are file related (fileset, filterchain, path, resourcefileset, etc.). If you need something more specific to your application, or if you need vastly different or more complex types, the standard types that come with NAnt are not enough.

Fortunately, creating custom NAnt tasks with nested elements is not much more difficult than creating a standard custom task.

This post we'll walk through the steps to create a task we'll call iis.httperrors. This is a task I created to be able to set the http errors for a specific site on a Windows server.

This type of task is something that can be accomplished using vbscript...

Option Explicit
Dim objIISObject
Dim objHTTPErrors
Dim intIndex
Dim arrOrigHTTPErrors
Dim arrNewHTTPErrors(43)
arrNewHTTPErrors( 0) "400,*,FILE,C:\WINDOWS\help\iisHelp\common\400.htm"
arrNewHTTPErrors( 1) = "401,1,FILE,C:\WINDOWS\help\iisHelp\common\401-1.htm"
arrNewHTTPErrors( 2) = "401,2,FILE,C:\WINDOWS\help\iisHelp\common\401-2.htm"
arrNewHTTPErrors( 3) = "401,3,FILE,C:\WINDOWS\help\iisHelp\common\401-3.htm"
arrNewHTTPErrors( 4) = "401,4,FILE,C:\WINDOWS\help\iisHelp\common\401-4.htm"
arrNewHTTPErrors( 5) = "401,5,FILE,C:\WINDOWS\help\iisHelp\common\401-5.htm"
arrNewHTTPErrors( 6) = "401,7,FILE,C:\WINDOWS\help\iisHelp\common\401-1.htm"
arrNewHTTPErrors( 7) = "403,1,FILE,C:\WINDOWS\help\iisHelp\common\403-1.htm"
arrNewHTTPErrors( 8) = "403,2,FILE,C:\WINDOWS\help\iisHelp\common\403-2.htm"
arrNewHTTPErrors( 9) = "403,3,FILE,C:\WINDOWS\help\iisHelp\common\403-3.htm"
arrNewHTTPErrors(10) = "403,4,FILE,C:\WINDOWS\help\iisHelp\common\403-4.htm"
arrNewHTTPErrors(11) = "403,5,FILE,C:\WINDOWS\help\iisHelp\common\403-5.htm"
arrNewHTTPErrors(12) = "403,6,FILE,C:\WINDOWS\help\iisHelp\common\403-6.htm"
arrNewHTTPErrors(13) = "403,7,FILE,C:\WINDOWS\help\iisHelp\common\403-7.htm"
arrNewHTTPErrors(14) = "403,8,FILE,C:\WINDOWS\help\iisHelp\common\403-8.htm"
arrNewHTTPErrors(15) = "403,9,FILE,C:\WINDOWS\help\iisHelp\common\403-9.htm"
arrNewHTTPErrors(16) = "403,10,FILE,C:\WINDOWS\help\iisHelp\common\403-10.htm"
arrNewHTTPErrors(17) = "403,11,FILE,C:\WINDOWS\help\iisHelp\common\403-11.htm"
arrNewHTTPErrors(18) = "403,12,FILE,C:\WINDOWS\help\iisHelp\common\403-12.htm"
arrNewHTTPErrors(19) = "403,13,FILE,C:\WINDOWS\help\iisHelp\common\403-13.htm"
arrNewHTTPErrors(20) = "403,14,FILE,C:\WINDOWS\help\iisHelp\common\403-14.htm"
arrNewHTTPErrors(21) = "403,15,FILE,C:\WINDOWS\help\iisHelp\common\403-15.htm"
arrNewHTTPErrors(22) = "403,16,FILE,C:\WINDOWS\help\iisHelp\common\403-16.htm"
arrNewHTTPErrors(23) = "403,17,FILE,C:\WINDOWS\help\iisHelp\common\403-17.htm"
arrNewHTTPErrors(24) = "403,18,FILE,C:\WINDOWS\help\iisHelp\common\403.htm"
arrNewHTTPErrors(25) = "403,19,FILE,C:\WINDOWS\help\iisHelp\common\403.htm"
arrNewHTTPErrors(26) = "403,20,FILE,C:\WINDOWS\help\iisHelp\common\403-20.htm"
arrNewHTTPErrors(27) = "404,1,FILE,C:\WINDOWS\help\iisHelp\common\404b.htm"
arrNewHTTPErrors(28) = "404,2,FILE,C:\WINDOWS\help\iisHelp\common\404b.htm"
arrNewHTTPErrors(29) = "404,3,FILE,C:\WINDOWS\help\iisHelp\common\404b.htm"
arrNewHTTPErrors(30) = "405,*,FILE,C:\WINDOWS\help\iisHelp\common\405.htm"
arrNewHTTPErrors(31) = "406,*,FILE,C:\WINDOWS\help\iisHelp\common\406.htm"
arrNewHTTPErrors(32) = "407,*,FILE,C:\WINDOWS\help\iisHelp\common\407.htm"
arrNewHTTPErrors(33) = "412,*,FILE,C:\WINDOWS\help\iisHelp\common\412.htm"
arrNewHTTPErrors(34) = "414,*,FILE,C:\WINDOWS\help\iisHelp\common\414.htm"
arrNewHTTPErrors(35) = "415,*,FILE,C:\WINDOWS\help\iisHelp\common\415.htm"
arrNewHTTPErrors(36) = "500,12,FILE,C:\WINDOWS\help\iisHelp\common\500-12.htm"
arrNewHTTPErrors(37) = "500,13,FILE,C:\WINDOWS\help\iisHelp\common\500-13.htm"
arrNewHTTPErrors(38) = "500,15,FILE,C:\WINDOWS\help\iisHelp\common\500-15.htm"
arrNewHTTPErrors(39) = "500,16,FILE,C:\WINDOWS\help\iisHelp\common\500.htm"
arrNewHTTPErrors(40) = "500,17,FILE,C:\WINDOWS\help\iisHelp\common\500.htm"
arrNewHTTPErrors(41) = "500,18,FILE,C:\WINDOWS\help\iisHelp\common\500.htm"
arrNewHTTPErrors(42) = "500,19,FILE,C:\WINDOWS\help\iisHelp\common\500.htm"
arrNewHTTPErrors(43) = "500,19,FILE,C:\WINDOWS\help\iisHelp\common\500.htm"

Set objIISObject = GetObject("IIS://LocalHost/W3SVC")
objIISObject.HTTPErrors = arrNewHTTPErrors
objIISObject.Setinfo
Set objIISObject = Nothing


... but things get more difficult if you want to implement this in NAnt without shelling out the execution to a separate vbscript.

You might consider executing a series of "exec" commands in NAnt to accomplish this, but you'll discover there is no single command to set an individual http error setting (well, there may actually be, but let's say for the sake of argument that there isn't an easy way to do this atomically). Wouldn't it be great if we could just include lines in a NAnt script to specify these settings?

<iis.httperrors sitename="MySite">
<httperrorset>
<httperror definition="400,*,FILE,C:\WINDOWS\help\iisHelp\common\400.htm">
<httperror definition="401,1,FILE,C:\WINDOWS\help\iisHelp\common\401-1.htm">
<httperror definition="401,2,FILE,C:\WINDOWS\help\iisHelp\common\401-2.htm">
<httperror definition="401,3,FILE,C:\WINDOWS\help\iisHelp\common\401-3.htm">
<httperror definition="401,4,FILE,C:\WINDOWS\help\iisHelp\common\401-4.htm">
...

</httperrorset>
</iis.httperrors>


Good news! We can. More good news! It's not that hard to do.

Define the xml schema



Referencing the nant script above as our guide for a schema, the first line declares the task iis.httperrors with one parameter, sitename:

<iis.httperrors sitename="MySite">


It contains an embedded type, httperrorset which is a set of zero or more httperror definitions:

<httperrorset>
<httperror definition="400,*,FILE,C:\WINDOWS\help\iisHelp\common\400.htm">
<httperror definition="401,1,FILE,C:\WINDOWS\help\iisHelp\common\401-1.htm">
<httperror definition="401,2,FILE,C:\WINDOWS\help\iisHelp\common\401-2.htm">
<httperror definition="401,3,FILE,C:\WINDOWS\help\iisHelp\common\401-3.htm">
<httperror definition="401,4,FILE,C:\WINDOWS\help\iisHelp\common\401-4.htm">
...


If we really needed to get fancy, we could break apart the definition, for example:

<httperror url="C:\WINDOWS\help\iisHelp\common\400.htm" uri="FILE" suberror="*" error="400">


This is not difficult to do either, but the goal of the post is the embeded type itself so I'm going to stick with the simpler case. As an exercise you can make the modifications yourself to accomplish the more complex schema (see hints at the end of this post).

Create the Custom Class



The first steps involve creating a ordinary custom NAnt task. Here's a summary of the steps (but I highly recommend you read and make yourself familiar with Richard Case's blog post on creating custom NAnt tasks).


  1. Create a Class Library project. The assembly name should end in "Task" and you should reference Nant.Core.dll. We'll also be using the System.DirectoryService assembly to do the work, so you can also go ahead and include that, too.
  2. Create a class (or rename the initial class) with the name IISHttpErrorTask

  3. Add the necessary using statements:
    using NAnt.Core;
    using NAnt.Core.Attributes;
    using System.DirectoryServices;

  4. Have the class derive from Task and add the TaskName attribute to the class definition.
    [TaskName("iis.httperrors")]
    class IISHttpErrorsTask : Task
    {

  5. There is only one property to define: sitename (the name of the IIS site on the local machine to update). Add this in the standard way:
    class IISHttpErrorsTask : Task
    {
    string m_siteName;

    ///
    /// The site name as displayed in IIS
    ///

    /// Required.
    [TaskAttribute("sitename", Required = true)]
    [StringValidator(AllowEmpty = false)]
    public string SiteName
    { get { return m_siteName; }
    set { m_siteName = value; }
    }

  6. For now just add a "do nothing" Execute() method. We'll come back to it later.
    /// 
    /// Executes the IISHttpErrorsTask task.
    ///

    protected override void ExecuteTask()
    {
    // TODO: Implement the Execute() method.
    }


At this point, you should be able to build the project. If all goes well, you are now ready to create the nested type!

Creating the Nested Type



Now we need to define what a NAnt "httperrorset" is, and map how the data gets loaded from the NAnt script. Add a new member variable instance to the class we just created:

IISHttpErrorSet m_httpErrorSet;


You won't be able to compile after adding this declaration because we haven't yet defined what an IISHttpErrorSet type is, but we will define that class soon enough. For now though, go ahead and add the HttpErrorSet property and add the NAnt attribute "BuildElement". This attribute tells NAnt that there is additional instance data in the IISHttpErrorSet class to load from the script.

/// 
/// Used to specify the HttpErrorSet to configure.
///

[BuildElement("httperrorset")]
public virtual IISHttpErrorSet HttpErrorSet
{ get { return m_httpErrorSet; }
set { m_httpErrorSet = value; }
}


Ok, if you are like me, having dangling class definitions like IISHttpErrorSet is something that just nags at you, so let's get to it. Create a new class called IISHttpErrorSet, add the necessary using statements:

using NAnt.Core;
using NAnt.Core.Attributes;


Derive the class from DataTypeBase and also add the NAnt attribute "ElementName".

[ElementName("httperrorset")]
class IISHttpErrorSet : DataTypeBase
{


So now you can start to see how NAnt pulls all this together. In the IISHttpErrorsTask we declared a property with a BuildElement attribute, and we have just created a class with an ElementType attribute, and both have the same value. This essentially maps these guys to each other, allowing NAnt to build the "httperrorset" from the data in the script. Cool, huh? This should all compile without errors now, but we're not done yet. NAnt has no idea what kind of data belongs to this thing we have defined as an "httperrorset" so it's now time to get that going.

If you peruse the NAnt classes in the Object Browser you'll notice that NAnt has a convention of adding all the classes for their nested types in the NAnt.Core.Types namespace, whereas the tasks can be found in the NAnt.Core.Tasks namespace. You can (and probably should) adopt a similar convention for your own embedded types; however, for the purpose of making this example easier to follow, I've simply added the nested types to the same assembly as the task.


If we say that an "httperror" definition is essentially a string, we can define our "httperrorset" implementation as a collection of strings. Add a member variable to the IISHttpErrorSet to reflect this:

private StringCollection m_httpErrors = new StringCollection();


To make it easier to "get at" this collection from another class, let's go ahead and define a read-only property:

/// 
/// Gets the collection of HttpError strings
///

public StringCollection HttpErrors
{
get { return m_httpErrors; }
}


To have NAnt build this collection from data in the script we need to define a specialized property. This property has an the BuildElementArray attribute "httperror" defined.

/// 
/// The items to include in the httperrorset.
///

[BuildElementArray("httperror")]
public HttpError[] HttpErrorElements
{
set
{ foreach (HttpError httpError in value)
{
HttpErrors.Add(httpError.Definition);
}
}
}


Ok, I pulled another one of those dangling definitions. The HttpError type is undefined, so this will not yet compile. If we're just loading a bunch of strings and we've already defined an httperrorset as a collection of strings, why do we need this extra type? Because it will help us set up a complex element type later (if we need one).

We're going to define the HttpError type as an embedded class which derives from Element:

[ElementName("httperrorset")]
class IISHttpErrorSet : DataTypeBase
{
...

public class HttpError : Element
{
private string m_definition;

///
/// The http error definition.
///

[TaskAttribute("definition", Required = true)]
[StringValidator(AllowEmpty = false)]
public virtual string Definition
{
get { return m_definition; }
set { m_definition = value; }
}
}


This class has a single member variable (the string which defines the error) and a single read/write property for get/set operations. Notice also that the property has TaskAttribute and StringValidator attributes very similar to the TaskAttributes we've seen before.

Testing what we have so far



At this point you should be able to compile the assembly. You can test that the data gets loaded properly by adding the following to the Execute() method:

protected override void ExecuteTask()
{
foreach (string httpError in m_httpErrorSet.HttpErrors)
{
Project.Log(this, Level.Info, String.Format("Found httperror: {0}", httpError));
}
}


I'm going to follow up the implementation of the Execute() method using ADSI in a later post. There's actually a lot of ground to cover there and I don't want to shift the focus of creating the framework for the custom NAnt script nested types.

More complex Elements



I mentioned earlier in this post that it should be possible to create a more complex type for the "httperror" element:

>httperror url="C:\WINDOWS\help\iisHelp\common\400.htm" uri="FILE" suberror="*" error="400">


By now, it should be pretty obvious how you might go about this. In the embedded HttpError class we just added, just add some additional member variables and properties with the necessary TaskAttributes for each of the parameters. Presto. You now have a class that represents a complex element. Have fun!