Table of Contents

2011-12-12 ASP.NET and .NET Microframework HTTP web server together (part 1/2)

I will show in this post how to call a web page from a .NET Microframework board which has implemented a HTTP Web Server and get results from it. The idea is to do a mix of previous posts. You are more and more to follow this blog so you may now be familiar with my implementation of a Web Server on a Netduino board. If not, read first the article which explain how to create such a web server on a board with no OS like ASP.NET or Php or Java.

When you'll have read it, you'll need also to read how to read a setup file in .NET Microframework. This is used to setup the board which will pilot led to light up my Lego city.

Once you've done that, you'll also need to read the post on how to display overlay images in HTML using Javascript. Yes, I know lots or reading before starting but I don't want to go again thru all the code. I will only focus on the "new" parts.

First let start with 2 web pages on the Netduino board. The first one allow to switch on and off one led. The code of the function looks like:

private static void ProcessSwitch(HttpListenerContext context) { 
    HttpListenerRequest request = context.Request; 
    HttpListenerResponse response = context.Response; 
    // decode params 
    string strParam = request.RawUrl; 
    ParamPage MyParamPage = new ParamPage(); 
    bool bLight = false; int iID = -1; 
    Param[] Params = decryptParam(strParam); 
    if (Params != null) 
        for (int i = 0; i < Params.Length; i++) { 
            //find both params 
            int j = Params[i].Name.ToLower().IndexOf(MyParamPage.id); 
            if (j == 0) { 
                Convert.ToInt(Params[i].Value, out iID); 
            } 
            j = Params[i].Name.ToLower().IndexOf(MyParamPage.lg); 
            if (j == 0) { 
                Convert.ToBool(Params[i].Value, out bLight); 
            } 
        } 
        // if the ID value is valid, just light up the light :-) 
        string strResp = strOK; 
        if ((iID != -1) && (iID < myLegoLight.Count)) { 
            ((LegoLight)myLegoLight[iID]).Light = bLight; 
        } else { 
            strResp = strProblem; 
        } 
        strResp = OutPutStream(response, strResp); 
    }

In previous posts, I've explained how to handle parameters from a web query and transform them into real value. That's what the first part of the code is doing.

Please note that strOK = "OK" and strProblem = "Problem". We will need this later on on the ASP.NET side.

if ((iID != -1) && (iID < myLegoLight.Count)) { 
    ((LegoLight)myLegoLight[iID]).Light = bLight; 
} else { 
    strResp = strProblem; 
}

Those lines just validate that the ID of the light is in the range of ID. If yes, the OK status will be send out, if no, that the problem which will be sent out. The LegoLight structure contains information on the led, a name and coordinates on a picture. All that is explained in the previous post as a pre reading Sourire

private static void ProcessLights(HttpListenerContext context) { 
    HttpListenerRequest request = context.Request; 
    HttpListenerResponse response = context.Response; 
    string strResp = ""; 
    LegoLight mLegoLight; 
    ParamPage MyParamPage = new ParamPage(); 
    for (int i = 0; i < myLegoLight.Count; i++) { 
        mLegoLight = (LegoLight)myLegoLight[i]; 
        strResp += mLegoLight.Name + ParamSeparator; 
        strResp += mLegoLight.ID.ToString() + ParamSeparator; 
        strResp += mLegoLight.Light.ToString() + ParamSeparator; 
        strResp += mLegoLight.Network.ToString() + ParamSeparator; 
        strResp += mLegoLight.PosX.ToString() + ParamSeparator; 
        strResp += mLegoLight.PosY.ToString() + strEndFile; 
        strResp = OutPutStream(response, strResp); 
    }  
}

The second function is also very simple, it does just "serialize" in the "old" way all the LegoLights objects and send them thru the output which is the web page. ParamSeparator = "&" and strEndFile = "\r". So similar to what I've already explain in the post on how to read (and write) a setup file.

Of course, when you do code on a very reach and fast platform, you can serialize those object in a nice way with XML and create the schema and all what is needed to look good, nice and reading by a human. Reality is I'm using an embedded platform where resources are expensive, almost no memory is available and it runs very slowly. Just think it's a PC from the early 1980… And you'll be close to the reality of what the Netduino can do.

The output looks like: mairie&0&False&1&158&59 station&1&True&1&208&300 train&2&False&1&10&10 rue&3&False&1&700&550

It does contain a "serialized" view of the LegoLight array. Now, the question is how to consume this? Answer is: thru an ASP.NET application Sourire

So let's go for the code to consume this. First step is to create an ASP.NET application. I've used a simple default template which contains the code to create and manage users, a default page and an about page. So all what is needed to start. First step is to create the LegoLight object.

public class LegoLight { 
    private string myName = ""; 
    private int myPosX = 0; 
    private int myPosY = 0; 
    private byte myNetwork = 0; 
    private bool myLight = false; 
    private int myID; 
    public int ID { get { return myID; } set { myID = value; } } 
    public string Name { get { return myName; } set { myName = value; } } 
    public int PosX { get { return myPosX; } set { myPosX = value; } } 
    public int PosY { get { return myPosY; } set { myPosY = value; } } 
    public byte Network { get { return myNetwork; } set { myNetwork = value; } } 
    public bool Light { get { return myLight; } set { 
        // do call the Netduino here :-) 
        // and change the status 
        //Create a web client object 
        string strUri = LegoCityWeb.Properties.Settings.Default.NetduinoURL; 
        ParamPage MyParamPage = new ParamPage(); 
        strUri += MyParamPage.ParamStart + LegoCityWeb.Properties.Settings.Default.NetduinoID + MyParamPage.ParamEqual + myID; 
        strUri += MyParamPage.ParamSeparator + LegoCityWeb.Properties.Settings.Default.NetduinoLight + MyParamPage.ParamEqual + value; 
        //URL will look like http://ipaddressnetduino/switch.aspx?id=0;lg=true 
        Uri MyUri = new Uri(strUri); 
        string myResponse = GetStringFromURL(MyUri); 
        if (myResponse == LegoCityWeb.Properties.Settings.Default.NetduinoOK) myLight = value; 
    }
    } 
    public string GetStringFromURL(Uri mUri) { 
        WebClient instanceHTTP = new WebClient(); 
        const int MAX_BUFFER = 1024; 
        string myResponse =""; 
        Stream returnValue; 
        try { 
            //call the specific 
            URI returnValue = instanceHTTP.OpenRead(mUri); 
            // read the stream. This stream can't be seek, so get every byte "manually" 
            byte[] mybuff = new byte[MAX_BUFFER]; 
            int i = 0; int ret = -1; 
            do { 
                ret = returnValue.ReadByte(); 
                //if there is nothing to read return -1. Values goes from 0 to 255 
                if (ret > 0) { 
                    mybuff[i] = (byte)ret; 
                } else { 
                    mybuff[i] = 0; 
                } i++; 
            } while ((ret != -1) && (i < MAX_BUFFER)); 
            //returnValue.Read(mybuff, 0, (int)returnValue.Length); 
            myResponse = System.Text.Encoding.ASCII.GetString(mybuff, 0, i-1); 
            returnValue.Dispose(); 
        } catch(Exception ex) { 
            return myResponse; 
        } 
        //close the stream 
        return myResponse; 
    } 
}

Nothing really complicated here, you'll find very basic properties to get and set the ID, the name, the network, the position. Where it gets a bit more complicated it's for the Light property. Well, remember that when you set the property to false, the idea is to switch off the light and when on true, to switch it to on. The idea is to call the first web method we've just write on the Netduino board. Remember, the URL to call looks like http://ipaddressnetduino/switch.aspx?id=0;lg=true

In the code, I'm using application setting for the URL (NetduinoURL), the name of the paramaters (both for the ID NetduinoID and light status NetduinoLight). And I build the URL with the ID of the LegoLight object and the status of the light (so True or False, will also work with 1 and 0). When it's done, I call a function called GetStringURL. This function has the only purpose to call the URI and return as a string the result. I'll explain the function later on. In our case, back to the beginning of the article, this function will return "OK" if everything is correct of "Problem" if not. So I just test if it's OK or not. And set the new light status if everything works fine.

So now, let have a look at the GetStringURL function. It does take a URI as an argument and return a string.

WebClient instanceHTTP = new WebClient(); 
const int MAX_BUFFER = 2048; 
string myResponse =""; 
Stream returnValue; 

First step is to create the variables we will need. We will call a web page, so we need to create a WebClient object. the MAX_BUFFER constant will be used to create a buffer that will contain what is returned by the Netduino. I have limited it to 2048 as it's the maximum number of characters that will be send. On the Netduino, the original setup file will not exceed 1024 characters. But on the response stream, it will contains the ID and the light status as True and False rather than in text. So it will be a bit longer but there is no chance that the returned page will be larger than 2048. the myResponse will be used to put the response text and return it. And the stream object to get the stream from the WebClient object call.

try { 
    //call the specific 
    URI returnValue = instanceHTTP.OpenRead(mUri); 
    // read the stream. This stream can't be seek, so get every byte "manually" 
    byte[] mybuff = new byte[MAX_BUFFER]; int i = 0; 
    int ret = -1; 
    do { 
        ret = returnValue.ReadByte(); 
        //if there is nothing to read return -1. Values goes from 0 to 255 
        if (ret > 0) { 
            mybuff[i] = (byte)ret; 
        } else { 
            mybuff[i] = 0; 
        } i++; 
    } while ((ret != -1) && (i < MAX_BUFFER)); 

The next part of the code open the URI and place the result in the stream returnValue. It's a synchronous call but it will be quick as the Netduino will only return text. So no need here to do an asynchronous call which will be necessary if you have large amount of data to read.

Now, the specificity with the stream we just get is that it is not seekable. So the only way I found to get the data is to pull every single char after the other. The function ReadByte allow this kind of read and return a byte (so a value between 0 and 255). It return -1 in case of problem. The "do while" loop if here to read the entire buffer. And of course, when you start manipulating stream, you better have to use a try catch section.

So either when the buffer is full or when you've reach the end of the stream, the mybuff byte array will contain all the stream. Next step is to convert if to a string.

myResponse = System.Text.Encoding.ASCII.GetString(mybuff, 0, i-1);

That's what this function is doing for you. In the same class, you find also a function to convert from string to a char array and more. And the conversion will be done only for the right amount of read data.

So here it is for the LegoLigh object. A bit different that the one on the Netduino but only for the Light part. Which on the Netduino call hardware function to actually light up leds.

Now it still does not answer the question on how to consume the "serialized" LegoLight array returned by the Netduino. For this, we will need also to read a stream and convert it to a string and "deserialize" the string to rehydrate the objects. The best place to do that is when the web application starts. It suppose that the Netduino is already started. If not, it's just about adding a reinitialisation fonction which will basically call again the same code. It may be needed after a cold Netduino boot. It can also be checked by the main ASP.NET application on a regular basis like every day, week or hour. We're not there for the moment.

void Application_Start(object sender, EventArgs e) { 
    // Code that runs on application startup 
    LegoLight mLegoLight = new LegoLight(); 
    string strUri = LegoCityWeb.Properties.Settings.Default.NetduinoURLLight; 
    ParamPage MyParamPage = new ParamPage(); 
    //URL will look like http://ipaddressnetduino/lights.aspx 
    Uri MyUri = new Uri(strUri); 
    string mySetupString = mLegoLight.GetStringFromURL(MyUri); 
    int i = mySetupString.IndexOf(MyParamPage.EndFile); 
    string mySubstring = ""; 
    string[] myParam; int j = 0; 
    int inc = 0; 
    try { 
        char[] mSeparator = MyParamPage.ParamSeparator.ToCharArray(); 
        while ((i < mySetupString.Length) && (i != -1)) { 
            //split the substring in 3 
            mySubstring = mySetupString.Substring(j, i - j); 
            myParam = mySubstring.Split(mSeparator); 
            mLegoLight = new LegoLight(); 
            mLegoLight.Name = myParam[0]; 
            int myint = 0; 
            myint = Convert.ToInt32(myParam[1]);
            mLegoLight.ID = myint; 
            mLegoLight.Light = Convert.ToBoolean(myParam[2]); 
            //Convert.ToInt(myParam[1], out myint); 
            myint = Convert.ToInt32(myParam[3]); 
            mLegoLight.Network = (byte)myint; 
            myint = Convert.ToInt32(myParam[4]); 
            //Convert.ToInt(myParam[2], out myint); 
            mLegoLight.PosX = myint; 
            myint = Convert.ToInt32(myParam[5]); 
            //Convert.ToInt(myParam[3], out myint); 
            mLegoLight.PosY = myint; myLegoLight.Add(mLegoLight); 
            //next string 
            j = i + 1; 
            if (j < mySetupString.Length) 
                i = mySetupString.IndexOf(MyParamPage.EndFile, j); 
            else 
                i = -1; inc++; 
        } 
        Application.Add("LegoLight", myLegoLight); 
    } catch { } 
}

The code will seat in the Application Start function. This is the first method called when the ASP.NET application starts. So the perfect moment to add those kind of initialization.

Same as for the LegoLight object for the Light method, it starts with the creation of a URI and the call of the specific page which will return the serialized object. The deserialization is not too complex, it's about splitting the string. First by finding the "\r" character and then the separators "&". It's quite artisanal but it's perfectly working. Then a simple conversion for int and bool allow to populate the LegoLight object and add it to the Array. And finally, this array is stored into the Application storage to be used later.

And that's it for the first part of this article. In the second part, we will see how to call the method to change the light and display this in a nice and sweet ASP.NET page. As always, I'm coding in planes and also write articles in planes with no Internet connection. I don't know why but I enjoy coding in planes with no way to find better code than the one I write. I'm just a marketing director writing code Sourire. And as always, feedback welcome. Thanks for one I already received, it motivates me to continue. And thanks also for being more and more to read this blog.