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 Windows or Linux. And also how to create a dynamic management page 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
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
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 https://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 https://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 https://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 . 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.
Comments
- Anonymous
December 19, 2011
Hi I am interested, can you tell the details about the job? Thanks http://createaserver.com