ReadyMedic A Health Information Appliance Final Report 5-8-00 Keith Kirkendall Jeremy Pearce Steven Saunders Lanny Schrib Kerri Smith Table of Contents 1. ABSTRACT ...................................................................................................................3 2. FEATURES ...................................................................................................................4 3. BASIC MODULE OVERVIEW ..................................................................................5 4. DETAILED MODULE DESCRIPTION ....................................................................8 5. TEST REPORTS AND RESULTS ...........................................................................34 BETA TESTING .................................................................................................................34 COMPONENT TESTING .....................................................................................................34 TOP-DOWN TESTING ........................................................................................................34 6. CONCLUSION ...........................................................................................................36 7. ACKNOWLEDGEMENTS .......................................................................................37 8. REFERENCES ............................................................................................................38 2 ABSTRACT The ReadyMedic aims to empower the user with greater control of their health care by providing the patient with health information and communication with their personal physician without requiring previous technical knowledge. It provides access to personal medical records, prescription scheduling, automatic health information gathering, emergency medical contact, and connections to modern medical devices to aid in home health care. Additionally, all information is maintained in a web-based database that will provide the doctor or surgeon with a convenient means of monitoring and storing patient information. The ReadyMedic project is composed of two deliverables. First is the handheld health information appliance, based on an NEC MobilePro 800, which uses a Global Positioning System and Ethernet connection to help connect the user to the world. Second is the web-server, which facilitates communication between the ReadyMedic device and the online medical history records and automatic information gathering, as well as giving physicians means of easily storing patient information. This server also provides the doctor with a powerful means to monitor and update patients. This report details the results of the ReadyMedic design team in developing the first prototype of the ReadyMedic health information appliance and its extended accessories. 3 FEATURES The ReadyMedic is designed to provide an easily understood user interface while still providing powerful features. It requires minimal user input and programmatically provides the maximum amount of information. This facilitates use by both less technically inclined people as well as people with busy lifestyles that do not want to spend the time learning yet another technical device. Part of the ReadyMedic’s power lies in its centralization of data, enabling multiple doctors to easily access and monitor the same patient via the provided online interface. Scheduler—A scheduler keeps track of multiple medicine dosage times and gives the ReadyMedic user notification of the need to take medication. Medical History—An online database containing medical records is accessible (for readonly) to the patient via the ReadyMedic. Medical records are updated by the physician(s) treating the patient through the ReadyMedic Web Service to facilitate the localization of data and easy access in emergency situations. Health Information Queries—By taking advantage of the wealth of medical knowledge stored online, the ReadyMedic is able to provide the user with the latest information on areas of medical interest such as disease, medication or health practices from the American Medical Association website. This searching capability is abstracted from the user such that they simply enter the desired information and they are notified when it becomes available. Ask a Doctor—The ReadyMedic allows the user to enter questions via the keyboard and automatically forwards them to the patient’s personal physician. After the physician has responded to the question, the answer is provided to the user via the ReadyMedic device. Emergency Locator—The ReadyMedic is equipped with a Global Positioning System, which allows a ReadyMedic operator to be notified of the user’s location during an emergency that requires immediate professional medical attention. The operator is able to access the user’s medical and contact information. Protection from accidental invocation of the service is provided with a countdown timer allowing cancellation of the function. Medical Device Interface—An interface is provided for connecting supplemental medical devices to the ReadyMedic. This greatly facilitates home care of the patient allowing the user to monitor their blood pressure, blood sugar, pulse, and temperature. Currently, these devices are simulated with LabVIEW software. ReadyMedic Web Service—All information is stored in a database and is accessible to doctors through the online web interface, to patients through the ReadyMedic device, and to emergency operators through the operator software system. 4 BASIC MODULE DESCRIPTION Basic Overview The following section of the report will give a basic overview of how the components of the ReadyMedic project are incorporated to provide all of the functionality. Figure F-1 gives a graphical representation of the components of the project and how they interact. Figure F-1 ReadyMedic Device Windows CE User Interface This is the entry point and control for the ReadyMedic device. This is what the user interacts with to access all the features. The user has the option to: Ask a Doc, Medical Info, Scheduler, Medical History, Emergency, and Medical Device. Ask a Doc allows the user to write questions to their doctors and read the responses to them. Medical Info enables the user to search for the latest and greatest information on medical topics. Scheduler allows the user to schedule multiple medications and it reminds the patient when to take their medicine. Medical History enables the user to view their medical information and history. Emergency gives the user the ability to request immediate medical attention. Medical device allows the user to take different medical measurements. The user interface controls the flow of the program and is the cornerstone of the project. 5 Connect This allows for communication between the User Interface and the ReadyMedic.dyndns.org Linux server. It opens connections to the server to send and receive information. Ask a Doc, Medical Info, and Medical History all use the connection to the database to retrieve and send data. ReadyMedic Linux Server Server This is the key component of the ReadyMedic Linux Server. It routes all the connections to the appropriate modules and information gathering programs. All Medical Info requests are sent to getHTML to search the web, whereas all Ask A Doc requests are sent to QueryClient to connect to the database. The server stores all information for the ReadyMedic to later download at a convenient time. getHTML The server uses this module for Medical Info requests. It takes the query entered by the user and searches the web for news on a certain medical topic via the AMA Website. QueryClient Whenever there is a need to access the database, the library QueryClient is called to make embedded C MySQL calls to the database. The information is then returned to the Server or the Emergency Server. Database The database stores all the information used by the system and is accessed by virtually every piece of the system. The Emergency Server requests operator login information and patient information from it. Ask a Doc stores the questions to the doctors. The Medical History is retrieved from there. Web Pages The web pages are mainly used for the doctor to add new information or update old information. It connects to the database to retrieve information for the doctor or to store new information. Additionally, the ReadyMedic device accesses various “hidden” pages to allow the user to view Medical History. Barbrady Windows NT Server Emergency Server This module handles all emergency requests from the ReadyMedic and the Operator. It also connects to the ReadyMedic Linux Server to connect to the database and retrieve information using QueryClient. It requires administrator privileges, and thus currently runs on the Barbrady computer in the 483 lab. 6 PC Windows 98/NT Operator This can be on several PCs all over the world. It is used to handle requests that come to the Emergency Server requesting immediate medical attention. It also uses the Emergency Server to query the database to view the person’s information. 7 DETAILED MODULE DESCRIPTION The following section of the report will give a more detailed description of the components of the ReadyMedic project and the technical details that were involved in development. Figure F-2 gives a graphical representation of the components that will be discussed in this section of the report and is an expansion of Figure F-1. Figure F-2 ReadyMedicUI The entire ReadyMedic user interface has been developed using Microsoft Visual C with the Windows CE SDK (software developer's kit). Currently, all modules are built using the "Palmsize PC 2.01" compiler. This compiler was found to generate much smaller code than the "Handheld PC 2.00" compiler, and allowed some features which were not implemented in 2.00. All the currently compiled code was built was "Win32 (WCE MIPS) Release" code since the NEC is a MIPS based processor. User Interface design is initially very difficult in Windows CE. There are literally hundreds to functions that can be used to accomplish various tasks. As such, it is often hard to determine exactly which call to make in order to complete the necessary task. It is recommended that the two Windows CE books mentioned in the references section be consulted to learn the basics. Simple questions, such as which parameters a function takes, can be quickly found in the online help with MSVC (Microsoft Visual C), or on www.msdn.microsoft.com. Be forewarned, various 8 examples were taken from Microsoft’s massive amounts of documentation, and many times they would not work! Those new to Windows programming should also be forewarned about two other areas of confusion. Firstly, Windows examples are written using the Hungarian notation for naming variables, which prefixes a name with certain characters giving clues to its type, such as “p” for “pointer”. This quickly leads to long, hard to read names. Secondly, Unicode is used (which is a two byte version of characters designed to accommodate other languages, like Japanese, that ASCII neglects). Unicode, which is the only form of string (i.e. char*) processing in Windows CE, did not produce readable files using the standard Windows CE FileWrite calls. This frustrating problem required finding the proper functions to convert back and forth with ASCII. These functions are provided in the Utility.cpp file. The TEXT macro, which is used throughout the code, converts standard strings into Unicode. It is also useful to know that in many cases, a string manipulation function, such as strcat (which concatenates strings), is replaced by wcscat (which concatenates wide character strings (i.e. Unicode)). MSVC allows for a great deal of automatic MFC (Microsoft Foundation Class) code generation, and the base ReadyMedicUI app was initially created this way. Using this tool, one can draw the basics of the user interface, then programmatically resize it as needed later on. The MainDlg dialog was created this way, and its buttons mapped to function calls using the ClassWizard (simply right-click on a button, and select the ClassWizard option. You then have choices about what messages, such as single-click, to map to functions). MFC handles routing the flow of program control to these user interfaces handling functions automatically. In the ReadyMedicUI’s case, these functions load new windows to continue the user interface flow. For example, clicking the Emergency button will instantiate a EmergencyClient dialog box class, run it until it completes execution with the DoModal method, then delete the object and resume waiting for the user to click another button. As another example, clicking the Medical History button loads an entirely new process (the htmlView program) and waits for that process to complete before again regaining control. Since the htmlView process was written using standard Windows CE message passing (as opposed to MFC), it can take command line attributes, and in this case it takes the name of the initial html file to display. A black dialog box is used in the background to cover up all evidence of Windows CE existing, completely forcing the user to use the device only as intended. This is very important in reaching the average user, which does not want to be bothered with such additional complexity. The background is simply painted black using standard drawing commands to make the screen appear to be blank except for the dialog boxes that are displayed. This is useful because it allows the dialog boxes to be small (so they could be easily ported to smaller devices than a MobilePro). The htmlView program actually uses the maximum possible screen space, which is important because it display more information than any other module. However, it interacts with Windows CE to determine its size, and thus it also would not require changes if ported to a smaller device. In using the ReadyMedic, one will notice that windows are stacked “in the Z axis” to ensure only one is available at a time. Again, this was decided to ensure that the user did not have to worry about multitasking, it occurs for them transparently. To the programmer however, it is important that these windows are all the same size to ensure that accidentally clicking the edge of one doesn’t bring up something out of order. This is done in the DlgSpecs.h file, which calculates the dimensions and placement that every dialog box should have. All dialog boxes that are created with MFC had the OnInitDialog function added (to handle the WM_INITDIALOG message from the class wizard handled) as used the following function to ensure its specs: SetWindowPos(NULL, DLG_IX, DLG_IY, DLG_DX, DLG_DY, SWP_SHOWWINDOW); 9 This stacking of windows is demonstrated in Figure F-3, which shows the flow of windows associated with finding Medical Info slightly spread out to emphasize the stacking. Figure F-3 Closing the ReadyMedic Please note, the project has been set up such that a user could never “exit” the program and thus return to Windows CE. However, for the developer, it is often nice to be able to exit cleanly without resorting to resetting the machine or using CTRL-ALT-DEL to kill a process. If the main screen is visible along with the toolbar at the bottom of the screen (which shows two processes with no names corresponding to the background dialog box and the main dialog box), it is possible to hold the ALT key and click the second process to close the program. Closing in this manner will stop all scheduled activities and clean up any files that were written. Doctor Questions and Finding Medical Info MFC was used to create the flow of dialog boxes required to enter questions to the doctor or medical info. The both use standard Windows CE functions to send and get text to/from the objects on the window. This text is parsed and formatted into a query string usable with the ReadyMedic server. Since sockets connectivity does not work currently, this string is displayed in a MessageBox(), which allows the user to use other methods (such as rmClient as described in 10 the User Manual) to send the information along its way. The expected names of the resulting queries are stored in the registry, and later used by the AutoDownload module (see this section for more detail). Standard functions have been created to let the developer quickly access the windows registry in the HKEY_LOCAL_MACHINE\ReadyMedic\* keys. These keys contain information about the above directories, as well as the patient’s ID and doctor’s name. At times the schedule also stores information here. The MainDlg.cpp file of the ReadyMedicUI project initially sets values for the registry items. They may be changed in this file to modify the starting default values of the patient ID and doctor’s name (both of which are used in create the strings for transferring information) The developer may view/edit the contents of the registry manually using the MSVC Tools\Windows CE Tools menu option and selecting the Remote Registry Editor. Reading the results when they are downloaded is a simple process of generating the list of available replies/documents and placing them in an html file as links. This enables the htmlView module to display them and let the user easily switch between the various responses. The DisplayLinks() function was created (it resides in Utility.cpp) to generate this list in the html file. Here the text written to the file was converted from Unicode to ASCII to prevent having characters in the resulting file being separated by boxes (presumably representing the increased data capacity of the Unicode). This module automatically calls the htmlView module to let the user interact with the information. HtmlView The htmlView program was based on an example from Microsoft, which utilized the Html Viewer Control API, provided with Windows CE. This example was written with the standard Windows message loop style instead of MFC. Note, the complexity of the program was greatly reduced and each of its features within the code are documented. The program accepts a command line argument, which it expects to be a string listing of the file to display, and thus tries to display that file. A file is displayed by first determining if it must be downloaded from the Internet (which means it contains “http://” in its name) and using the DownloadPage module to save the file to a specified location. Various paths are set, and the file is opened and loaded into memory. The Html API actually takes care of interpreting and displaying the Html code. Every file that is displayed is added to the end of a circular buffer, which is an easy implementation for a BACK button. Whenever that button is pressed, the current location in the buffer is decremented by one and the page name stored there is displayed. Since the buffer is circular, the user may not be able to go back to all pages visited, but there will also never be a low memory condition either. The API automatically generates messages whenever a hyperlink is clicked (NM_HOTSPOT), and the link is then displayed as described above. The API does not directly handle graphics, thus they also generate a message and are handled manually. This section of code was commented out to prevent viewing of pictures. Although it works, pictures from the Internet must be downloaded first, and this can become very difficult. During this revision of the product, it was found that the AMA page could be used very nicely without graphics. This does however demonstrate the major deficiency with the htmlView module, it must have the full path of something to download. Since most WebPages only use relative paths, this is a problem that will need to be resolved if future work is done. A filter has been considered that would always replace relative links with the full name. The user interface of the htmlView only displays the actual Viewer Control (which automatically supplies a scroll bar), a close button and the back button. The two buttons were created using the CommandBar control, which is created very similar to a new window and is handled with the WM_CREATE message. The back button was drawn as a bitmap, registered with the program 11 and added at runtime. The close button was added as an “adornment” and is a more common feature of Windows applications. AutoDownload Windows CE provides a very nice function for scheduling a program to run at a certain time: CeRunAppAtTime(). This function will run the specified program at the specified time, even if it must power up the machine from its low power state. This allows the ReadyMedic to remain turned off to the user, thus conserving power, but activate automatically when some event needs to take place. Both the AutoDownload and Scheduler modules make use of this feature. Both are separate subprograms that get scheduled by the ReadyMedicUI program using CeRunAppAtTime(). AutoDownload simply looks for files on the ReadyMedic server and displays a message if it finds any. It then reschedules itself to look again at a later time. It should be noted that this time is currently 1 minute, which facilitates a quick demo. It is easy to change this time to be an hour or a day later to reduce the number of connections to the network. Because of the Windows bugs with the Connectivity modules, a kludge was required to allow any functionality. Currently, any time the user enters a question for the doctor or a request for medical info, the remote and local names of that file which will be generated by the server are stored in the registry. Thus AutoDownload simply reads these names and downloads only that server file. The more correct way of doing this would be to use FTP functionality and download all files that are associated with a given ID number. Scheduler The medicine scheduler is probably the most interesting component of the ReadyMedic user interface. Like AutoDownload, it is a subprogram that may be scheduled to execute at a certain time. Its basic algorithm is as follows: ReadyMedicUI Append UI data as an entry in a text file SetSchedule() to initialize notification Scheduler Subprocess Alert user during notification SetSchedule() to set next notification As such, every medicine that is scheduled is added to a Unicode text file in the form: MED: <medicine name> END: <last date to take medicine> -> <time> END. The information is pulled directly from the scheduler user interface and appended to the end of the file each time. SetSchedule() uses the MED & END tokens to break up the list of medicines into the individual medicines and parses the entries for the next time during that day. If there is no future time for the current day, the time is set to the next day and again a time is attempted to be set. If it is past the medicines end date, its entry is removed from the text file. Each medicine entry is scanned and the nearest time is used as the next medicine to schedule. This medicine’s name is added to the registry, and the scheduler subprocess is scheduled for its time. When the scheduler subprocess is started, it reads the name out of the registry and notifies the user to take a dose of it. It then again calls SetSchedule() to find the next time to schedule itself. 12 Connect The Connect function provides communication between the WinCE user interface and the ReadyMedic.dyndns.org Linux server. It opens connections to the server to receive information. Ask a Doc, Medical Info, and Medical History all use the connection to the database to retrieve data. Originally, attempts were made to use sockets to provide connection between the systems using the sockets with a client running on the ReadyMedic WinCE device. The ideal solution would have been to open an socket connection between the client, WinCE device, and server, Linux box, to allow upload of patient questions to their physicians and topics for information query and allow the download of results from the previous functions as well as the patient’s medical information. When this proved to be unfruitful, other solutions were sought. Windows Networking has WinInet functions defined in the wininet.h header file that are stated to be supported by WinCE. The Windows CE WinInet API ideally enables the use of HTTP (Hypertext Transfer Protocol) and FTP (File Transfer Protocol). Function code is provided in Chapter 7 of the Microsoft Windows CE Communication Guide to allow the function to be called and submit an HTTP request for the default HTML document from the server and display the code and the transaction headers. Ideally, these functions would have provided the appropriate results for the functions with required modifications. However, it was discovered that there are ‘Known Microsoft Bugs’ that do not allow this functions to work properly. Upgrades to WinCE 2.12 and Internet Explorer 4.01 for WinCE were required to properly use these function calls. The group received an upgrade, however, not wanting to risk loosing the functions of the project that were performing properly, the upgrades were not installed on the WinCE ReadyMedic device. In the end, an example HTTP function was modified to allow any file to be programmatically downloaded to the WinCE ReadyMedic device and provided half the functionality of the ‘Ask A Doc’ and ‘Information Info’ functions and full functionality of the ‘Medical History’ function. Code was developed on the Linux server to compensate for the lack of connection between the two systems, yet showing the possibility for full functioning of the ReadyMedic device’s functions provided either the appropriate upgrades are installed on the device or a work around for the lack of FTP protocol is developed. ReadyMedic Server It was decided early on that an FTP server with incoming and outgoing folders would be the best solution for processing information requests. Any time the user makes a request, the ReadyMedic would Ftp the request to the inbox. This box serves as a queue and requests are handled in order. When they are completed, the results are posted to the outgoing folder, which the device will scan periodically (looking for its personal ID code). If it is a longstanding request (such as a request for all new info on a certain medicine) the request is left in the queue, otherwise it is deleted. This concept is demonstrated as follows: Loop (forever) { Open(fromInbox, withBlocking); ProcessRequest(fromInbox); If(fromInbox is a single request) Delete fromInbox; Else Return fromInbox with updated date } 13 However, it was discovered that no calls existing that would allow a program to block until a file was created in a directory, as specified in the previous biweekly report. Based on input from Robert, as well as detailed examples from online resources, a sockets server was created to produce the required functionality. Sockets are a common tool, and were thought to work across the Linux, Windows CE and Windows NT platforms, and they would still enable the server to not waste CPU cycles polling for data. Although the server now works, it turns out that the Windows CE sockets could never successfully connect to it because of Microsoft bugs. The component diagram of the server and its various process that it communicates with is shown in Figure F-4. rmServer - receive incoming info & doctor queries - save & signal rmQueryHandler rmQueryHandler - process and save queries - getHtml() for info queries - queryclient() for doctor queries - store / delete daily info queries rmRenewQuery - resignal daily queries and sleep()… rmCheckDocAnswer - process doctor replies and sleep()… Figure F-4 An example client-server program written in C for UNIX was used as the first baseline. He first got it to compile and execute on both his Linux machine and across the Internet to the ReadyMedic server. This baseline server uses standard sockets calls to configure a port to listen for incoming requests. The 'accept' call blocks until a client connects on the designated port (in this case, 7333 was selected), then negotiates a port for message passing. The server then uses the 'fork' system call to create another process to handle the incoming request. The parent simply closes the new port and waits for another connection. The child handles the incoming request on the new port by displaying a message on the screen. The baseline client simple used standard calls to attempt a socket connection at a specified web address. It sends a simple message, then a message of length 0 to signify the end of the connection. The server program, rmServer.c, underwent many changes from this baseline. When the server forks a child process, the child's memory space is not cleaned up until the server explicitly specifies it. The 'waitpid' system call was used to allow children to be cleaned up if they are finished executing/processing, otherwise it continues. This reduces the number of cycles the parent must waste waiting when it could be looking for another incoming socket connection. 'waitpid' will generally have one defunct child, since a child is usually done by the time a new connection is requested. When a message is passed, the child process now loads it into a buffer, reads the ID number, finds that ID number's previous requests, or creates a new list. The server’s representation of a request is a file, which contains one request per line and is named after the ID. This file is useful because it allows a different program than the server to parse it. Whenever a new request is created (or an existing one has a request appended to the end of it), the server issues a signal. A signal is an operating system interrupt. Another program that is blocking in the background until it receives that signal wakes up and completes processing of the request(s). 14 This program, rmHandleQuery.c, reads each file in the in directory and processes each request within the files. Requests are in the form <id>: <num> <type>: <action> for info queries and <id>: <num> <type>: <doctor id> <action> for doctor questions where <id> is the patient’s id number, <num> is 1 for execute request forever and 0 for execute once, <type> is a I, D, or K for information query, doctor question, or kill (delete) existing request, and <doctor id> is the doctor’s logon id. The <action> is the information to search on or ask the doctor about. The requests are loaded into a linked list of requests, and the kill type is run to remove requests. The rest are then processed in order, and if the num is 1, they are saved for future use to the done directory. Due to lack of time, the actual ReadyMedic device does not provide a method of sending the kill message to the server. However, using the client mentioned below, this action can still be performed. Info queries use the GetHtml module to search the web and return the appropriate html, which is stored in the out directory. Doctor questions are routed to the doctor using the queryclient() module. Another program, rmRenewQuery.c, restores requests at the end of the day from done to in. This is performed by the ‘rename’ call, equivalent to moving from on location to another (mv). RenewQuery then issues the appropriate signal to allow query handling. It uses the sleep system call to be scheduled to run only once a day, thus reducing the number of wasted CPU cycles. The rmCheckDocAnswers.c program is the most simple. It simply loops infinitely (with a certain about of nonblocking sleep() time between each iteration to reduce polling) and uses the queryclient() functionality to store a doctor’s reply to a question in the out directory. The client program, rmClient.c was kept relatively unchanged from the initial example. It was modified to allow the user to enter the string to transfer, surrounded by quotes, via the command line. This module has the server to transfer (ReadyMedic.dyndns.org) hardcoded to facilitate easy use. This may be modified if a new server is used. Although the original code was written in C, much of it was converted to C++. C++ has strong type checking, which always finds many errors in passing arguments. C++ also allowed the creation of a message logging object. It allows calls such as: rmLogger log("rmServer", "logFile"); // Declare this globally log.logMessage("Connection on port %d!", portNum); // Use this anywhere! This allows every module to use the same generic class to log its output to either the specified file or to stdout (i.e. the screen). The log files note the time and place of message and are very useful for finding errors that may occur. Obviously, compiling and starting the server could be difficult given so many modules. This was aided by creating a tcsh shell script to compile and start each module in the appropriate order. A makefile was written to facilitate compiling as well as cleaning (make clean) of the directories. QueryClient Common Common is a file that lists function definitions for all functions used in every query to any MySQL database. It has an associated header file, common.h, which contains the function 15 prototypes. Please note the #include’s, which would obviously need to be changed such that the correct libraries (especially MySQL API) can be found. The functions within the common.c file are: do_connect(), print_error(), and do_disconnect(). The function do_connect() takes parameters: hostname (char *), username (char *), password (char *), dbname (char *), portnum (unsigned int), socketname (char *), and flags (unsigned int). Depending upon the version of MySQL used, different parameters are used to connect. Once the connection is established it will return a connection handler. The function print_error() basically prints the error received and held in the connection handler construct. It implements other functions from the MySQL API to do this upon receiving a connection handler and a user-defined message (char *). The function do_disconnect() simply disconnects the connection when passed the connection handler. QueryClient Queryclient.c is a file that lists function definitions for the following functions: queryclient(), ask_to_buff(), and buff_to_ask(). The associated header file containing the function prototypes is named queryclient.h. Again, note the #include’s at the top of the file, as well as the #define’s. Notice that the dbname as well as other vital information is hardcoded there and would possibly need to be changed based on the MySQL set up. Also, a global variable conn, which is a MySQL connection handler, has been set up. The queryclient function takes a query (char *), an option (int), and an error (int *) and returns an array of strings containing the results of the query. First, the module attempts to connect to the database. Then based upon the option argument it will follow one of the following paths: a question is inserted into the database (whereas the query argument should contain a patient id, a drlogin value, and a question, each of them separated by a space and in that order), any answered questions will be retrieved from the database (where query is an empty string), values will be checked against information in the database (where query will contain operator login and operator password, each separated by a space in the proper order), and patient information retrieved from the database (where query contains a patient id). Following the first path, or option equals 0, the query is sent to the buf_to_ask function, which basically inserts the values into an Askadoc struct (containing fields for drlogin, pid, question, and answer). Note more information will be given on the buf_to_ask function later. The struct is then passed to a function called qinsclient (discussed under the qinsclient section). If the information is successfully installed the first string in the return array, buffer, is set to success; else, the entire buffer is set to NULL and returned. Following the second path, option equals 1, again the query is sent to buf_to_ask function. Then the qselclient is used to retrieve the answers from the database, which returns an array of Askadocs full of answered questions. Note more information on qselclient will be given in the qselclient section. If any information is retrieved it will then be processed one struct at a time by the ask_to_buf function (described later). Each struct in the array corresponds to a string in the returned string array, buffer. Following the third path, option equals 2, the query is passed to the qselcj1 module (explained further in the qselcj1 section). This module returns 0 if no errors occurred (i.e., the information was found in the database), and the first entry in the return array is set to “SUCCESS”. Following the final path, option equals 3, the query is passed to the qselcj2 module (explained further in the qselcj2 section). As opposed to returning an error, it returns the information already in an array of strings to be returned. After that the queryclient module will disconnect from the server and database, and return the string array to the proper client. 16 The buf_to_ask function first declares an Askadoc struct as well as some pointers used in traversing the query passed in. Each of the pointers in the struct is initialized to NULL, and the other pointers are set appropriately (endptr—the end of the query, spcptr—the query). Then after testing for a valid query string, a loop that ends when either spcptr equals endptr or if a stop flag equals 0. Until one of these two things happen, I will search the query for a space, count the number of characters from the beginning of the query, then copy from query (which is reset after each copy) the number of characters counted into the appropriate field in the struct. The correct field is known due to the order being known and an option, known as value, to keep track of what attribute has been completed. To know how many characters are in the last string, a running total of how many characters are left in the query is kept, in tot. The struct is then returned. The buf_to_ask function takes the struct, as well as a char *, and uses sprintf to take the values from the struct and insert them into the char *, of course being separated by spaces. Qinsclient The prototype for this function can be found in the file qinsclient.h. The qinsclient module is passed a connection handler as well as an almost completed Askadoc structure. The first task to be done is to build the two query strings. The first will make sure that the patient is in the database and linked to the doctor who will receive the question. The second will actually insert the question into the Askadoc table in the database. The database is then sent the first query through the API call mysql_query(). Then, if the patient is present and there are no errors in the retrieval process, a validation flag, verified, is set. Upon “verification”, the second query string is built from the information held in the Askadoc structure. To get the date, the function strftime is used. Again using the mysql_query() function, the information is stored into the database and the memory allocated for the query strings is released. Qselclient The qselclient module is passed a connection handler, a completely empty Askadoc structure, and a pointer to an integer. The pointer is used to set and receive error codes, which are described in error.h. First, an array of Askadoc structures is set up as well as allocating memory for the two query strings. The first query string is used to select any questions from the database that have been answered (i.e., the answer field in the database is not null). The second query string is used to delete all the questions that have been answered. The first query string is built, and sent to the database using mysql_query(). Each answer is stored in an Askadoc structure in the array as mentioned earlier. This is accomplished by using a while loop and the mysql_fetch_row() function on the result of the query. This takes a tuple from the database and puts each field in an entry in the “row” array, which can simply be indexed to get the result. The fields will come back from the database in the order requested in query. Then each element of each Askadoc struct in the array is set to NULL so that proper processing will take place in the queryclient module. Then for each answered question, a second query string is built that, when sent to the database, will delete the entry with a matching value for its answer. The memory allocated to the query strings is then released and the array of Askadocs is returned. Qselcj1 This module performs the login validation for operators in the emergency medic feature. The function prototype can be found in the file qselcj1.h. The qselcj1 module is passed a connection handler, and a query (char *) which contains an operator login and an operator password. First using a somewhat similar method as buf_to_ask, the query that is passed in is parsed to get values for the operator login and password. These values are then inserted into a query string. To do 17 this, the first space in query is found and then the number of characters prior to the space is counted and stored in count. Then the first count characters are copied into a variable, oplogin, and the remaining characters into oppswd. Then using sprintf, the query string is built. This query string will be sent to the database via the mysql_query() function. If the values are present in the database a zero is returned, else another value is returned. Qselcj2 This module performs the patient contact information retrieval when an emergency occurs and the patient uses the emergency medic feature. The function prototype can be found in qselcj2.h and the error codes can be found in error.h. The module is passed a connection handler, a query (char *) containing the patient id number, and an int *. Again, the integer pointer is used to set and receive error codes. First, space is allocated to a query string and also to an array of strings. The array of strings, ans, is used to store the results of the query and will be returned back to the calling function. The query string then is built and is used to retrieve all the information corresponding to the given patient id. Again, this is accomplished through the mysql_query() function. Only certain fields were needed by the operator however. As in the qselclient, the mysql_fetch_row() is used and each row is stored into an array (note, there should only be one row corresponding to each patient id). Then a case statement is used to select the information that is really required. Each field is then put into a separate space in the ans array. Also, note that when the state field arrives from the database, it is a number (in string form). The function StringToInt is used to change the string into the corresponding number. This number corresponds to a state, and this state is inserted into ans via a large case statement. The memory is then released and ans is returned. getHTML getHtml is a function written for the sole purpose of getting HTML code from a search engine. The function is written using C Stream sockets. It posts a header to a search engine, and returns a string that contains the HTML code returned by the search engine. As the function is programmed now, it only looks at the AMA web site. This was done for a multitude of reasons. It would not be hard, however, to manipulate the code to search multiple engines, or to allow the user to select a search engine. This will be explored in more detail later. The function is called getHtml. It is defined, in C, as: char * getHtml(char *search); The function succeeded if it returns a string, and failed if it returns a NULL pointer. The function expects to be passed a string to work with. Here's the caveat: the function does no parsing on the search string. If you want to search with multiple words (such as "colon cancer" instead of "leukemia") then you must pre-format the string with a + in the middle... e.g., the string would say "colon+cancer" when passed to the function. It must pass this string (notice no spaces) followed by the obligatory /0 (the string end). The buffer used to return the string is defined as: #define bufSize 65536 char buffer[bufSize]; 18 This is a limitation on the size of the HTML file that can be returned. If you are not getting the full HTML file, then change bufSize to something larger, preferably a base-2 number. If you are consistently getting files that are much smaller than this, and memory is a concern, then you can shrink bufSize. The function first formats the header it is going to pass to the search engine. It then opens a Stream socket. If the socket fails to open, the function fails and returns a NULL pointer. After the socket is open, the function converts the hostname to an IP address (with the port hardcoded to 80) and opens a connection to the server socket. It then sends the command string, which was preformatted, to the server. If this fails, then the function fails and returns a NULL pointer. It then buffers the input from the server. If the receive fails at any time during buffering, then the function fails and returns a NULL pointer. After the buffering is completed, it closes the socket and returns the string. To make this function capable of searching multiple engines (say, X search engines), you would have to repeat this process X times. You would then concatenate all of the strings returned by the search engines into one long string, and return it. To make this function able to search multiple engines, but only one at a time by user preference (e.g., the user wants to search Altavista instead of AMA), you would have to include another parameter in the parameter list. It would be logical to use an int. char * getHtml(char *search, int engine); You would then use a switch statement on engine to control which search engine is used, and the procedure would be identical to what happens in the function now, except for that one change. There would be significant overlapping of code (the same code inside the switch, over and over), but that is the price you pay. The function will run in the same amount of time as the original function; the object and executable code will only be larger. Database The ReadyMedic MySQL database provides the backend to the functionality of the ReadyMedic project, including the web pages, the PDA device, and the operator. ReadyMedic approved physicians are able to easily maintain and access medical information regarding their patients, users of the device are able to view their personal information, and emergency operators can view vital information of those who request emergency services. The database is designed to provide security from inappropriate personnel gaining access to the information of ReadyMedic patients using verification tables. Using the insight gained in the user interface design meetings and information gained from medical personnel, a database was implemented in mySQL including the attributes and relationships shown in the entity-relationship diagram in Figure F-5. 19 Figure F-5 To provide the organization of the project, a single database entitled ‘Medical’ was created in mySQL using the command: CREATE DATABASE <database name>; The Medical database includes eleven tables to hold the information of pertinent to the operation of the ReadyMedic services. Tables were added to the Medical database using the command: CREATE TABLE <tablename> (column specifications); The twelve tables encompass the medical database include the following: ‘patient’ table with common information, ‘Allergies’ table with information regarding allergies, ‘FH’ (family history) table with information about medical problems in the patient’s family, ‘SH’ (social history) with information regarding smoking, alcohol and drug usage, ‘HPI’ (history of the present illness) table which documents all doctor’s visits, ‘PMSI’ (past medical and surgical information) table with information about patient’s procedures, treatments and diagnosis, ‘medication’ table with information about the medicines a patient may be taking and the prescribing physician, a ‘doctor’ 20 table with general information about the physicians, and a ‘drlogin’ (doctor login) table requiring a physician to have appropriate verification to make changes and updates to the database, a ‘operator’ table for verification of operators login and password, a ‘verify’ table which places a link between the patient and the physician, and a ‘askadoc’ table which stores questions and answers until they are sent to the appropriate person. Below are tables describing the attributes of the tables described above. patient pfname pminit plname pssn pstreet pstate pzip phphone pwphone pinsco pinsno pbdate pec1fname pec1lname pec1street pec1city pec1state pec1zip pec1hphone pec1wphone pec2fname pec2lname pec2street pec2city pec2state pec2zip pec2hphone pec2wphone Allergies pid phallmed pallcon phallanes FH pid phill prelation SH pid palcoamt psmokyr psmokamt pdrugname pdrugstdate pdrugenddate HPI pid cistartdate cistartloc cisymptoms ciworse cibetter cibefore cidiagnosis citreat cidate cidrid PMSI pid phyear phdiag phproc phcompl medication pid pmedname pmeddose pmedfreq pmedstdate pmedenddate doctor drfname drminit drlname drstreet drcity drstate drzip drwphone drspec drlogin drlogin drlogin drpassword operator oplogin oppassword verify drlogin pid askadoc pid question drlogin qdate answer WebPages The ReadyMedic Website makes use of HTML, PHP, and a MySQL database on the backend and an Apache web server. HTML (HyperText Markup Language) is the de facto standard for building a web page. It is very simple to learn and provides many basic display necessities. PHP is simply a scripting language that is pre-processed by the web server and then displayed as HTML. The web server will grab the file, and if it is a PHP file (denoted by extension .php3), the file will be passed on to the pre-processing module. Then a pipe is opened between the module and the web server, and the web server pushes out whatever comes out of the pipe. PHP code can be used literally anywhere in HTML, because it is used just like a tag (i.e., <?php mycode here ?>). PHP is used to take information from the HTML forms to the MySQL database on the backend through the many built in functions and the easy use of PHP variables. For instance, if you named a form element (via the name attribute) mytextfield, the text typed into the field could 21 be referenced through $mytextfield in PHP code. PHP is also used to dynamically generate HTML. In general (with exception to the dynamic HTML creation pages), a PHP file will have the following structure in our project: 1. Establish database connection (via mysql_connect(), mysql_select_db()) 2. Data Testing and Validation 3. Do Database task(s) (via mysql_query()) 4. Re-direct User to Appropriate Location and Set Appropriate Cookies (via header("Location: $URL") and setcookie()) The design model for the ReadyMedic Website takes on a "pathways" form and provides what are called “pseudo-sessions”. Once a user has begun a “pseudo-session”, the user follows a “path” to get to the information needed. The window flow is something of the following: a login screen, a screen allowing the doctor to choose which "pathway" to follow (i.e., Add a patient, Update a patient, View Information and possibly Delete a patient). The previous paths are comprised of the following: Add--two pages of information on the patient, the first general information and the second more specific medical history Update--a page to choose what to update (General information, History of Present Illness, or the more specific medical history), then a page to do such changes View—a page to request what information to view, the information page. Each of these will involve a PHP page, since there will be queries, and an HTML page for the user to enter the information into via forms. However, some of the HTML is generated dynamically from PHP. Each path will now be discussed further. Add a Patient The Add a Patient pathway requires two pages of information on the patient, the first general information and the second more specific medical history. Each of these requires an HTML file and then a PHP file to insert the data. The two PHP files follow all steps from the general algorithm above. Specifically in step 3, the query string used as the argument is of the form “INSERT INTO <tablename> VALUES (<phpvar1>…<phpvarN>)”. View Patient Information The View Information pathway requires an HTML page to obtain information from the user, a PHP page to process the choices made and a PHP page to display the results of the query. The user is able to enter both a first and last name, or a patient id to obtain information. The first and last name presented the problem of multiple records. The solution was to use another intermediate PHP page to display key information about each “John Doe” in the database and let the doctor select one based on id, as given. Once the information is retrieved, it is delivered back via the mysql_query() function and is stored in $result. Then the mysql_fetch_array() function is used so that information can be identified by the corresponding attribute name in the database. Update Patient Information The Update Information pathway includes a page to allow the user to choose what to update (HTML and a PHP file), followed by several dynamically created web pages via PHP. The dynamically created web pages query the database, then use syntax such as print(“<myHtmlTagHere>”). By setting the value attribute on the HTML form tags equal to the variable received back from the database query, the information already in the database is inserted into the forms to be changed. Upon submission, the code first deletes the old record via 22 “DELETE FROM <tablename> WHERE <columnname>=’value’ ” and then inserts the new information. Login and Registration Additionally, a login screen (HTML & PHP), as well as a doctor registration screen (HTML & PHP), has been created. The login screen allows access via a “SELECT” query and the registration screen allows doctors to enter their information via an “INSERT” query. Patient Message View This “pathway” includes a PHP file that retrieves all the questions for that doctor and posts them in tables to be answered. Again PHP is used to dynamically create tables. In each table entry there is a textfield, to allow the doctor to answer, and a submit button. Each text field/Submit button combination corresponds to an entry in an array within the PHP code. Upon submission, another PHP file will decide which question has been answered (based on the submit value) and places the question and answer back into the database. This was done through an UPDATE query so as not to destroy the data integrity of the database. The setcookie(CookieName, CookieValue, ExpirationDate) function was used in a very important way, that is to create “pseudo-sessions”. Within a “pseudo-session”, the cookie allows us to maintain information about the user, provide a certain amount of security, as well as minimize the amount of data entry when possible. The cookies currently used are CurPatID—which holds the current patient’s ID number and CurDrID—which holds the current Dr.’s Login ID. The CookieValue, which is just a string, can be accessed anywhere once set using a PHP variable $CookieName. Also, it should be apparent that all spaces in any of the entries are replaced with underscores. The print() command in PHP will only print up to the first space when the argument is a variable. The solution to this is to use the echo() command. Emergency Server The EmergencyMedic feature allows for the user immediate medical attention if an emergency occurs. For example, if a user were having a heart attack, the user would hit the designated EmergencyMedic button. This would prompt the ReadyMedic to capture the GPS string and send it along with the device ID to the server. The server would then dispatch the GPS string and certain medical information associated with the device ID to an available operator. The operator would then have the location of the user in the world and would be able to dispatch the appropriate emergency medical services. The whole process can be summed up in this diagram shown in Figure F-6: 23 User invokes the EmergencyMedic button. ReadyMedic captures the GPS string from the GPS receiver. ReadyMedic sends the GPS string and device ID to server. The server queries database for certain medical information. User’s life is saved! Operator uses location and medical information to dispatch emergency medical services. Server sends GPS string and medical information to available Operator program. Server searches for an available operator connected. Figure F-6 The EmergencyMedic service is broken up into four distinct parts: the NT Server, the Linux ReadyMedic Server/ Database, the Operator application, and the ReadyMedic device. Each of these has more subsystems, which we will go into detail later. An overview of the system is pictured below in Figure F-7. Linux Server/Database NT Server ReadyMedic Device Operator Figure F-7 This shows how each of the components piece together. The central component, the NT Server, is the key part of the system. It handles requests for both the Operator and the ReadyMedic Device as well as connections to the Linux Server to query the database. When the user hits the EmergencyMedic button on the ReadyMedic, the ReadyMedic should open a serial port connection and read the GPS data off the serial port. This will be described more in detail later. Once the GPS string has been verified as correct data, the ReadyMedic sends the GPS string a long with the ID of the device to the NT Server. The NT Server, which keeps track of Operators logged into the system, then opens a connection with the Linux Server and sends the ID. Then it waits for the Linux Server to send back the person’s information. Then it closes the connection with the Linux Server. After that, it looks for an available Operator logged in and makes a connection with it. It then sends the GPS string along with the person’s information to the 24 Operator. The Operator is notified that there is an incoming call and the Operator user is able to view the coordinates of the person who requested the help and who that person is. The nice feature of this system is that the Operator will be able to locate where this person is and who this person is. No matter where a ReadyMedic user is in the world, they will be able to request emergency assistance. The GPS device, the Motorola Oncore VP, is a very useful piece of this project. Not only is it easy to use it also gives the means to track down a user’s location. NT ReadyMedic Emergency Server This is the key part of this system; this binds the system together and handles communication between each of the parts. It listens on three separate ports, and it opens up to three separate connections. It uses a networking set of functions called sockets. Sockets are the endpoints for communication which allows an application program to “plug in” to the network and send data (i.e. streams or blocks of bytes) to any other program that is also plugged into the network. Because this server was written for the Windows NT Operating System environment, we used the Microsoft Foundation Class CSocket. There are six method associated with creating, opening, and reading and writing sockets: create(), connect(), listen(), accept(), receive(), send(). With these functions, it is simple to create an application as a server or a client. The NT Server acts as both a server and a client because of the way it routes the information to different location. To start, I will go into the steps of setting up a server socket. BOOL Create( UINT nSocketPort, int nSocketType, LPCTSTR lpszSocketAddress); This method is called regardless whether or not you set up a socket connection as a server or a client. What determines the difference is the parameters you send to it. For a server, you want to specify a port number for nSocketPort. For our project, we used 7003, 7004, and 7005 to specify the ports to listen for ReadyMedic connections, Operator logins, and Operator logouts respectively. The nSocketType is set to SOCK_STREAM which uses TCP protocol and is defined in one of the Windows libraries. This can be set to DGRAM and support the User Datagram Protocol, but these can be somewhat unstable so we went with the TCP protocol. The lpszSocketAddress is the IP address that you want to connect to. Since this is a server, we are not connecting to anything we are accepting connection so we set it to NULL. BOOL Listen( int nConnectionBacklog ); This method is specifically used for server applications only. It makes the socket a listening socket and sets the maximum number of pending connections in the queue. This number is denoted by nConnectionBacklog and it can not be more than 5. We call this in each of the server threads. BOOL Accept( CAsyncSocket& rConnectedSocket); This method blocks until a connection has come in. That is why we had to set up three threads to listen for different connections, which I will go into more later. You pass it the address of a socket that you want to use for reading and writing. This is denoted by the rConnectedSocket argument. Basically, it checks the queue periodically to see if a connection has come in. If there is an incoming connection, then it will stop blocking it and you can handle the connection. int Receive( void* lpBuf, int nBufLen, int nFlags ); virtual int Send( const void* lpBuf, int nBufLen, int nFlags = 0 ); These methods are used after a connection has been established. Using the rConnectedSocket that was specified in the Accept call, data can be sent and received using Receive() and Send(). 25 Receive waits until data has been sent to it. It can be configured to time out, but for our purposes we did not bother setting it up. lpBuf is the string that is to be sent or received. We used this to pass data in between process running on different machines. nbufLen is the size of the string you are sending or receiving. For more information about the CSocket class check the documentation in the Microsoft Developer Network Guide. Going back to the NT Emergency Server, we had to have three different sockets opened for listening. Because Accept() is a blocking call, we needed a way to listen to several ports without locking up the system. That involved using threads. Operator Query operator ID and password Linux Server Query patient information ReadyMedic Main Program Op2NTServer OpDisconnect NT2LinuxOp Operator Logins Operator Logouts ReadyMedic connections ServerThread NT Server Figure F-8 Figure F-8 is a more detailed version of the diagram to display the threads and the communication between them. As you can see, there are three threads started in the NT Emergency Server. There is one thread to listen for operators to log in. Once an operator attempts to login, the server connects to the Linux Server and then the Linux Server queries the database and tells them whether or not the operator name and password is in the database. If the Linux Server returns “TRUE”, then the NT Emergency Server adds the Operator’s IP address to a linked list. Then both connections are closed. The Operator now starts the ServerThread to listen for incoming connections from the NT Emergency Server. Now, the NT Emergency Server has an IP of an Operator stored in the linked list. This will allow the NT Emergency Server to open a new connection with the Operator once a request from the ReadyMedic is made. The NT Emergency Server is also listening for Operator Logouts. Once an Operator disconnects from the system, the 26 NT Emergency Server removes that from the linked list. The third thread that is running on the NT Emergency Server is NT2LinuxOP, which is the thread that listens for ReadyMedic connections. Once a connection is made, the NT Emergency Server makes a new connection with the Linux Server and sends the patient ID that it received from the ReadyMedic. The Linux Server returns to the NT Emergency Server the patient’s medical information. The NT Emergency Server takes the first element off the linked list and opens a new connection with the Operator program. It then sends the GPS string and the patient information to the Operator program. This is a very complex system because of so many components communicating with each other. You might be wondering why we did not just simply used the Linux Server instead of making this separate NT Server. Well, the Linux Server is connected via a DSL connection and is not on the TAMUNet network. Therefore, the firewall prevents it from making connections inside TAMUNet. The NT Server runs on one of the machines in the lab and therefore can make connections inside and outside of TAMUNet. Operator Figure F-9 Figure F-9 shows the User Interface of the Operator program. As you can see, it is very simple. There is a button to connect to the NT Emergency Server. There is a button to disconnect from the NT Emergency Server. The bottom portion of the application is used solely when an emergency occurs. Once an emergency occurs, a message box pops up notifying the incoming emergency. The Alert Mode status is changed and the Latitude and Longitude coordinates are set. The View button becomes active. Once the View button is active, the Operator can click it and view the patient’s information: name, address, home and work phone number, longitude, latitude, and the emergency contact. 27 Connecting To connect to the server, the Operator hits the connect button. This brings up a password for logging into the system. When the user hits ‘Submit’, the Operator program opens a connection to the server as a client. To do this, we used the same class CSocket as we did above in the NT Emergency Server. However, this time the Operator is a client instead of a server. We still call create(), but this time we do not specify the port number. BOOL Connect( LPCTSTR lpszHostAddress, UINT nHostPort ); Instead of calling Listen and Accept, we call a method named Connect. We need to specify the address of the computer we are connecting to. The NT Emergency Server is located on “barbrady.cs.tamu.edu”. The type of socket is still SOCK_STREAM. For our project, the port number for Operator connections is specified as 7000. If no errors have occurred, we can read/ write to the socket all we want. We send it the Operator name and password and the NT Emergency Server returns whether the name and password is valid. It is valid, the Operator is now logged in. It starts its own thread, ServerThread, to listen for incoming connections. Starting a thread in Windows is simple using the Microsoft Foundation Classes. You create a thread in this format: UINT ServerThread (LPVOID pVoid) pVoid is a void pointer and the function must return an unsigned integer, which represents the exit code. If it returns a negative one, then the thread has been closed by some other means than a normal exit. To start the thread, we call AfxBeginThread(ServerThread) and it starts running. In our implementation, we call Accept, which is a blocking call. To be able to close this thread outside the thread, we have to call CancelBlockingCall on the socket it has opened. This exits the Accept function and then exits the thread. Server Thread The ServerThread opens a socket for listening. It waits for a connection from the NT Emergency Server. Once a connection has been made, it reads the GPS string and information from the NT Emergency Server and notifies the Operator user that of the incoming connection. It parses the GPS string into the longitude and latitude coordinates and displays all the information into the appropriate windows. Parsing the GPS string is simple; we simply look for commas as breaks of the data. Here is the break down of the GPS string: $GPGGA,[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12] *hh[CR][LF] [1] – UTC time of position fix, hhmmss format. [2] – Latitude, ddmm.mmmm format. [3] – Latitude hemisphere, N or S. [4] – Longitude, ddmm.mmmm format. [5] – Longitude hemisphere, E or W. [6] – GPS quality indication. 0 – no GPS, 1 – GPS acquired. [7] – Number of satellites to use. [8] – Horizontal dilution of precision, 1.0 to 99.9. [9] – Antenna height above/below mean sea level. [10] – Geoidal height. [11] – Differential GPS (RTCM-SC104) date age. [12] – Differential Reference Station ID,0000 to 1023 *hh – Checksum value in hex. 28 For our purposes, we parse the [2], [3], [4], and [5] elements of the string. Disconnecting Disconnecting from the NT Emergency Server is basically the same as connecting to it. It simply opens a connection to “barbrady” on port 7005 instead of port 7000. It sends a disconnect message to the NT Emergency Server and the NT Emergency Server removes the Operator’s IP from its list. This application consists of using the ReadyMedic of three parts. One to start a thread to count down to ten. Another to communicate with the serial port to obtain the GPS string from the Motorola Oncore VP. The last to connect to the NT Emergency Server. To handle the serial communications between the GPS device and the Mobile Pro, the serial port needed to be configured. A class called CComm was created, which opened the serial port, closed the serial port, read the serial port, and verified the data sent. Count Down Thread The CountDown thread was made to help deter mistakenly making calls to the NT Emergency Server. Basically it launches a thread to Count down for ten seconds. In that time the user can hit the “Abort” button to cancel making the call. If it does not cancel the call, it will open the serial port and read the GPS string off the serial port. It checks to see if the data is valid and then sends it to the NT Emergency Server. Opening the Serial Port The opening the serial port method is the most important step in configuring the serial port. In order to open the serial port, the function CreateFile is called to initialize the serial port to be read. The COM port is specified as COM1: and opened for GENERAL_READ and GENERAL_WRITE. There is a struct type called DCB, which is used to set up the serial port’s properties. Here is the current setup: PortDCB.BaudRate = CBR_4800; PortDCB.fBinary = TRUE; PortDCB.fParity = TRUE; PortDCB.fOutxCtsFlow = FALSE; PortDCB.fOutxDsrFlow = FALSE; PortDCB.fDtrControl = DTR_CONTROL_ENABLE; PortDCB.fDsrSensitivity = FALSE; PortDCB.fTXContinueOnXoff = TRUE; PortDCB.fOutX = FALSE; PortDCB.fInX = FALSE; PortDCB.fErrorChar = FALSE; PortDCB.fNull = FALSE; PortDCB.fRtsControl = RTS_CONTROL_ENABLE; PortDCB.fAbortOnError = FALSE; PortDCB.ByteSize = 8; PortDCB.Parity = NOPARITY; PortDCB.StopBits = ONESTOPBIT; PortDCB.EvtChar = '$'; 29 The baud rate is set for 4800, which is the speed that it needs to be to capture the GPS string from the Oncore VP. Notice the event character being set as a ‘$’. This character signals the program to start reading the characters into a buffer. Next, the SetCommState is called with the DCD struct to set up the COM port with these settings. The GetCommTimeouts and SetCommTimeouts are called to configure timeouts. These are not important because there are no timeouts set. The last function called is SetCommMask with the EV_RXFLAG parameter. This sets the serial port to watch for the event character. Reading the Serial Port Reading the serial port is fairly simple. WaitCommEvent is called to wait for the event set up by the SetCommMask. For the GPS string, it waits until it sees a ‘$’ pass through the serial port. ReadFile is called to read one byte in from the serial point. The character is checked to see if it is a valid character and then it is stored in the buffer. It keeps reading until a newline character is read in. Closing the Serial Port Closing the serial port is very simple. CloseHandle is called to close the serial port. The serial port is now available to be opened by another program. Verifying the Data The Motorola Oncore VP, basically sends data in this format: $GPGGA,[Location and Time],*[Checksum] There is also a ‘\r’ and a ‘\n’ at the end of the string. A simple way to view this would be to open a HyperTerminal Window with a 4800 baud connection. You would see that the Motorola Oncore VP simply repeatedly writes this to the serial port. There is no message passing or anything like that. Sometimes, the information received off the serial port is corrected and needs to be checked. That is why the Oncore sends a checksum to the serial port. The checksum is equivalent to each byte XOR’ed together from the character after the ‘$’ up to and including the ‘*’ character. Our algorithm simply scans through the characters and XORs them together. It then converts the calculated checksum to a string and compares it to the checksum received from the Oncore VP. Connecting to the NT Emergency Server This is one area which we were not able to complete due to a bug in the Windows CE build that we have. There is a corresponding class to Windows CSocket for Windows CE. It is called CCeSocket. It is suppose to have the exact same functionality just implemented slightly differently. However, when we attempted the same connections using the CCeSocket class as we did for the CSocket class it failed. We made a program to emulate the functionality of this connection in Windows NT called testclient. Basically, it sends a hard coded GPS string and patient ID to the server. For this, we make a client connection using the port number 7003. It then sends the GPS string and the patient ID to the server and closes the connection. Linux Server and Database This part of the system is used to query the database on the ReadyMedic server “ReadyMedic.dyndns.org”. This allows the NT Emergency Server to verify the operator login and obtain the patient information. This piece is broken up into two separate processes: emergency1 and emergency2. Emergency1 handles all the requests from the NT Emergency Server dealing with operator logins. It uses a library called queryclient to determine if the operator name and password is in the database. If it is determined that the operator is in the database, then it sends back “TRUE”, otherwise it sends back “FALSE”. Emergency2 handles all 30 the requests from the NT Emergency Server dealing with patient information. It reads in the patient ID and queries the database. It then sends all the information back to the NT Emergency Server. Both of these processes are very similar and use the same principles of sockets. Since this is on a Linux server, they use Berkeley Sockets and not Windows sockets. Berkeley sockets are very similar, but require more function calls than the Windows sockets. The processes are set up to listen on two separate ports for incoming requests from the NT Emergency Server. LabVIEW Medical Devices & MedDevice Program The purpose of this document is to explain the design and workings of the medical device interface for the ReadyMedic. The LabVIEW simulation will first be discussed, and then the workings of the MedDevice program will be considered. Figure F-10 – Medical Device Simulation Control Panel 31 The medical device attachment is made up of two parts. First, there is the medical device simulation. This was implemented using LabVIEW. The control panel of the interface is shown in Figure F-10. This simulation simply sends a string, along with some control characters, down a serial port. The serial port can be set. As default, the serial port is COM2 and the baud rate is 9600. To use the simulation, all you have to do is choose which device you want to simulate. There are four devices. 1. 2. 3. 4. Thermometer Blood pressure Pulse Blood sugar You simply set the value that you want on the device you wish to model, choose which cluster to send using the knob at the bottom right, and then press the red arrow on the top left of the screen. To stop the simulation, you press the green Program State button on the bottom right. Figure F-11 – MedDevice User Interface The second part of the medical device attachment is the MedDevice executable, which runs on the ReadyMedic. The user interface for MedDevice is shown in Figure F-11. To use this program, all you need to do is press “RUN”. After that, it will read in data from the serial port (from the LabVIEW simulation) and display the data in the proper text box. The other three instruments that are not being simulated will show “N/A”. The data is then logged to a text file called “meddevices.txt” that is in the same directory as the executable. In order to ensure data integrity between the simulation and the ReadyMedic, the MedDevice executable should be started (i.e., you should press run) before the LabVIEW simulation has begun. The LabVIEW module is fairly simple. The schematic diagram is shown in Figure F-12. 32 Figure F-12 – LabVIEW simulation schematic diagram To change the port number, you simply change the integer constant next to the loop. 0=COM1, 1=COM2, and so on. The baud rate is set at 9600. You may need to change the port number based on which serial port you hook the ReadyMedic up to on your PC. The rest of the diagram is self-explanatory. It would not take much work to add more devices. You would also have to edit the MedDevice executable, however, to deal with these new devices. The MedDevice executable is fairly simple. Each button has an action associated with it. When RUN is pressed, the program initializes the serial port, opens it, and begins reading from it. It waits for a ‘$’ before accepting any characters. It stops reading when it receives a ‘\n’, or newline character. To modify MedDevice, you need Microsoft Visual C++ 6.0. If you wish to add a device, you use the Class Wizard to edit the existing class, and add fields as you see fit. You then go to MedDeviceDlg.cpp and edit the source code to deal with the extra text box. You need to edit the methods: 1. 2. 3. 4. void CMedDeviceDlg::ShowReceivedMessage(LPARAM message, int type); int CMedDeviceDlg::StringType(TCHAR *text); TCHAR * CMedDeviceDlg::StringParser(TCHAR *text); void CMedDeviceDlg::FileWriter(TCHAR *text, int type); The method, ShowRecievedMessage, simply prints the parsed string out in the correct text box. Method StringType returns an int, and differentiates between the types of strings for the other functions; 1 is temperature, 2 is blood pressure, 3 is pulse, and 4 is blood sugar. Method StringParser simply returns the string, minus the character that identifies which type it is. Method FileWriter writes the data to a file, based on what type it is and the data associated with it. 33 TEST REPORT AND RESULTS Beta Testing The web component of the ReadyMedic project was involved in Beta testing using medical personnel. Three individuals who are third year medical students at Baylor College of Medicine were given tasks to accomplish using the website www.ReadyMedic.dyndns.org with given login ids and passwords. The tasks included adding a new patient, updating information on a current patient using the history of the present illness form and viewing the information that they had inserted. One of the persons was observed using the website and areas of difficulty and frustration were observed. The following items were mentioned and observed by these persons and were corrected to improve the project. Alignment of the secondary emergency contact information. Lack of acceptance of the last name of emergency contact persons. Inadequate information accepted and returned from the Social History entry form. The following items were mentioned by these persons, and due to the lack of time we were not able to adequately make changes. Physical Exam form on the site for input on History of the Present Illness would be beneficial. ‘Submit All’ button when entering information in multiple categories of the adding a new patient and updating patient web pages. View all patient information on one screen as well as viewing each category separately. Smaller input boxes for Family History input to account for the size of the entries in the database. Component Testing As the project was assembled, each person tested individual components apart for the rest of the project. For instance, a main module was created for QueryClient and it was thoroughly tested before it was built as a library . Before it was known that Windows CE sockets calls would not operate correctly, rmClient() was created to exercise the ReadyMedic Linux server. TestClient() was created to exercise the emergency server. The MedDevice interface was able to be tested apart from the rest of the ReadyMedic user interface, largely due to the fact that it is run as a separate process. Thus, as each component was created, its component testing module was run and the output was verified. Every module underwent this testing before being incorporated into the large whole, and top down testing progressed. Top Down Testing Although it would have been ideal to have several typical users use the device for several days as a part of the testing phase, this was not possible because of time constraints and the need for cord connectivity for several of the features. However, the team spent a day participating in top down testing of the medical device and web pages. Individuals of the team spent time using the functions of the project that they were not directly involved in developing and looking for any problems or inconsistencies. The following bugs were found: 34 In the Doctor Reply web page, the doctor may not enter an apostrophe. Although hitting submit seems to work normally, the reply never seems to make it into the database. In the Ask A Doc portion of the ReadyMedic user interface, the user may not type punctuation (such as !,.?) to the doctor. Although it will transfer and be processed okay, the resulting expected filename will expect to have that punctuation in it, which is illegal for a Windows filename. Cookies apparently do not work with the htmlView module. When a scheduled medicine is completed and removed, it removes all other medicines, and occasionally displays error messages. This error was fixed to perform normally. Information was passed incorrectly from the queryClient module to the Operator module. The systolic reading sent from LabVIEW to the ReadyMedic can only display 2 digits, and thus values above 99 will only display the upper two digits. htmlView does not handle the post method, however, it does accept the get method. 35 CONCLUSION The ReadyMedic project collaborated many of the aspects of our college education and forced us to use these components to provide useful deliverables. These topics incorporated Software Engineering, Operating Systems, Networks, Databases, and Microprocessor Systems Design. The following is a list of the topics, which were researched and used in the actual development, and topics researched and then not even required in the implementation. Development: Windows 98/NT/CE Programming Linux Programming Serial Prot, TCP & Client/Server Programming Multithreading Inter-Process Communication Linux System Administration MySQL Database Administration C API HTML PHP LabVIEW Research Only: VPN Perl Sockets MedDevice Connection Thus, this project has been good at integrating our entire education together. All members were able to learn a great deal of new information and apply it successfully in the design of the ReadyMedic. 36 ACKNOWLDEGEMENTS The ReadyMedic project group would not have been able to successfully complete much of this project without the insight and expert advice of the individuals listed below: Cam Baker – University representative of Microsoft who aided us in obtaining upgrades for WinCE 2.12 and Internet Explorer 4.01 for WinCE in our attempts to work around Microsoft bugs. Aaron King – Employee of National Instruments who gave insight into means of modeling medical devices in LabVIEW for use with the ReadyMedic device. Dr. Rabi Mahapatra – Course professor who helped us in all stages of development of the ReadyMedic, especially with all the problems we faced. Prof. Willis Marti – Networks professor who assisted us in troubleshooting problems in connection with sockets. Kamran Shah – Employee of National Instruments who gave insight into means of modeling medical devices in LabVIEW for use with the ReadyMedic device. Robert Tate – Robert never failed to always be in the right place at the right time to give us valuable advice whenever we a problem would arise. 37 REFERENCES Books Boling, Douglas. Programming Microsoft Windows CE. Microsoft Press: 1998. Burdick, Robert. Essential Windows CE Application Programming. Wiley Computer Publishing: 1999. Turner, Alice (Project Editor). Microsoft Windows CE: Communications Guide. Microsoft Press: 1999. Turner, Alice (Project Editor). Microsoft Windows CE: Programmer’s Guide. Microsoft Press: 1999. Turner, Alice (Project Editor). Microsoft Windows CE: User Interface Services Guide. Microsoft Press: 1999. WebSites Apache – www.apache.org GPS – www.mot.com/ies/GPS/products/prodvp.html Last Semester’s GPS Team - http://www.cs.tamu.edu/courseinfo/cpsc483/common/99c/g2/g2.html Linux administration - www.linux.org Microsoft Developer’s Network – www.msdn.microsoft.com MySQL – www.mysql.org MySql Development - www.devshed.com/Server_Side/MySQL/Administration PHP – www.php.net Redhat Linux - www.redhat.com USB Implementers Forum Homepage – www.usb.org Webmonkey-The Web Developer’s Resource – www.webmonkey.com Windows CE – www.microsoft.com/windowsce 38