Background
A couple of months ago, my company had a competition on HTML 5. It reminded me pf an OPT subject when I was still a student. I brought this idea into the competition to maximized applying HTML 5 features in. There are two most important features I put in the competition:
- Canvas
- Websocket
There are three functions implemented in the application:
- P2P communication on webpage
- Animation on canvas
- Subscription on webpage
Introduction
- I wrote a .NET windows forms application as websocket server. In the windows application, I implemented an OPT logic, and opened all key events to external programming through socket. Actually, any socket connections are able to get the data from the server.
- I will not explain too much detail on OPT. Basically, the question is to produce P.
To produce P, two components need to be produced first: P1 and P2. We can make money from selling P as well as P1, P2. The price of P > the price of P1+P2.
There are 3 machines: one Red machine, one Green machine, one Yellow machine to produce P1, P2 and P.
To produce P1, the red machine needs to warm up for A minutes (I forgot the number) and to produce one P1 need ** dollars.
To produce P2, the green machine needs to warm up for B minutes, then work on P2 for C minutes. This is the first step. The second step is to warm up red machine for D minutes to work on P2 for E minutes. At this time, the red machine will stop producing P1 (we have only one red machine) and then green machine to continue working on P2 for the third step to work out P2.
To produce P, the yellow machine needs to warm up for F minutes. With one P1 and one P2, the yellow machine will produce one P for G minutes.
All the manufacturers need money.
Pay salary every week.
The question is: how to manage the production to get a maximum revenue.
I put an arithmetic calculation I think is the best in the window application. After you push the GO button, the application will run the arithmetic to get the maximum revenue.
I reproduced all effects on the windows form into a web page.
Using the Code
WebSocket
I referred to http://www.codeproject.com/KB/webservices/c_sharp_web_socket_server.aspx?bcsi_scan_84C04EB1A8996739=IUdio+8K+Vb+tVCNrQ5lbwYAAABJQgES&bcsi_scan_filename=c_sharp_web_socket_server.aspx to write websocket server The most important part for websocket connection is the handshake information. When chrome tries to connect one websocket server, it will send out a handshake information:
GET /chat HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: www.zendstudio.net:9108
Origin: http://www.zendstudio.net
Cookie: somenterCookie
When the server gets the information, reply:
HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin: localhost
WebSocket-Location: ws://localhost/chat
To set up the connection:
- All the key data produced during P, P1 and P3 production will be subscribed through websocket. There are 3 structures defined to contain those information:
public class MileStoneMessage :JSonObject
{
#region Properties N:Total number; R: Remain number; S:Sales Number;
SR: Sales Revenue; M:Material
public int P_N { get; set; }
public int P_R { get; set; }
public int P_S { get; set; }
public decimal P_SR { get; set; }
public int P2_N { get; set; }
public int P2_R { get; set; }
public int P2_S { get; set; }
public int P2_U { get; set; }
public decimal P2_SR { get; set; }
public int P1_N { get; set; }
public int P1_R { get; set; }
public int P1_S { get; set; }
public int P1_U { get; set; }
public decimal P1_SR { get; set; }
public decimal Salary { get; set; }
public decimal MCost { get; set; }
public decimal M_R { get; set; }
public string T_P { get; set; }
#endregion
}
public class ClientInstruction : JSonObject
{
public InstructionType Type { get; set; }
public int Number { get; set; }
public string Sender { get; set; }
public string To { get; set; }
public string Notice{ get; set; }
}
- Websocket server also accepts the below instructions to control the windows form which means, we can control windows form from web page.
[DataContract]
public enum InstructionType
{
[EnumMember]
Auto=0,
[EnumMember]
Manual=1,
[EnumMember]
Start=2,
[EnumMember]
Pause=3,
[EnumMember]
Stop=4,
[EnumMember]
Timer=5,
[EnumMember]
InputP1=6,
[EnumMember]
InputP2=7,
[EnumMember]
Private = 8,
[EnumMember]
Register = 9
}
- Server sends out message:
public void SendToAll(string message)
{
foreach (var c in Connections)
{
try
{
if (!c.Send(message)) DisposedConnections.Add(c);
}
catch
{
DisposedConnections.Add(c);
}
}
foreach (var c in DisposedConnections)
{
Connections.Remove(c);
}
DisposedConnections.Clear();
}
- JavaScript connect websocket server:
ws = new WebSocket('ws://' + document.getElementById("server").value +
':8181/TWCompetition', 'ClientInstruction');
ws.onopen = function () {
status = 1;
for (var i = 0; i < onOpenEventListeners.length; i++)
onOpenEventListeners[i]();
}
ws. önclose = function () {
status = 0;
for (var i = 0; i < onCloseEventListeners.length; i++)
onCloseEventListeners[i]();
}
- JavaScript receives messages from websocket:
ws.onmessage = function (evt) {
var message = eval('(' + evt.data + ')');
switch (message.Key) {
case "InitData":
if (message.Trace.length > 0) RenderMoneyInitTrace(
"MoneyDashboard", message.Trace, "MoneyIcon");
break;
case "Users":
OnlineUsers = message;
DisplayUsers();
break;
case "Private":
var sender = message.Sender;
var conversationMessage = message.Message;
var name;
if(conversationMessage=="black you"){
if(blackyouObj!=null) {
blackyouObj.OpenBlack();
ClosePrivateWindow("PW"+sender);
return;
}
}
for (i = 0; i < OnlineUsers.Keys.length; i++) {
if (OnlineUsers.Keys[i] == sender) {
name = OnlineUsers.Names[i];
}
}
PrivateConversation(sender, name, conversationMessage)
break;
case "Register":
Token = message.Token;
SendName(document.getElementById('name').value);
break;
case "OneSecond":
for (var i = 0; i < onSecondEventListeners.length; i++)
onSecondEventListeners[i](message);
break;
default:
for (var j = 0; j < onMilestoneEventListeners.length; j++)
onMilestoneEventListeners[j](message);
break;
}
}
}
- JavaScript sends back instructions:
var SendInstruction = function (type, number) {
var json = JSON.stringify({ Type: type, Number: number });
ws.Send(json);
}
Canvas
To some degree, the syntax in JavaScript to draw shapes on canvas is kind of exactly like GDI+ syntax. JavaScript draws line:
ctx.beginPath();
ctx.moveTo(14, 10);
ctx.lineTo(23, 15);
ctx.lineTo(14, 20);
ctx.closePath();
JavaScript draws gradient:
grd = ctx.createLinearGradient(0, canvas.height-mywidth, 0, canvas.height);
grd.addColorStop(0, "gray");
grd.addColorStop(0.5, "white");
grd.addColorStop(1, "gray");
How to Demonstrate
- Under WindowsOpt folder, double click opt.exe.
- Have a look at the right upper corner of the Windows Form. Select a workable IP to build the client connection.
- Click GO button on Game Control panel, the windows form begins to run the animation.
- After you set up the websocket, you will see the exact same animation like windows form on your webpage and you can also control the game.
- You can also see a thoughtworker is running on the screen
- P2P communication, a little bit like Google talk
- Login the WebOPT.html in a new tab with a different name. Then you will see online user list. Your name is in pink, others are in green.
- Click on someone in the list, a dialog will pop up on the left bottom corner.
- Write anything in and press enter. Then you are in a private conversation.
I designed a funny thing in, delete all text in the dialog and input “black you” in low case. The person you are talking to will have a black screen with a funny face on for a few seconds.
Every colorful thing you see in the webpage is written by JavaScript. There is no picture linked to the page. That is what I said - Canvas in HTML5 can replace GDI+.