Here's an explanation of each part of the app.
It is composed of 6 parts.
1) The TestConsole project: is a command-line interface to host the WebHost. (I use this for fast debugging).
2) The WebContent project: has the webpage stuff in it and bridges the gap between the webhost layer and the RelayConfigurator. (aka code-behind page).
3) The WebHost project: that hosts the WebContent stuff. (This emulates IIS, or Apache for the Java folks)
4) The RelayLib project: that talks with the KMTronic hardware. (Untested)
5) The RelayConfig project: this stores the Relay Configuration stuff, and right now it bridges the gap between the WebContent and the RelayLib. (aka business-logic layer)
6) The BassThatHzRelay project: that hosts the Window Service stuff (when we are running it for real, and aren't debugging stuff via the command line hoster), this hosts the WebHost.
The two important pieces are as follows....
#4 The RelayLib:
Pretty straight forward, based on the examples that KMTronic has posted on their website. This is where the rubber meets the road.
Since the web host is multithreaded I dropped down into single-threading mode so as not to have issues with the controller.
I see a code bug there with the State flags, they are being ignored, no big deal I'll fix it when I get the hardware.
Code:
Code:
[CODE]public class Relay
{
public byte ID;
public bool State;
public string Name;
}
public class RelayController
{
private static Mutex mut = new Mutex();
public void ToggleRelays(List RelaysToStart, List RelaysToStop)
{
ToggleListOfRelayBoxes(true, RelaysToStart);
ToggleListOfRelayBoxes(false, RelaysToStop);
}
private void ToggleListOfRelayBoxes(bool State, List Relays)
{
if (Relays != null)
{
foreach (var relay in Relays)
{
relay.State = State;
ToggleRelay(relay.ID, relay.State);
}
}
}
private void ToggleRelay(byte ChannelID, bool State)
{
byte StateByte = 0x00;
if (State)
StateByte = 0xFF;
mut.WaitOne();
var coms = System.IO.Ports.SerialPort.GetPortNames();
var serialPort1 = new System.IO.Ports.SerialPort(coms[0]);
serialPort1.Open();
serialPort1.Write(new byte[] { 0xFF, ChannelID, StateByte }, 0, 3);
serialPort1.Close();
mut.ReleaseMutex();
}
}
[/CODE]
#3 The WebHost:
This is my favorite part, I always wanted to write my own HTTP handler.
This code is gonna need more work if I want things to look they weren't built in 1995 (resource locator/handler)
I have it hardcoded to localhost domain name resolution and port 8080 with no security layer yet. (good enough for now....)
I process the HTML requests Asynchronously, assuming EndGetContext is multi-threaded and thread-safe.
I put in some default HTML return code just to help with debugging stuff, just in-case I mess something up (unlikely).
Code:
Code:
[CODE]public class WebHost
{
private HttpListener listener = new HttpListener();
private Func _HttpResponseHandler;
Thread ListenerThread;
bool IsRunning = true;
public void Start(string uri, Func httpResponseHandler)
{
//example: "http://localhost:8080/"
listener.Prefixes.Add(uri);
listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
_HttpResponseHandler = httpResponseHandler;
Stop();
listener.Start();
IsRunning = true;
ListenerThread = new Thread(new ThreadStart(ProcessRequestsAsync));
ListenerThread.Start();
}
private void ProcessRequestsAsync()
{
while (IsRunning && listener.IsListening)
{
IAsyncResult result = listener.BeginGetContext(new AsyncCallback(HttpRequestHandler), listener);
result.AsyncWaitHandle.WaitOne();
}
}
public void Stop()
{
IsRunning = false;
if (ListenerThread != null)
{
try
{
ListenerThread.Abort();
}
catch { }
}
if (listener.IsListening)
{
listener.Stop();
}
}
private void HttpRequestHandler(IAsyncResult result)
{
HttpListenerContext Context = listener.EndGetContext(result);
HttpListenerRequest Request = Context.Request;
HttpListenerResponse Response = Context.Response;
if (Request.RawUrl != "/" && Request.RawUrl != "/Default.html")
{
return;
}
string ResponseString = String.Empty;
Response.StatusCode = 200;
Response.StatusDescription = "OK";
Response.ContentType = "text/html";
if (_HttpResponseHandler != null)
{
ResponseString = _HttpResponseHandler.Invoke(Request);
}
if (String.IsNullOrEmpty(ResponseString))
{
ResponseString = "Default Content";
}
byte[] Buffer = System.Text.Encoding.UTF8.GetBytes(ResponseString);
Response.ContentLength64 = Buffer.Length;
System.IO.Stream OutputStream = Response.OutputStream;
OutputStream.Write(Buffer, 0, Buffer.Length);
OutputStream.Close();
Response.Close();
}
}
[/CODE]
Here is the actual webpage that gets send to the bowser, kept it pretty simple (for now...)
Code:
[/CODE]
Should run lightning-fast, and all of this fits within just 30kb of harddrive space and RAM. (winning batman,
WINNING!
)
Much better than the jigobytes of RAM, diskspace, and zillions of CPU cores needed to run the more traditional bloatware of today (Norton AV and Vista both come to mind
).