remote vehicle networking: the case of vex...

26
1| Page Remote Vehicle Networking: The Case of VEX ClawBot Testing and Implementation Matt Grojean and Nathan Hart April 24 th , 2014 CEN 4935 Software Project in Computer Networks Instructor: Dr. Janusz Zalewski Computer Science & Software Engineering Programs Florida Gulf Coast University Ft. Myers, FL 33965

Upload: duonghuong

Post on 17-Aug-2018

216 views

Category:

Documents


0 download

TRANSCRIPT

1 | P a g e   

Remote Vehicle Networking: The Case

of VEX ClawBot Testing and Implementation

Matt Grojean and Nathan Hart

April 24th, 2014

 

 

CEN 4935 Software Project in Computer Networks 

Instructor: Dr. Janusz Zalewski 

Computer Science & Software Engineering Programs 

Florida Gulf Coast University 

Ft. Myers, FL 33965 

 

 

   

2 | P a g e   

1. Introduction

This remote networking project is inspired by the ability to remotely access and

manipulate a robot from anywhere in the world. Today users can access anything with the click

of a mouse and given the accelerated rate of software development, implementing a remote

development tool is a vastly beneficial addition to today’s software development lifecycle.

The software and robotic system referenced in this document is that of the same system

seen in the Software Design Document of the project Remote Vehicle Networking. “As software

is developed for VEX via a remote location, one can upload the new designs to VEX and carry

out the instructions.”[2] On that notion, the full operation of the Remote Vehicle network

designed for this VEX robot is explained and tested for accuracy in this document.

From the objectives covered in section two of the accompanied SRS document[1], the

items tested include the remote accessibility of the robot, receiving commands from the web-

panel, live stream of the behavior of ClawBot, and the ability for remote uploading user

generated code. The overall section architecture is presented in Figure 1.

Figure 1. System Architecture

3 | P a g e   

2. Implementation

To begin building the core functionality of the network to be used for communicating

commands to ClawBot, the connection from the PC to the VEX Cortex must be understood.

Beginning with the programming A-to-A USB cable supplied with the VEX competition kit, the

VEX serial drivers are installed and configured. Having this implemented as the first step

provides and insight into the commands and protocols needed to communicate with the VEX

Cortex. Prior to this, it was quite unknown how the ClawBot was to communicate with the

server. These drivers create a direct COM port connection from the computer to the VEX Cortex

which allows both access to the re-writable memory and to the source of main input which

monitors for incoming commands to the VEX code running on the Cortex. To get a command to

the VEX Cortex, the send code used takes in a string with the command requested and sends it

via the write()function of the serial port class from the PC's web-server code. A sample of the

command function is shown below in figure 2.1.

using (SerialPort sp = new SerialPort(Session["vexPort"].ToString(), 115200, Parity.None))

{ sp.Open(); sp.Write(command); sp.Close(); }

Figure 2.1. Sample write command and Serial Port object initialization.

The implementation of the ClawBot Manager occurs in several different pieces. The

aspect first implemented is sending commands to the ClawBot. This was done using C# code

executed on a button click. The web page can currently support commands for the ClawBot to

move forward, reverse, left, right, raise and lower its arm, open and close the claw. When a user

clicks the forward directional button the C# code is executed which opens a COM port and

writes a specific command to the port using the string command (Figure 2.2).

4 | P a g e   

Once sending commands from the website was accomplished via the USB cable directly

attached to the VEX Cortex, the next step is making the system wireless. During this build, only

two options were considered for creating the necessary wireless connection needed to support the

communication and at the range desired. That protocol is 802.11g. Initially the prototype design

was to incorporate a serial to Wi-Fi adapter which sent commands directly into the UART ports

fitted on the VEX cortex. Lacking sufficient documentation on this protocol pushed the design to

focus on the use of VEX based connections. Given the system works directly via the supplied

programming cable, consideration for the VEX programming kit add-on for the remote controller

grew to the most acceptable design. The major benefit to using this programming kit is the

gaining of the connected state desired with the concrete protocol for sending commands to the

Cortex.

Figure 2.2. Flow-chart for manual controls via ClawBot Manager

The add-on kit which connects the controller to the server simulates the hard-wired COM port

initially used for designing the commands to be sent. This was a major step and convenience

which allowed the focus to be shifted to the operation and streamlined behavior of the overall

5 | P a g e   

design. Having the communication channel now open from the server to the VEX Cortex,

website design now becomes the next objective to complete.

The second portion of the Web site to be implemented is to allow a user to upload their

code to the ClawBot (Figure 2.3). A discovered limitation with this option is if a user is to upload

code which does not support the directional commands sent from the website then the ClawBot

can no longer be controlled remotely unless he/she has the original code. To avoid this issue a

Restore Default button was incorporated alongside the button to upload user code. This allows

for another user to restore the default behavior back to the ClawBot when necessary.

Figure 2.3. Flow-chart of upload code command.

The final portion of the web site is the functions to allow uploading code to the ClawBot.

To ensure the ClawBot isn’t going to receive “bad” code, the ClawBot Manager first verifies the

user uploaded an actual binary file with a “.bin” extension. The website then saves this binary

file on the server and executes a batch script to send the code to the ClawBot. The code to enter

the ClawBot into programming mode and send the program to the Cortex is borrowed from an

open source project called Purdue Robotics Operating System (PROS)[3].

6 | P a g e   

There are a number of VEX proprietary commands which ClawBot uses to enter

programming mode and receive commands and code. The documentation which may have aided

in the development of a more catered solution was not able to be located which is why the

PROS-developed upload code is necessary. The PROS upload code is written in Java and needed

a few minor changes to fit the projects needs and compile into an executable .jar. This .jar

is then executed from the batch scripts. There are two batch scripts created, one passes the JAR

file containing the user’s binary file and the other passes the default binary file housed on the

server for the Restore Default feature.

To understand a bit more of how the batch scripts are implemented with the Java code see

figure 2.4 below. The ExecuteBatFile()method is used to call the two bat files needed for

uploading different programs via the local PC. The method begins by reading the file name to be

executed. A new process is started in the window environment and passed the parameters of the

program needed, the directory, and the script name by the line processInfo = new

ProcessStartInfo("cmd.exe", "/c " + batName; The remaining lines of code are

needed to define the behavior of error reporting and how to display the command window if at

all. Finishing the operation of the code is a while loop reading the outputs of the .bat file. This

avoids losing the errors in method-to-method communication. The final if-else check parses the

output stream and looks for keywords of “EXCEPTION” and “ERROR” and instructs the

method to report to the website an error occurred.

7 | P a g e   

private void ExecuteBatFile(string batName) { // upload to vex //execute bat file which will upload the prog to the vex.. //thanks to PUrdue OS for the uploading java code which made this possible... ProcessStartInfo processInfo; Process process; processInfo = new ProcessStartInfo("cmd.exe", "/c " + batName); processInfo.CreateNoWindow = true; processInfo.UseShellExecute = false; // *** Redirect the output *** processInfo.RedirectStandardError = true; processInfo.RedirectStandardOutput = true; process = Process.Start(processInfo); //while (!process.StandardOutput.EndOfStream) //{ // lblInfo.Text = lblInfo.Text + process.StandardOutput.ReadLine() + System.Environment.NewLine; //} process.WaitForExit(); // *** Read the streams *** string output = process.StandardOutput.ReadToEnd().ToUpper(); string error = process.StandardError.ReadToEnd(); //exitCode = process.ExitCode; if (!string.IsNullOrEmpty(error) || output.Contains("EXCEPTION") || output.Contains("ERROR")) { lblInfo.ForeColor = System.Drawing.Color.Red; lblInfo.Text = "Problem uploading code!"; } else { lblInfo.ForeColor = System.Drawing.Color.DarkOrange; lblInfo.Text = "Successfully uploaded!"; } process.Close(); }

Figure 2.4 Java code snippet to execute upload batch scripts.

8 | P a g e   

3. Testing

3.1 Test Plan

The following test cases are catered to the feature requirements found in the SRS

document[1].

Test #1 - Req 3.1.1 - The VEX shall be controllable by live manual commands.

Purpose: This test case is to verify the ClawBot Manager is able to send commands to

ClawBot successfully.

Description: From the ClawBot manager, after verifying the live stream is available.

Click the left and then the right navigational keys. The robot is expected to rotate left 45

degrees then rotate the same distance to the right. The ClawBot is expected be in the

position in which the test was started.

Figure 3.1.1. Confirmation of design for Req 3.1.1.

Test #2 - Req 3.1.2 - The ClawBot manager’s hosting server will be available via a static IP.

Purpose: This test case simply confirms the configuration of the website on the ROCK

server and that it is accessible.

Description: As per the design of the website, navigating to vex.cs.fgcu.edu is

expected to resolve to the ClawBot Manager.

9 | P a g e   

Test #3 - Req 3.1.3 - The web panel will be secured via restricted user logins and support only

one user at a time.

Purpose: This test case will confirm the accessibility for the website is restricted to those

with privileged access.

Description: To confirm the accepted security of the web-panel, enter the accepted

credentials of username: fgcu and password: vex. The web-panel will grant access as

expected. To also confirm the denial of invalid credentials, enter anything other than

username: fgcu and password: vex to ensure a denial of access to the ClawBot manager.

Figure 3.1.3. Confirmation of design for Req 3.1.3.

Test #4 - Req 3.1.4 - The web panel will have access to the local file system of the user PC

making the connection to the server.

Purpose: This provides the ability to select a local file for upload to VEX and confirms

files can be selected from the local disk and published to the website.

Description: Clicking Choose File will prompt the user to navigate to the needed

directory and select the file for uploading[2] Once the file is selected, clicking Open will

then check the selected file to ensure it is a binary file. A status message will be reflected

according to the file. Upon successful verification, click Upload to send the code to the

ClawBot. Once uploaded the program will begin running as the uploaded code.

10 | P a g e   

Following the same instructions selecting a non-binary file, the ClawBot Manager

displays a message reporting the file found is not a binary file. Uploading is not allowed.

Figure 3.1.4. Confirmation of design for Req 3.1.4.

Test #5 - Req 3.1.5 - A live stream of VEX’s view will be seen through the mounted IP camera

and embedded into the web panel.

Purpose: This confirms the ability to view the robot’s behavior on the screen with the mounted 

camera with a maximum 750ms delay. 

Description: Following successful login to the web‐panel, one is met with the live stream of the 

robot view on the left side of the screen.  

3.2 Test Report

Test #1 The initial test failed. A modification to the motor duration to a shorter constant

resolved the over-turning observed. The test now passed.

Test #2 This test easily passes as it either works or does not. Initially the site is accessible

via 69.88.163.32 and clicking the VEX link. Following this report, VEX.FGCU.EDU is the

preferred method to resolve the location of the website.

Test #3 This test passes.

11 | P a g e   

Test #4 This test passes.

Test #5 Initially, the test failed. The stream was resolving to the wrong IP address. Once

the site is loaded it was trying to resolve the IP address of the local network and not the network

of which the server is on. Changing this to the same IP as the server and proper port number, test

passed.

12 | P a g e   

4. Conclusion

Remote networking of this VEX robot was a full success. Full functionality of the web-

panel and desired convenience and accessibility expected is delivered in a neat package for

privileged users. A great deal of serial to IP communication was learned from the issue of

achieving our desired network connectivity of the VEX ClawBot to the server. Once overcoming

the hurdles of VEXnet and the key system, the project was overall most enjoyable of those in the

senior year.

Given the amount of work that was required to implement the core objectives within this

project, adding new features is certainly possible in the future. The next additions should include

the transmission from newly attached sensors. More importantly to track precise location, the

servos should be exchanged with those that can monitor their positions. This info being reported

to not only the VEX Cortex but also the website will make for much more precise development.

A final but more minor addition would be that of a high-definition, wide-angle webcam.

This would allow a much better view of the robot and its surroundings than from the current

camera.

13 | P a g e   

5. References

1. Grojean, M., and Hart, N. Automated Robotic Retrieval System – Software

Requirement Specification. Florida Gulf Coast University, 2014

2. Grojean, Matthew, and Nathan Hart. VEX Robotics - Software Design Description.

Florida Gulf Coast University, 2014

3. S. Carlson, Perdue Robotics Operating System, Perdue University - Lafayette, IN

<http://purdueros.sourceforge.net/doc/>

14 | P a g e   

6. Appendix

i. ClawBot Manager Source Code

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.IO; using System.IO.Ports; using System.Threading; using System.Diagnostics; namespace ClawBotManager { public partial class Default1 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { string[] ports = SerialPort.GetPortNames(); if (ports.Length == 0) { lblConnectionErr.Visible = true; setControls(false); } else { lblConnectionErr.Visible = false; setControls(true); Session["vexPort"] = ports[0]; } } } protected void goForward(object sender, EventArgs e) { writeCommand("w"); } protected void goReverse(object sender, EventArgs e) { writeCommand("s"); }

15 | P a g e   

protected void goLeft(object sender, EventArgs e) { writeCommand("a"); } protected void goRight(object sender, EventArgs e) { writeCommand("d"); } protected void armUp(object sender, EventArgs e) { writeCommand("u"); } protected void armDown(object sender, EventArgs e) { writeCommand("l"); } protected void clawOpen(object sender, EventArgs e) { writeCommand("o"); } protected void clawClose(object sender, EventArgs e) { writeCommand("t"); } private void writeCommand(string command) { try { using (SerialPort sp = new SerialPort(Session["vexPort"].ToString(), 115200, Parity.None)) { sp.Open(); sp.Write(command); sp.Close(); lblOutOfRange.Visible = false; } } catch (Exception) { lblOutOfRange.Visible = true; } } protected void RestoreDefault(object sender, EventArgs e) { lblInfo.Text = "";

16 | P a g e   

string defaultBat = "\"" + @"C:\Users\Nathan\Documents\Visual Studio 2013\Projects\ClawBotManager\ClawBotManager\vexFiles\defaultUpload.bat" + "\""; ExecuteBatFile(defaultBat); } protected void UploadFile(object sender, EventArgs e) { if (fileuploader.HasFile) { // Call a helper method routine to save the file. string file = SaveFile(fileuploader.PostedFile); if (!file.Equals("")) { if (File.Exists(file)) { lblInfo.Text = ""; string userBat = "\"" + @"C:\Users\Nathan\Documents\Visual Studio 2013\Projects\ClawBotManager\ClawBotManager\vexFiles\userUpload.bat" + "\""; ExecuteBatFile(userBat); } else { lblInfo.ForeColor = System.Drawing.Color.Red; lblInfo.Text = "Error uploading binary file!"; } } else { lblInfo.ForeColor = System.Drawing.Color.Red; lblInfo.Text = "Please upload only binary files!"; } } else { lblInfo.ForeColor = System.Drawing.Color.Red; lblInfo.Text = "Choose a file!"; } } string SaveFile(HttpPostedFile file)

17 | P a g e   

{ // Specify the path to save the uploaded file to. string savePath = @"C:\Users\Nathan\Documents\Visual Studio 2013\Projects\ClawBotManager\ClawBotManager\vexFiles\"; string newFileName = "userProgram.bin"; if (!Directory.Exists(savePath)) { Directory.CreateDirectory(savePath); } // Get the name of the file to upload. string fileName = fileuploader.FileName; //if filename is binary file if (fileName.Contains(".")) { if (fileName.ToUpper().Split('.')[1].Equals("BIN")) { // Create the path and file name to check for duplicates. string pathToCheck = savePath + newFileName; // Check to see if a file already exists with the // same name as the file to upload. if (System.IO.File.Exists(pathToCheck)) { System.IO.File.Delete(pathToCheck); } // Append the name of the file to upload to the path. savePath += newFileName; // Call the SaveAs method to save the uploaded // file to the specified directory. fileuploader.SaveAs(savePath); return savePath; } else {

18 | P a g e   

return ""; } } else { return ""; } } private void ExecuteBatFile(string batName) { // upload to vex //execute bat file which will upload the prog to the vex.. //thanks to PUrdue OS for the uploading java code which made this possible... ProcessStartInfo processInfo; Process process; processInfo = new ProcessStartInfo("cmd.exe", "/c " + batName); processInfo.CreateNoWindow = true; processInfo.UseShellExecute = false; // *** Redirect the output *** processInfo.RedirectStandardError = true; processInfo.RedirectStandardOutput = true; process = Process.Start(processInfo); //while (!process.StandardOutput.EndOfStream) //{ // lblInfo.Text = lblInfo.Text + process.StandardOutput.ReadLine() + System.Environment.NewLine; //} process.WaitForExit(); // *** Read the streams *** string output = process.StandardOutput.ReadToEnd().ToUpper(); string error = process.StandardError.ReadToEnd(); //exitCode = process.ExitCode; if (!string.IsNullOrEmpty(error) || output.Contains("EXCEPTION") || output.Contains("ERROR")) { lblInfo.ForeColor = System.Drawing.Color.Red; lblInfo.Text = "Problem uploading code!"; } else {

19 | P a g e   

lblInfo.ForeColor = System.Drawing.Color.DarkOrange; lblInfo.Text = "Successfully uploaded!"; } process.Close(); } private void setControls(Boolean enabled) { btnarmDown.Visible = enabled; btnarmUp.Visible = enabled; btnLeft.Visible = enabled; btnRight.Visible = enabled; btnCloseClaw.Visible = enabled; btnOpenClaw.Visible = enabled; btnForward.Visible = enabled; btnReverse.Visible = enabled; btnRestore.Visible = enabled; btnUpload.Visible = enabled; fileuploader.Visible = enabled; } } } ii. Manual Control Operation in C #include "main.h" void operatorControl() { while (1) { int command = getchar(); //putchar(command); //echo command back used for testing.. if (command != -1) { switch (command) { case 119: //if command is "w" goforward motorSet(10, 75); motorSet(1, 75); motorSet(6, 0); motorSet(7, 0); delay(400);

20 | P a g e   

break; case 97: //if command is "a" go left motorSet(10, 50); motorSet(1, -50); motorSet(6, 0); motorSet(7, 0); delay(275); break; case 100: //if command is "d" go right motorSet(10, -50); motorSet(1, 50); motorSet(6, 0); motorSet(7, 0); delay(275); break; case 115: //if command is "s" go reverse motorSet(10, -75); motorSet(1, -75); motorSet(6, 0); motorSet(7, 0); delay(400); break; case 111: //if command is "o" open claw motorSet(10, 0); motorSet(1, 0); motorSet(6, -50); motorSet(7, 0); delay(200); break; case 116: //if command is "t" close claw motorSet(10, 0); motorSet(1, 0); motorSet(6, 50); motorSet(7, 0); delay(200); break; case 117:

21 | P a g e   

//if command is "u" lift arm motorSet(10, 0); motorSet(1, 0); motorSet(6, 0); motorSet(7, -50); delay(300); break; case 108: //if command is "l" lower arm motorSet(10, 0); motorSet(1, 0); motorSet(6, 0); motorSet(7, 20); delay(300); break; default: motorSet(10, 0); motorSet(1, 0); motorSet(6, 0); motorSet(7, 0); break; } motorSet(10, 0); motorSet(1, 0); motorSet(6, 0); motorSet(7, 0); } /* * JoyStick commands * */ /* if (joystickGetDigital(1, 5, JOY_UP)) { motorSet(6, 100); } else if (joystickGetDigital(1, 5, JOY_DOWN)) { motorSet(6, -100); } else { motorSet(6, 0); } if (joystickGetDigital(1, 6, JOY_UP)) {

22 | P a g e   

motorSet(7, 100); } else if (joystickGetDigital(1, 6, JOY_DOWN)) { motorSet(7, -100); } else { motorSet(7, 0); } int joystick1 = joystickGetAnalog(1, 2); int joystick2 = joystickGetAnalog(1, 3); motorSet(10, joystick1); motorSet(1, joystick2); delay(20); */ } } iii. ClawBot Manager HTML

<%@ Page Title="" Language="C#" MasterPageFile="~/Default.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ClawBotManager.Default1" %> <asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server"> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server"> <asp:ScriptManager runat="server"></asp:ScriptManager> <div class="row"> <asp:Label ID="lblConnectionErr" runat="server" Text="Cannot communicate with VEX controller unit!!" ForeColor="Red" Font-Size="X-Large" Visible="false"></asp:Label> <div class="col-md-8"> <div class="panel panel-info"> <div class="panel-heading"> <h4>Control ClawBot </h4> </div> <div class="panel-body text-center"> <asp:UpdatePanel ID="camPanel" runat="server" UpdateMode="Conditional">

23 | P a g e   

<ContentTemplate> <img id="streamImg" src="http://192.168.10.116/img/video.mjpeg" /> <asp:Label ID="lblOutOfRange" runat="server" Text="The clawbot is out of range" ForeColor="Red" Visible="false"></asp:Label> </ContentTemplate> </asp:UpdatePanel> <br /> <br /> <asp:UpdatePanel ID="ctrlPanel" runat="server" UpdateMode="Conditional"> <ContentTemplate> <div class="row"> <div class="col-xs-4"> <table style="width: 25%;"> <tr> <td></td> <td> <button runat="server" type="button" id="btnForward" onserverclick="goForward" class="btn btn-default btn-lg"> <i class="fa fa-arrow-up"></i> </button> </td> <td></td> </tr> <tr> <td> <button runat="server" type="button" id="btnLeft" onserverclick="goLeft" class="btn btn-default btn-lg"> <i class="fa fa-arrow-left"></i> </button> </td> <td></td> <td> <button runat="server" type="button" id="btnRight" onserverclick="goRight" class="btn btn-default btn-lg"> <i class="fa fa-arrow-right"></i> </button> </td> </tr>

24 | P a g e   

<tr> <td></td> <td> <button runat="server" type="button" id="btnReverse" onserverclick="goReverse" class="btn btn-default btn-lg"> <i class="fa fa-arrow-down"></i> </button> </td> <td></td> </tr> </table> </div> <div class="col-xs-4"> <table style="width: 25%;"> <tr> <td>Arm Up <button runat="server" type="button" id="btnarmUp" onserverclick="armUp" class="btn btn-default btn-lg"> <i class="fa fa-long-arrow-up"></i> </button> </td> <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td> <td>Close Claw <button runat="server" type="button" id="btnCloseClaw" onserverclick="clawClose" class="btn btn-default btn-lg"> <i class="fa fa-chevron-right"></i><i class="fa fa-chevron-left"></i> </button> </td> </tr> <tr> <td> <br /> </td> </tr> <tr> <td> <button runat="server" type="button" id="btnarmDown" onserverclick="armDown" class="btn btn-default btn-lg"> <i class="fa fa-long-arrow-down"></i>

25 | P a g e   

</button> Arm Down </td> <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td> <td> <button runat="server" type="button" id="btnOpenClaw" onserverclick="clawOpen" class="btn btn-default btn-lg"> <i class="fa fa-chevron-left"></i><i class="fa fa-chevron-right"></i> </button> Open Claw </td> </tr> </table> </div> </div> </ContentTemplate> </asp:UpdatePanel> </div> </div> </div> <div class="col-md-4"> <div class="panel panel-info"> <div class="panel-heading"> <h4>Program ClawBot </h4> </div> <div class="panel-body text-center"> <h5>Made possible by <a href="http://purdueros.sourceforge.net/doc/">Purdue Robotics Operating System</a></h5> <h5>Use the PROS IDE to code and compile. Then upload your code here!</h5> <br /> <asp:UpdatePanel ID="prgrmPanel" runat="server" UpdateMode="Conditional"> <ContentTemplate> <asp:FileUpload ID="fileuploader" runat="server" /> <br /> <div class="row"> <div class="col-xs-5"> <button runat="server" id="btnUpload" type="button" onserverclick="UploadFile" class="btn btn-default btn-md"> Upload/Program

26 | P a g e   

</button> </div> <div class="col-xs-5"> <button runat="server" id="btnRestore" type="button" onserverclick="RestoreDefault" class="btn btn-default btn-md"> Restore Default </button> </div> </div> <hr /> <asp:Label ID="lblInfo" runat="server"></asp:Label> </ContentTemplate> <Triggers> <asp:PostBackTrigger ControlID="btnUpload" /> </Triggers> </asp:UpdatePanel> <asp:UpdateProgress ID="UpdateProgress" runat="server" AssociatedUpdatePanelID="prgrmPanel"> <ProgressTemplate> Programming the VEX </ProgressTemplate> </asp:UpdateProgress> </div> </div> </div> </div> </asp:Content>