report-2

94
RFID-BASED PATIENT TRACKING SYSTEM Final Report Homewood Biomedical Design Associates Johns Hopkins University Team Members Dhondup Pemba, Stephanie Keung, Annand Sharma, Wilfred Wong, Brian Miller, Kihyuk Hong, Robert Dewan, Bill Diplas, Worawan Limpitikul, Jai Madhok, Sho-Yu Wang, Brigitte Warner Advisors Robert Allen, Ph.D., Principal Investigator Dickson Cheung, M.D., Co-Principal Investigator Submitted May 9th, 2006

Transcript of report-2

RFID-BASED PATIENT TRACKING SYSTEM Final Report

Homewood Biomedical Design Associates Johns Hopkins University

Team Members Dhondup Pemba, Stephanie Keung, Annand Sharma, Wilfred Wong, Brian Miller, Kihyuk Hong, Robert Dewan, Bill Diplas, Worawan Limpitikul, Jai

Madhok, Sho-Yu Wang, Brigitte Warner

Advisors Robert Allen, Ph.D., Principal Investigator

Dickson Cheung, M.D., Co-Principal Investigator

Submitted May 9th, 2006

TABLE OF CONTENTS

Title Page ............................................................................................................................ i

Table of Contents .............................................................................................................. ii

Abstract...............................................................................................................................1

Introduction........................................................................................................................2

Prototype Design ................................................................................................................3

Hardware Implementation .......................................................................................3

Software Setup .........................................................................................................4

Software Design.......................................................................................................4

Results .................................................................................................................................6

Programming Debugging.........................................................................................6

Future Improvements ...............................................................................................7

Appendices..........................................................................................................................8

Appendix A: Materials.............................................................................................8

Appendix B: Budget ................................................................................................9

Appendix C: Figures ..............................................................................................10

Appendix D: Calculations......................................................................................18

Appendix E: References ........................................................................................24

Appendix F: User’s Manual...................................................................................25

Appendix G: RFID Vendor Research ....................................................................37

Appendix H: Testing..............................................................................................41

Appendix I: Program Code ....................................................................................49

Appendix J: Proposal .............................................................................................83

ii

Abstract Our goal was to design, develop, and test a passive, portable, and inexpensive

patient tracking system that measures the time between registration and treatment by an attending physician. We decided to use a Radio Frequency Identification (RFID) system to easily and transparently follow patients from the point of admittance to when they are seen by a doctor; this system incorporated a software program, in JAVA, as well as unobtrusive RFID “tags” carried on their person, to register patients and analyze their hospital activity. Data collected by our system would aid hospital management in making important decisions regarding staffing, resource allocation, and physical space available to the Emergency Departments. If this system was put into place in many hospitals nationwide, it would provide a large-scale basis of comparison of registration-to-treatment times across institutions, establishing a standard for evaluating managerial decisions. This would lead to smarter choices in hospital management, potentially serving to minimize wait times and reduce overcrowding.

Introduction

Overcrowding in the nation’s emergency departments (ED) has put a spotlight on clinical efficiency. Currently, EDs in the United States have a serious problem with overcrowding. A 2001 Academic Emergency Medicine journal article indicates that over 90% of hospitals, both private and academic, experience problems with overcrowding. In recent years, this problem has only continued to grow. 1

In the continuing effort by emergency departments to improve clinical efficiency, it has become apparent that an improved system to record and measure the effects of managerial decisions is necessary. One of the most significant indicators of ED efficiency is the wait time between patient registration and evaluation by a doctor. Therefore, a system that measures this delay can be used to evaluate the effects of managerial decision-making in the ED. Figure 1 in Appendix C, demonstrates how managerial decisions can be correlated to emergency room wait times. The key features to consider are mean and standard deviation, where concurrent decreases in both indicate shorter wait times and faster treatment of more patients– both indicators of improving clinical efficiency.

Currently, the most common method used to track wait times is ‘manual data

tracking’. Our team found that this process is tedious, time-consuming and inaccurate, with over 83% of patient data logs containing factual errors. This outdated technique begs a modern approach to the problem. Measuring door-to-doctor time electronically is a potential method of measuring the effect of managerial decisions. While several software tracking systems are currently available, they all depend upon active input of data by providers who simply do not have the resources to perform this task2. A new approach could provide a welcome solution to this issue. Goals Our goal was to design, develop and test a passive, portable, and inexpensive tracking system that measures the time between patient registration with a triage nurse and treatment by an attending physician. We sought to design software that is easily scalable to accommodate an effectively unlimited number of patients, one which could produce an easily comprehensible report as well as output data to be further analyzed with statistical programs.

Experimental Solution We have developed a prototype using Radio Frequency Identification (RFID) technology that incorporates high-frequency readers and battery-powered (“active”) tags to track patient flow. Not only will our system passively track the amount of time it takes for a each patient to move from registration to treatment, it will also be able to present this data in a user-friendly and useful manner. We chose RFID because it is relatively

2

inexpensive and well-tested. Also, it requires no user interaction – making it a truly passive technology.

Constraints In order to demonstrate the widespread potential of this tracking system, we

abided by several project constraints: an operating frequency that must not cause any electromagnetic interference with hospital equipment, a reading range of between three and five feet, patient-carried tags small and compact enough to maximize their comfort and utility, consistent signal readability at different spatial orientations, and minimal user interaction.

Prototype Design Our team decided to utilize RFID technology which employs the use of portable tags and stationary “readers” to gather data. RFID tags are very small, integrated circuits that serve as antennas for sending and receiving signals at a specified frequency. For long-range readers, this falls in the Ultra High Frequency (UHF) range (from 1 MHz to multiple GHz). An “active tag” differs from a passive one in that it incorporates a battery to boost the power of its signal, as opposed to relying on the flux generated by the reader itself, as in the case of passive tags. Both varieties automatically broadcast a unique ID number which is detected by the reader when it enters its range. Incoming patients, as well as hospital doctors, would carry these tags. In addition to the constraints already discussed, limiting the possibility of picking up unintended signals was necessary to ensure accurate data capture. Therefore, we used an RFID system that had approximately a 3-5 ft radial range. After discussing our concerns with clinical engineers at the Johns Hopkins Hospital, we found that an appropriate reading frequency would be either below 460 MHz or above 650 MHz. This frequency range avoids interference with other vital hospital equipment. Readers above 650 MHz are prohibitively expensive for widespread implementation. We chose active tags because at frequencies less than 460 MHz, passive tags must be obtrusively large to consistently transmit their data and their reading ranges are dependent upon their orientation relative to the reader. Active tags at this frequency can be as small or smaller than a credit card and still offer more reliable transmission. Considering commercially available RFID systems using active tags and a reader that could operate at an appropriate frequency, we encountered prices in excess of $5000. With our limited budget, we had to find companies willing to provide a system at a substantially discounted price. After talking with 21 different RFID vendors (See Appendix G), we decided to purchase two 433 MHz readers and five tags from Avante International Inc. (see Appendix C, Figure 2). Hardware Implementation

3

With this equipment available, we then had to determine the most effective placement of readers within the Emergency Department in order to obtain patient flow data. We found that placing an RFID reader at the registration desk produced a significant number of accidental readings; tags that were not intended to be scanned were being falsely detected. Therefore, after several visits to the Emergency Department at Johns Hopkins Hospital and discussions with Dr. Dickson Cheung, we decided to implement an alternate design in which the admitting nurse provides a tag to the patient at check-in while at the same time registers the check-in time in the database with a click of a button. Both RFID readers would then be placed in individual treatment rooms connected by serial cables to networked computers. This remains an efficient system, because it simply requires a minimal addition to the pre-existing check-in procedure. (See Appendix C, Figure 3 for setup) Even with the two RFID readers placed in different treatment rooms, concerns still persist as to their reading ranges. Even with intervening walls, it is impractical to ensure that signals from this type of RFID system do not penetrate to adjacent rooms in the Emergency Department; this could pose unexpected problems if other patients or doctors are in close proximity. Thus, we had to ensure that the RFID readers’ ranges were limited to a single room. To do this, our design team went to the Johns Hopkins Bayview Hospital Emergency Department and tested varying configurations of attenuators, resistors that limit the broadcasting signal (Appendix C, Figure 4). By restricting the signal by 14dB, and optimally positioning the readers (Appendix C, Figure 5), we were able to limit the approximate range to a single treatment room. Once the RFID readers detect the presence of a tag within their range, the attached computer wirelessly transmits a signal with that tag’s unique ID and time of recording to the networked server. These data capture stations will record all times that the reader senses a tag within its reading range, whether carried by a doctor or a patient. For our patient tracking system, we are only concerned with the registration time (which is stored when the nurse registers a patient) and the time when the patient is first seen by the attending physician. To determine this latter piece of information, our software sifts through the raw data stored in the server and calculates at what time both patient and doctor are present in the treatment room. (See table 1 in Appendix C) Software Setup Appendix C, Figure 6 is a visual representation of the flow of data in our system for the entirety of a patient’s stay in the emergency department. In step one, the triage nurse assigns the patient an RFID tag by clicking the “Add Patient” button in the Java interface. The SQL sever now links the patient to this identification number and records the time. The reader in the specific treatment room (Room One in the diagram) reads both the patient’s and the doctor’s tags, and this data is captured by our software and recorded in the database. The software also calculates the duration between when the patient was registered and the doctor entered the room to treat him. An administrator can save this data and create a computer-generated report.

4

Software Design The analysis was the most challenging aspect of the software to design. We wrote our JAVA program using an object oriented approach, as can be seen in the Uniform Markup Language diagram in Appendix C, Figure 7. Appendix D explains this program’s operation in more detail. A summary of the design follows:

SQL Data Format The data stored in the SQL server contains the tag ID, reader ID, time period in which the tag is read by the reader. Each tag ID corresponds to either a patient or a doctor. Algorithm

1. Connect to the SQL server and read data row by row in chronological order. Each row contains the tag ID, reader ID, and the time the tag was read by the reader.

2. Create an empty list for logs of each tag ID. When reading each row, append the log to the list of the corresponding tag ID.

3. For each list, combine logs if the time difference between two consecutive times is less than 20 seconds.

4. For each of the combined logs in each list, if the time duration is less than 25 seconds, regard it as an interference and remove the entry from the list

5. For each patient’s list, compare with doctors’ lists and find the first encounter time and store the time as the treatment time for the patient.

6. For each patient, find the waiting time which is the time difference between the registration time and the treatment time.

Data structure Elements

1. A hash map that links tag IDs to corresponding patients 2. A hash map that links the tag ID to corresponding doctors. 3. A linked list for storing logs for each person in chronological order.

Classes

Visit: A "visit" has three components – the location, the entry time and the exit time. The location is just the reader ID. The entry time is the time when the person is first read by the reader and the exit time is the time when the person is last read by the reader. Person: a Person can be a Doctor or a Patient. An important component of a Person is the Visit ArrayList, which stores all the visits of that Person. For example, a Person may have three visits: in room A from time t0 to time t1, then in room B from time t2 to time t3 and then back in room A from t4 to t5. Doctor: The program has two main HashMaps, one to store the two Doctors and one to store all CurrentPatients. The number of CurrentPatients can be 0, 1, 2 or 3.

5

Patient: There is also an OldPatient LinkList. Once a patient has been treated, he will be removed from the CurrentPatients HashMap and be stored in the OldPatient LinkList ArrayList, HashMap, and LinkList are data structures which store information The code used to complete our project has been attached in Appendix I.

Results Programming Debugging After preliminary testing at Clark (see Appendix H, Tables 1, 2, 3 and 4 for tests and results), we encountered several problems and developed appropriate solutions. The table below summarizes the main issues and solutions we incorporated; see Appendix D, calculations, for a more details.

Problem Solution • More than one doctor sees the same

patient • Separately record first time of

encounter with every doctor. Consider the earliest encounter time as “treatment time”

• A patient/doctor transiently enters the patient treatment room

• Set an additional criterion for minimum visit time. Encounter must be longer than minimum visit time be considered “treatment”

• Reader samples ever 11 seconds – potential to be misinterpreted as several short visits rather than one continuous visit

• Set an additional criterion for continuous visit. “Continuous visit” if less than 20 seconds between subsequent readings

Once all of the initial issues were resolved, we performed a field test of the system at the Bayview ED. The output of our program corresponded completely with the data we recorded by hand over this time period. (See Appendix H, Tables 5 and 6) We were able to design, develop and test a passive, portable, and inexpensive patient tracking system to measure the time between patient registration by a triage nurse and treatment by an attending physician. The software interface used to record the patient’s admittance to the emergency room is shown in Appendix C, Figures 8, 9 and 10 (see user manual for detailed description of functions). Currently, it accommodates up to three simultaneous patient tags, each represented by an integer. As a tag is given to the patient at registration, the appropriate corresponding number is entered by the system user. The tag ID and its registration time are uploaded to the SQL server.

6

This data, as well as an analysis of the duration between registration and treatment, can be displayed on-screen with the click of a button and saved to file automatically. The user has the option of producing a report based on the saved data (see Appendix C, Figure 11). This report is intended to be easily read and interpreted by an administrator and can be subjected to further statistical analysis. In addition, our software is easily scalable to provide for up to 108 individual patients, with 103 readers – an almost unlimited capacity. (See Appendix D, for calculations) Future Improvements Despite successful results in the ED, there are several improvements that can be incorporated into our system. Currently, each RFID reader must be connected to a computer. As an improvement, we would like the readers to wirelessly transmit data to the server independent of an attached computer. In addition, we would like the software to incorporate options such as the simultaneous display of multiple graphs for quick comparison and the calculation of treatment time and efficiency ratios. Finally, we would like to be able to both expand the system to other parts of the hospital and collect data from other healthcare providers. If this system was put into place in many hospitals nationwide, it would provide a large-scale basis of comparison of registration-to-treatment times across institutions, establishing a standard for evaluating managerial decisions. This would hopefully lead to smart choices in hospital management, potentially serving to minimize wait times and reduce overcrowding.

7

Appendix A: Materials

The hardware components of patient tracking system consist of: • Avante RELAYER™ MONITOR ATM 8001 433 MHz Readers (2) • Avante 433 MHz active tags (5) • D-Link router • Serial to USB converter

The Avante Readers were used to capture activity from the Avante tags. The D-Link router was used to wirelessly transfer the captured data to the server. The serial to USB converter was used to allow data capture on laptops without a serial port. The software components that were used to write the JAVA program consist:

• JAVA SDK 5.0 • Eclipse IDE • Python • Matplotlib • Microsoft SQL Server 2005 Express

The Java SDK 5.0 was the language that the program was written in, and Eclipse was the editor used to write the JAVA program. Python was the language used to write the programs to generate the reports, and Matplotlib was the library for python that was utilized to develop the histogram and perform statistical calculations. The SQL server is responsible for maintaining a database.

8

Appendix B: Budget Component Quantity Total Cost RELAYER™ MONITOR ATM 800(433MHz RFID reader)

2 Part of Package

Active Tags

5 Part of Package

RFID Reader and Tag Package 1

$1000

D-Link Wireless Router 1 $50 Serial to USB Converter 1 $35 Programming Tools NA $0 Total $1085

9

Appendix C: Figures and Tables

Figure 1: Plots display positive effects of managerial decisions on clinical efficiency. The right graph represents an improvement in clinical efficiency. Y axis represents number of patients, and X axis represents waiting time in hours.

05

101520253035404550

0.0-1.01.0-2.0

2.0-3.03.0-4.0

4.0-5.05.0-6.0

7.0-8.08.0-9.0

9.0-10.0

10 .0-11 .0

11 .0-12 .0

12 .0-13 .0

13 .0-14 .0

14 .0-15 .0

15 .0-16 .0

16 .0-17 .0

17 .0-18 .0

18 .0-19 .0

19 .0-20 .0

Waiting time(hours)

Num

ber o

f Pat

ient

s

0

10

20

30

40

50

60

0.0-1.01.0-2.0

2.0-3.03.0-4.0

4.0-5.05.0-6.0

7.0-8.08.0-9.0

9.0-10.0

10 .0-11 .0

11 .0-12 .0

12 .0-13 .0

13 .0-14 .0

14 .0-15 .0

15 .0-16 .0

16 .0-17 .0

17 .0-18 .0

18 .0-19 .0

19 .0-20 .0

Waiting time(hours)

Num

ber o

f Pat

ient

s

0

10

20

30

40

50

60

0.0-1.0

1.0-2.02.0-3.

03.0-

4.04.0-5.

05.0-6.

07.0-

8.08.0-

9.0

9.0-10.0

10.0-11.0

11.0-12.0

12.0-13.0

13.0-14.0

14.0-15.0

15.0-16.0

16.0-17.0

17.0-18.0

18.0-19.0

19.0-20.0

Waiting time(hours)

Num

ber o

f Pat

ient

s

Improvement

Figure 2a: 433 MHz RFID Reader

Figure 2b: Active RFID tags

10

Figure 3: The JAVA program is installed on a workstation that connects to a SQL server. Data capture stations are located in patient treatment rooms; they capture and wirelessly transmit RFID tag activity within reading range.

Figure 4: Signal Attenuators, resistors that limit the broadcasting signal

11

Figure 5: Optimal positioning of readers

Figure 6 Step 1: Triage nurse clicks on “Add Patient” button, assigning patient an RFID tag. SQL server receives registration time and can now identify patient by identification number. Step 2: Reader in Room 1 reads patient and doctor tags, transmits data to SQL server. Step 3: Java program extracts data from server and calculates wait time. Step 4: User can view and save analyzed data, and can either view or save the report.

12

Figure 7: The UML represents the design of our software. The blue classes are responsible for creating the user interface, viewing and creating reports. The green class is used to communicate with the SQL server. The classes in red represent the core analysis engine of our program.

Figure 8: Main screen of program with the Add Patient button. Clicking Add Patient button imports tag ID and registration time stamp

13

Figure 9: Pressing Analysis button activates Statistics table. It has 3 columns which display registration time, time when treated by attending physician, and waiting time. Output is stored in a file that can be accessed at a later date.

14

Figure 10: Clicking the Report button pops up a new window that allows the user to generate, view and save a report of analyzed data. The report option is customized so that it can generate reports for specific time periods.

15

Figure 11: Sample report generated by our program

16

Table 1 is an example of how our program calculates the wait time for a simple case. The first column Event, is not in the server, but was added for clarification. The reader ID 999 represent that the data that came from the computer at registration, and informs us of a patient registering. The tag id 20001027, 20001029, and 20001024 corresponds to patient 1, 2 and 3 respectively. After registration, no entries are added to the SQL server until either reader in the patient rooms captures a tag. Below reader ID 1, corresponds to the reader in patient room 1, and the tag it captured is 20001027 which corresponds to patient 1, thus informing us that patient 1 has entered room 1.The reader keeps sending data to the server, that corresponds to the event, Patient 1 in Room 1, but for clarification, it is replaced .… When the reader with ID 1 picks up the tag 20000461, we are able to discern this event as the doctor entering the room. As repeated rows representing the events Doctor Treating Patient 1 and Patient in Room 1 are added to the server, seen by … , it is evident that treatment is occurring.

Event Reader ID Tag ID Start Time End Time Patient 1 Registered 999 20001027 4/16/2006 14:22 4/16/2006 14:22 Patient 2 Registered 999 20001029 4/16/2006 14:22 4/16/2006 14:22 Patient 3 Registered 999 20001024 4/16/2006 14:25 4/16/2006 14:25

No readings … … … … Patient 1 Enters Room

1 1 20001027 4/16/2006 14:25 4/16/2006 14:25

Patient 1 in Room 1 1 20001027 4/16/2006 14:25 4/16/2006 14:25

… … … … …

Doctor Enters Room 1 1 20000461 4/16/2006 14:30 4/16/2006 14:30 Patient 1 in Room 1 1 20001027 4/16/2006 14:30 4/16/2006 14:30

Doctor Treating Patient 1 1 20000461 4/16/2006 14:31 4/16/2006 14:31

Patient 1 in Room 1 1 20001027 4/16/2006 14:31 4/16/2006 14:31 … … … … …

17

Appendix D: Calculations Maximum number of tags supported 8 digit tag Id is composed of numbers between 0-9 So maximum numbers of tags are 108= 100,000,000 Maximum number of readers supported 3 digit reader Id is composed of numbers between 0-9 So maximum numbers of readers are 103= 1000 Waiting time= Treatment time - Registration time For our project, the main “calculations” is in depth description of the program Algorithm in Depth Important aspects of the program

1. Visit: a “visit” has three components – the location, the entry time and the exit time. The location is just the reader ID. The entry time is the time when the person is first read by the reader and the exit time is the time when the person is last read by the reader.

2. Person: a Person can be a Doctor or a Patient. An important component of a

Person is the Visit ArrayList, which stores all the visits of that Person. For example, a Person may have three visits: in room A from time t0 to time t1 and then in room B from time t2 to time t3 and then back in room A from t4 to t5.

3. The program has two main HashMaps, one to store the two Doctors and one to

store all CurrentPatients. The number of CurrentPatients can be 0, 1, 2 or 3. 4. There is also an OldPatient LinkList. Once a patient has been treated, it will be

removed from the CurrentPatients HashMap and be stored in the OldPatient LinkList.

Analysis Algorithm

1. First, we grab the database table from the SQL server and go through the table row by row

2. In each row, we check the tag ID (since we only have 5 tags, we hard code the tag

ID into our program. Two of them belong to doctors and three of them belong to patients).

3. If the tag ID of the row belongs to a doctor, we update the visit of the doctor as

follows: - If the location of this visit is the same as the location of the doctor’s

previous visit and the entry time of this visit is within 20 seconds of the

18

exit time of the last visit, the program treat this event as a continuous visit. That is, it will just change the exit time of the last visit to the exit time of the current visit instead of adding a new visit.

- If the location of this visit is the same as the location of the doctor’s

previous visit and the entry time of this visit is after 20 seconds of the exit time of the last visit, the program treat this event as a new visit. That is, the program will add a new visit to the doctor’s Visit ArrayList.

- If the location of this visit is different from the location of the doctor’s

previous visit. The program will do two things. First, it will go back the check the duration of the last visit (duration = exit time – entry time). If the duration of the last visit is less then 20 seconds, the last visit must have just been an interference (i.e. the doctor enters a room temporarily and walk out without doing anything) and the program will delete the last visit. After checking the last visit, the program will add a new Visit of the new location.

4. If the tag ID of the row belongs to a patient, the program does the following:

- If the location is “999”, it means that this patient is newly registered and a new Patient object will be created. At the same time, we will search through the CurrentPatients HashMap to see if any current patient has the same tag ID. If so, we will find the treatment starting time of the current patient and remove that current patient from the CurrentPatients Hashmap and put him in the OldPatients LinkList. Finally, the newly registered patient will be added to the CurrentPatients Hashmap.

- If the location is not “999”, we will get from the CurrentPatients

HashMap to find the current patient with the tag ID of the current row and update his location using the same three checking conditions as the doctor in step 3.

5. To find the treatment starting time of a patient, the program first pulls out all his

visits. He is supposed to only have one visit, i.e. in the patient room from time t0 to time t1. However, he may leave the room momentarily to go to restroom from time t2 to time t3 and then re-enters the patient room. Then he will have two visits and the second visit being in the patient room from time t4 to time t5. For each of the visit of the patient, we check all the visits of all the doctors. If the location of a visit of the doctor is the same as the location of the visit of the patient and their visit times overlap, the treatment starting time will be the first time they met. In other words, it doesn’t matter whether the doctor enters the room first or the patient enters the room first as long as they meet in the same room and the treatment starting time will be the time when they first meet. Since a patient may be seen by several doctors for several times, the treatment starting time will be the first time the patient meets a doctor. If the program couldn’t find any time when the patient sees a doctor, the patient will be considered as not treated.

19

Problems and Solutions Problem one: More than one doctor sees the same patient (either at the same time or at two different times) - Original design For each patient, we checked one doctor at a time to obtain the first time the doctor and the patient were both read by the same reader. If we find such time, we stopped checking and regard the time we got as the treatment time. The problem with using the original design, all the doctors were not checked. - Solution. For each patient, we get the first encounter time with every doctor, and regard the earliest first encounter time as the treatment time. Problem two: A patient or a doctor accidentally enters a room (temporarily) This is considered a problem because if a patient or a doctor accidentally enters a room, the program might consider it as a visit thus resulting in a false treatment time. - Solution. We had to fix a parameter in the program based on the data analysis. The constant we fixed is the minimum time (20 seconds) for a visit to be considered as an actual visit. (Important for eliminating interference) Problem three: A patient registers but leaves without seeing a doctor The program was originally designed so that not treated patients are classified into "not treated patients" instead of keep increasing the waiting for such patients.As we expected, the program handled this problem correctly. Problem four: Reader Artifacts The reader reads every 11 seconds or so and records the time at which each tag was read. So even if the tag is right beside the reader, the reader records data as.. from 1:00:00 to 1:00:00 from 1:00:11 to 1:00:11 from 1:00:22 to 1:00:22 from 1:00:33 to 1:00:33 and so on instead of from 1:00:00 to 1:00:33 Sometimes the reader fails to read every 11 second and takes a little more time to detect the tag. For example, from 1:00:00 to 1:00:00 from 1:00:11 to 1:00:11 from 1:00:27 to 1:00:27 from 1:00:38 to 1:00:38 ...

20

This was a problem for determining exactly what case we should consider as a single "continuous" visit, instead of multiple short visits. - Solution We manually looked through the data table and analyzed the performance of the reader. We concluded that the reader takes at most 20 seconds to pick up the signal. As long as the two consequent readings are of less than 20 seconds difference, we considered the readings as "continuous" readings. Even if what actually happens in the reality can be two different visits rather than one continuous visit, the error of waiting time is at most 20 seconds, which is negligible. Main Program Classes and Explanations Java Classes The main analysis program consists of 5 Java classes (SQL, Person, Patient, Doctor and Visit). The other classes are for the GUI and report viewing/saving. Class SQL Variables of SQL: con

- the Connection from Java to Microsoft SQL server doctors

- the HashMap to store doctors cur_patients

- the HashMap to store currents patient waiting to be treated old_patients

- the LinkList to store old patients whom have already been treated or have left without a treatment.

tableModel

- the PatientTableModel to output the analysis in the GUI DOCTOR

- the string array to store the two tag IDs of the doctors PATIENT

- the string array to store the three tag IDs of the patients Functions of SQL Connection getConnection()

- returns the Connection to Microsoft SQL server void displayDbProperties()

- display the properties of the database

21

void executeStatement( String statement )

- enable Java to ask SQL to execute a SQL statement void addPatient(String tagID)

- when the add patient button is clicked on the GUI, this function is called to append a new row to the SQL database with location “999” and tagID

void analyze()

- implements the analysis algorithm static void getData( Timestamp from, Timestamp to )

- when the user chooses the time range of a report, this function is invoked. It will copy the relevant data from the raw data (rawdata.txt) and print it to a new text file (data.txt) so that the Python codes can use this new text file to generate the report.

Class Person (super class of Doctor and Patient) Variables of Person: tagID

- the string to store the tagID of the person visit

- the ArrayList to store all the visits of the person Functions of Person void addVisit( String location, Timestamp entry, Timestamp exit )

- add a visit to the Person and do the checking as described in step 3 of the algorithm

long timeDiff( Timestamp time1, Timestamp time2 )

- returns the difference between two times void deleteInterference()

- delete every visits that has duration of less than 20 seconds Timestamp firstEncounter( Person p )

- return the first time when this person meets with person p. Timestamp firstEncounter( Visit v1, Visit v2 )

- returns the first time when the two visits overlap

Class Doctor (inheritance of Person) Functions of Doctor

22

String toString() - display the tagID of the doctor and all his visits

Class Patient (inheritance of person) Variables of Patient: time_registration

- the time when the patient registers time_treatmentStart

- the treatment start time of the patient Functions of Patient void getTreatmentTime( Iterable<Doctor> doctors )

- find the treatment starting time of the patient String toString()

- display the tagID of the patient, all his visits and the treatment starting time Class Visit Variables of Visit: location

- the string to store the location of the visit (the reader ID is the location in the program)

entry

- the entry time of the visit exit

- the exit time of the visit Functions of Visit String toString() - display the location, entry and exit times of the visit.

23

Appendix E: References 1. Derlet R, Richards J, Kravitz R. Frequent overcrowding in U.S. emergency departments. Acad Emerg Med. 2001 Feb;8(2):151-5. 2.Ryan Forde , Massachusetts General Hosptial;C. Pitman Baker & Associates, Inc. , CPBEmergent RFID solution; Dr. Druckenbrod, Urgent Matters; Charles Feldmen, Precision Dynamics Control; Dr. Gerald Sandler, Georgetown University Hospital; John Martinez, RFID inc;

Matplotlib

3. http://matplotlib.sourceforge.net/

RFID

4. http://en.wikipedia.org/wiki/RFID

5. http://www.rfidjournal.com/faq

6. http://msdn.microsoft.com/vstudio/express/sql/

SQL

7. http://www.w3schools.com/sql/default.asp

8. http://www.sql.org/

Python

9. http://diveintopython.org/

24

Appendix F: User’s Manual

25

Contents Hardware Requirements............................................................................................... 27 Database Server, Data Capture Station, Workstation for Viewing SQL Server Setup .......................................................................................................... 28 Microsoft SQL Server Express, Creating tables Data Capture Station Setup .......................................................................................... 29 Avante Software, Reader Connection Workstation for Viewing Setup .................................................................................... 30 Patient Entry and Report Generator Overview of Hardware Setup ........................................................................................31 Network connections, Reader connections Using DT5 Software....................................................................................................... 32 Adding Patients, Analyzing Data Troubleshooting ............................................................................................................. 36

26

Hardware Requirements

• Database Server o RAM: 256MB (512 recommended)

o CPU: P4 (1.8GHZ or more)

o HDD: 100MB (Depending on size of tracking application)

o I/O: Network Connection (802.11g or Fast Ethernet recommended)

o OS: Microsoft Windows 2000, XP, 2003

• Data Capture Stations o RAM: 128MB (256 Recommended)

o CPU: P3 or equivalent (P4 recommended)

o HDD: 10MB

o I/O: RS232 or USB*, Network Connection (802.11g or Fast Ethernet recommended)

o OS: Microsoft Windows 2000, XP

• Workstation for Viewing o RAM: 128MB (256 Recommended)

o CPU: P3 or equivalent (P4 recommended)

o HDD: 10MB

o I/O: Network Connection (802.11g or Fast Ethernet recommended)

o OS: Microsoft Windows 2000, XP

*Requires additional USB to RS232 converter sold separately

27

SQL Server Setup

If a Microsoft SQL server already exists on the network, skip to step 3

1) Install the following on the Database Server (files located on the software CD)

a) Microsoft .NET Framework 2.0

b) Microsoft SQL Server Express Edition

c) Microsoft SQL Server Express Studio Manager

Note: Additional information on installing these applications can be found at:

http://msdn.microsoft.com/vstudio/express/sql/

2) Set up network connection

a) SQL server must have static IP address or DNS registration

b) SQL server must be set to allow TCP/IP connections on the specified IP

address or DNS name.

c) All Data Capture Stations and Workstation for Viewing must be have TCP/IP

communication with server

d) Contact your network administrator

3) Create the database and tables (or ask your network administrator to do so)

a) Execute Create_DB.sql script on the server to create ‘AccessTrakker’

database. The file will automatically create a user name: ‘sa’ and password:

‘password1’

b) Execute Create_Table.sql script to create four tables:

‘Avante_RFID_CapturedTag’, ‘Avante_RFID_EntranceMapTable’,

‘Avante_RFID_MonitorStatus’, ‘Avante_RFID_ReaderDoorMap’

28

Data Capture Station Setup

1. Connect readers via RS232 to stations

2. Set up network connection

a. Data Capture Station must be have TCP/IP communication with server

b. Contact your network administrator

3. Install ATMS Data Capture System

a. Just Click ATMS_DataCapture_Install.msi. It will create

[DataCapture1.0] icon on your computer's desktop

4. Change computer to military time (this is necessary for capture times to be

reported accurately)

5. Setup Time on RFID Readers

a. Make sure reader is plugged in and turned on

b. Go to Menu>Setting>Date & Time

6. Run the system

a. Click [DataCapture1.0] Icon in your computer's desktop.

b. The system will ask you to enter a server name, username, and password

when you first run it. Use the IP address or DNS name of your SQL server

and the username: ‘sa’ and password: ‘password1’. After logging in, the

system will automatically start running

c. If any readers running status is failed, please follow bellow steps:

(1) Click [Stop] button on the screen to stop running the program

(2) Click [Open Com] button to make sure the COM port is fine.

Suppose you will see the information: Com Port X is open

successfully

(3) Make sure the readers’ power is turned on

(4) Reset all readers by press reset button on the right side of the

readers

29

Workstation for Viewing Setup

1. Install JAVA SDK 5.0 (included on software CD)

2. Run DT5 Software JAVA program (included on software CD)

3. Set up network connection

a. Workstation for Viewing must be have TCP/IP communication with

server

b. Contact your network administrator

30

Overview of Hardware Setup

1. Database Server, Data Capture Station, Workstation for Viewing must all have

TCP/IP connectivity via 802.11g or Fast Ethernet. This means they should all be

able to ping the server and all be able to log into SQL server using ‘sa’ and

‘password1’.

2. RFID readers must be connected to Data Capture Stations.

31

Using DT5 Software

1. This represents the main screen of our program. The first feature noticed is the

“Add Patient” button. Currently we have 3 patient tags, and each one is

represented with a number from 1 to 3. As the appropriate tag is given to the

patient at registration, the corresponding adds patient button is pressed, this

imports the tag ID, the fact that it was at registration, and the time to the SQL

server.

32

2. The second main feature noticed, is the statistics table. This table is activated

when the analysis button is pressed. When activated it displays the registration

time, the time when treated by the attending physician, and the waiting time,

which is the time difference between registration and treatment time.

33

3. The third section has two buttons, analysis, and report. The analysis button

performs the waiting time calculations, displays it in the statistics, and stores it as

a file. The report button brings up a new window, with the options of saving a

report of the currently analyzed data, or the options of viewing previous reports

generated.

34

4. The following is an example of the report produced by our program. The graph at

the center provides a histogram of the number of patients and the waiting times

they experience. The table below the graph provides quick and relevant

information about the data, such as average, median, maximum, and minimum

waiting time. It also provides the standard deviation of the sample of waiting

times collected. The last row in the table provides the number of untreated

patients, these patients are classified as patients who never see a doctor, or have

waiting times exceeding 24 hours.

35

36

Troubleshooting

The Avante software can’t connect to the reader

Make sure your reader is connected to the first RS232 COM port on your PC. If they are

not labeled, try switching to the other one and restarting reader and program.

The Avante software can’t connect to the SQL database

Make sure your workstation can ping the server and that you are using the correct server

name, username, and password. You might want to download a simple SQL query tool to

test your login information. Contact your system administrator.

The reader is picking up tags outside the desired read range

Add additional attenuators to the reader antenna

There are time problems with the data being collected

Make sure that the Data Capture Station PCs are set to military time (see your computer

documentation) and that the reader’s internal time clock is accurate. This can be going

to Menu>Setting>Date & Time.

37

Appendix G: RFID Vendor Research Company Country Price Details Response when contacted Mode of communication

ActiveWave USA

$3000 for two types of active tag, Reader/Programmer, Field Generator/Motion Sensor, 9 tags, Complete Access control/Tracking software with limited data base

Could not lower price Phone

Alien Technology USA

For the 915mhz kit, Tags are around $75.00 for 100 tags, with the reader with antenna for $3800. Antennas (circular and linear polarization) are around $450.

Could not lower price Phone

AVANTE International Technology, Inc. USA See attached price sheet

Worked a deal for 2 433mhz readers, 5 active tags, programming, and setup help for $1000, estimated original cost $7250

Phone/email

Crosspoint Netherlands See attached price sheet

We have the technology which can do what you’re looking for (we think) However the price of under $ 1.000,00 is not feasible with our technology. You would need to write some dedicated software to calculate the time difference of entering and the treatment room We offer an evaluation kit for € 1.500 ex works + SDK This contains 2 locators (one for entry and one for the treatment room) 1 receiver (but you probably need 2 pieces if the distance between entry en treatment room is larger then 30 meters apart)

email

Feig Electronic Germany 13.56 mhz Demo kit with reader, antenna, tags for $750 and can read up to 30 inches I'm afraid we cannot offer some

products which will meet your requirements below $US 1000.

email

38

Identec Solutions Austria Canada

Evaluation Kit 1: $1,400: 4 tags,915 MHz PCMCIA i-CARD II reader, Wave Antenna. Evaluation Kit 2: $1,800.00: 6 tags, 915 MHz PCMCIA i-CARD III reader, Wave Antenna There is a Software Development Kit for $1,200.00.

Could not lower price phone

IntenseComp Singapore

Offers a beginners kit with a desktop reader, tags, software and a range of ideas from controlling music according to the RFID badge being read, to paper document tracking. RFID Beginner's Kit at US$438.00. Shipping to baltimore be around US$50 to US$60.00. I

The range is short at about 15cm. To get 3-5 feet, we are not able to provide you one that can fit your budget.

email

Intersoft USA

A fully functional reader/decoder with antenna in an attractive desktop enclosure passive read-only RFID tags (sampling of our tags, 9 in all) a 9V battery adapter for hand-held applications simple software with source code examples.

Read range was too small phone

Matrics USA

Matrics sell a development kit that includes a 915MHz stationary reader, general purpose antenna, cables, reader to USB interface and 10 EPC tags for $3550. The reader with interface board sells for around $3200. Matrics tags are priced at just over $1 each in quantities of 250 at present. Antenna for Matrics readers are available at around $400. They are 30 in. x 12 in. x 1.5 in. - not a small device! Matrics also sells a Visibility Manager software package to handle the interfaces with the readers and the initial analysis of the data for $3200.

Too expensive, can not get a deal phone

Phidgets USA USA $80 reader kit Read range was too small phone

RFID Inc. USA

125KHz development kit with reader, antenna, power supply, program and tag samples for $499. 13.56MHz kit with the same items also for $499. 433mhz kit much more expensive

Worked a deal for $599 for two readers(433mhz), and 6 tags, see attached for more details

email/phone

39

RightTag US Kit is $499 and 100 tags are available for $149

Read range was too small, but was very helpul;learned 13.56mhz was standard frequency for passive tags in hospitals

phone

SkyeTek USA Trial kit available at $750 includes a portable reader, 10 tags and software.

Thank you for your message, however I regret to inform you that due to limited resources and strategic prioritization, SkyeTek is not currently able to support educational customers directly.

email/phone

Socket Communications USA

"The kit includes both hardware and software and comes in two versions. The first has a single-function CompactFlash RFID reader plug-in card priced at $1,995. The second includes a multifunction CompactFlash RFID reader plus a laser bar code scanner for $2,495"

No response email

SONMicro Turkey

"It is both a Development Kit for ChipModule and RFID Programmer. User can create his own specific application with this kit.User can either connect to PC or any other peripheral device(e.g microcontroller).Develoment Kit provides Input/Output for ChipModule as well as other features of ChipModules( e.g provides Uart pins) and PC connectivity. The kit is only $66!"

Unfortunately our products are not proper for this kind of project.You should take a look at "Active Tags" for this project.

email

Tagsys UK

"They have a range of RFID Kits on their site from a basic kit through to an advance RFID Kit. The difference appears to be the reader - the advanced kit has a tabletop reader, tags, software - the expert system has a long range reader and two antennas, the basic kit having a less powerful portable reader. The prices appear to be around 500 Euros for each kit."

No response email

Texas Instruments USA Many different options No response, always busy line phone/email

ThingMagic USA $4000 for kit, multi-protocol reader with a linear polarized antenna, power supply and sample EPC and ISO tags, for $4000.

Could not lower price phone

40

Trolley Scan (pty) Ltd. South Africa

"Sell a small and a medium system. The small system costs 1700 Euros and contains reader system, power amplifier, antennas, 100 tags. The medium system cost 4200 Euros and contains the same hardware but includes 1000 tags."

No response email

GAOEngineering Canada $1450 for UHF Reader (902-928 MHz, read up to 8m), $180 for UHF antenna, $70 for 35 passive tags

Willing to lower the total price ($1700) to $1200 phone/email

Intermec USA

Reader without computer inside: 865, 869, 915, 950 MHz, reading range depends on the operating environment, can use 4 antennas; reader with computer inside: operating in Linux environment, 869, 915, 950 MHz, reading range depens on the operating wnvironment, can use 4 antennas

approximate price is $2100 for one reader, could not lower price phone

Appendix H: Testing Day 1 Testing in Clark Manual log of patient activity

Title Tag # Time Situation Patient 598 3:38:30 Registers Patient 461 3:38:33 Registers Patient 461 3:41:37 comes in Doctor 1027 3:41:43 comes in Doctor 1027 3:45:13 leaves Patient 461 3:46:34 leaves Doctor 1024 3:47:00 comes in (interference) Doctor 1024 3:47:51 leaves (interference) Patient 598 3:47:00 comes in

Doctor 1027 3:47:33 comes in (just left other room)

Patient 29 3:49:59 (interference) Doctor 1027 3:51:15 leaves Patient 598 3:52:36 leaves Doctor 1024 comes in and leaves Patient 598 3:53:35 Registers

Patient 598 3:54:35 comes in (new paient)

Doctor 1024 3:57:35 comes in Doctor 1024 4:01:06 leaves Patient 598 4:01:16 leaves

Wait time results produced by our program Registration time Treatment Time Waiting Time

3:38:30 3:47:33 0:09:03 3:38:33 3:41:43 0:03:10 3:53:35 3:57:35 0:04:00

41

Day 2 Clark Testing Manual log of patient activity Reader ID Tag ID Event Time 1 027 P1 Patient 1 Enters 2:25:37 255 029 P2 Patient Enters 2:27:33 1 461 D1 D1 Enters 2:30:20 255 598 D2 D2 Enters 2:31:49 1 461 D1 D1 Leaves 2:36:00 255 598 D2 D2 Leaves 2:37:10 1 027 P1 P1 leaves 2:37:10 255 029 P2 P2 Leaves 2:37:45

CASE 1: Doctor enters before patient

does

1 598 D2 D1enters b4 patient 2:38:41 1 598 D2 D1 leaves 2:39:16 1 024 P3 Patient Enters 2:40:18 Registration 027 P1 P1Added 2:40:34 Registration 029 P2 P2 Added 2:41:46 1 598 D2 D2 Enters 2:42:29 255 027 P1 P1 Enters 1 598 D2 D2 Enters 2:52:07 1 024 P3 P3 Leaves 2:53:40 1 027 P1 P1 enters 2:54:48 1 598 D2 D2 Enters 2:59:38 1 598 D2 D2 Leaves 3:04:15 1 027 P1 P1 Leaves 3:05:00 Registration 024 P3 P3 Added 3:06:53 CASE 2 Patient leaves and re-enters

1 029 P2 P2 Enters 3:07:20 1 029 P2 P2 Leaves 3:09: 33 255 029 P2 P2 Enters Again 3:13:36 255 029 P2 P2 leaves 3:14:00 255 029 P2 P2 enters 3:14:37 1 029 P2 P2 Interferes in R1 3:16:20 255 598 D2 D2 Enters 3:18:29 Registration 027 P1 P1 Added 3:20:50 255 598 D2 D2 leaves 3:23:45

42

255 029 P2 P2 leaves 3:24:00 Registration 029 P2 P2 Added 3:24:38 Patient 3 was Registered @ 3:06:53 and was not seen, so the tag was re-registered again.

Registration 024 P3 P3 Added 3:26:08 255 027 P1 P1 Enters 3:28:19 Wrong Doctors walks by 255

255 461 D1 D1 Passes by 3:29:57- 3:30:38 1 029 P2 P2 Enters 3:31:27 1 461 D1 D1 Enters 3:32:29 255 598 D2 D2 Enters 3:33:50 255 598 D2 D2 Leaves 3:39:19 1 461 D1 D1 Leaves 3:39:57 1 029 P2 P2 Leaves 3:40:40 255 027 P1 P1 Leaves 3:41:01 255 024 P3 P3 Enters 3:43:40 255 461 D1 D1 Enters 3:46:33 255 598 D2 D2 Enters 3:46:33 255 461 D1 D1 Leaves 3:50:10 255 598 D2 D2 Leaves 3:50:10 255 024 P3 P3 Leaves 3:51:48 Registration(999) 024 P3 P3 Added 3:56:06 Registration(999) 027 P1 P1 Added 3:55:41 Registration(999) 029 P2 P2 Added 3:55:44 1 027 P1 P1 Enters 4:19:56 255 029 P2 P2 Enters 4:21:36 1 598 D2 D2 Enters 4:24:38 1 461 D1 D1 Interference 4:25:57 255 461 D1 D1 Enters 4:29:48 1 598 D2 D2 Leaves 4:32:56 1 027 P1 P1 Leaves 4:35:40

43

255 461 D1 D1 Leaves 4:36:16 255 029 P2 P2 Leaves 4:37:00 Registration(999) 024 P3 P3 Registers

(earlier, P3 never seen)

4:39:00

Registration(999) 027 P1 P1 Registers 4:39:09 Registration(999) 029 P2 P2 Registers 4:39:23 1 029 P2 P2 Enters 4:54.25 255 027 P1 P1 Enters 4:55:50 1 461 D1 D1 interfereces,

just passes by 4:59:52

1 598 D2 D2 Enters 5:03:24 255 461 D1 D1 Enters 5:04:23 1 598 D2 D2 Leaves 5:12:23 1 029 P2 P2 Leaves 5:14:07 255 461 D1 D1 Leaves 5:15:31 255 027 P1 P1 Leaves 5:15:31 1 598 D2 D2 Enters 5:18:08 1 024 P3 P3 enters(patient

comes in after doctor)

5:19:18

1 598 D2 D2 Leaves 5:24:11 1 024 P3 P3 Leaves 5:24:04

1 598 D2 D2 Enters (Interference) + Leaves

5:29:45

255 461 D1 D1 Enters (interference) +

5:33:06

255 461 D1 D1 Leaves 5:33:39 999 027 P1 P1 Registers 5:34:46 999 029 P2 P2 Registers 5:34:53 999 024 P3 P3 Registers 5:34:57 1 024 P3 P3 Enters 5:42:17 1 598 D2 D2 Enters/Leaves 5:45:31 1 598 D2 D2 Enters 5:48:11 255 027 P1 P1 Enters 5:48:43

44

255 461 D1 D1 Enters/Leaves 5:50:54 255 461 D1 D1 Enters 5:52:04 255 461 D1 D1 Leaves 6:05:23 1 461 D1 D1 Enters 6:05:54 1 598 D2 D2 Leaves 6:06:20 255 027 P1 P1 Leaves 6:07:06 1 024 P3 P3 Leaves 6:07:35 255 029 P2 P2 Enters 6:09:16 1 029 P2 P2 Leaves 6:12:45 1 029 P2 P2 Enters 6:14:07 1 598 D2 D2 Enters 6:15:45 1 461 D1 D1 Enters 6:20:38 1 461 D1 D1 Leaves 6:21:00 255 027 P1 P1 Enters 6:24:54 255 461 D1 D1 Enters 6:33:49 1 029 P2 P2 Leaves 6:41:08 1 598 D2 D2 Leaves 6:41:10 255 027 P1 P1 Leaves 6:42:15 255 461 D1 D1 Leaves 6:42:15 999 027 P1 P1 Registers 6:46:36 999 029 P2 P2 Registers 6:46:40 999 024 P3 P3 Registers 6:46:44 1 027 P1 P1 Walks by 7:29:22 1 024 P3 P3 Enters 7:29:21 1 027 P1 P1 Leaves 7:29:33 255 027 P1 P1 Enters 7:30:35 1 461 D1 D1 Enters 7:32:53 255 598 D2 D2 Enters 7:33:52 1 024 P3 P3 Leaves 7:54:17 1 461 D1 D1 Leaves 7:54:20 255 598 D2 D2 Leaves 7:56:23 255 027 P1 P1 Leaves 7:56:34 1 029 P2 P2 Enters 7:59:23 999 024 P3 P3 Registers 7:59:50 1 598 D2 D2 Enters 8:02:02 999 027 P1 P1 Registers 8:06:38 1 598 D2 D2 Leaves 8:07:44 1 029 P2 P2 Leaves 8:07:49 999 029 P2 P2 Registers 8:10:43 1 027 P1 P1 Enter 8:21:27 255 024 P3 P3 Enters 8:22:29 255 598 D2 D2 Enters 8:28:03

45

1 Signal lost 8:21:27 1 Signal Returns 8:31:34 1 461 D1 D1 Enters 8:32:37 1 027 P1 P1 Leaves 8:42:02 1 461 D1 D1 Leaves 8:42:09 255 024 P3 P3 Leaves 8:46:34 255 598 D2 D2 Leaves 8:46:40 255 029 P2 P2 Enters 8:53:44 255 461 D1 D1 Enters 8:58:01 255 029 P2 P2 Leaves 9:08:55 255 461 D1 D2 Leaves 9:08:56 999 027 P1 P1 Registers 9:10:41 999 029 P2 P2 Registers 9:10:46 999 024 P3 P3 Registers 9:10:51 255 027 P1 P1 Enters 9:48:15 255 598 D2 D2 Enters 9:49:17 255 027 P1 P1 Leaves 9:57:32 255 598 D2 D2 Leaves 9:57:32 255 029 P2 P2 Enters 9:59:14 255 461 D1 D1 Enters 10:03:02 255 029 P2 P2 Leave 10:15:03 255 461 D1 D1 Leave 10:15:03 255 024 P3 P3 Enters 10:22:52 255 461 D1 D1 Enters 10:32:10 255 024 P3 P3 Leaves 10:45:33 255 461 D1 D1 Leaves 10:45:33

Wait time results produced by our program Registration Time Treatment Time Waiting Time

4/16/2006 14:22:37 4/16/2006 14:30:20 00:07 4/16/2006 14:22:47 4/16/2006 14:31:49 00:09 4/16/2006 14:22:52 4/16/2006 14:40:18 00:17 4/16/2006 14:40:34 4/16/2006 14:54:31 00:13 4/16/2006 14:41:46 4/16/2006 15:18:07 00:36 4/16/2006 15:06:53 not yet treated not treated 4/16/2006 15:20:50 4/16/2006 15:28:19 00:07 4/16/2006 15:24:38 4/16/2006 15:32:29 00:07 4/16/2006 15:26:08 4/16/2006 15:43:40 00:17 4/16/2006 15:55:41 4/16/2006 16:24:38 00:28

46

4/16/2006 15:55:44 4/16/2006 16:29:26 00:33 4/16/2006 15:56:06 not yet treated not treated 4/16/2006 16:38:58 not yet treated not treated 4/16/2006 16:39:00 4/16/2006 17:19:18 00:40 4/16/2006 16:39:09 4/16/2006 17:04:12 00:25 4/16/2006 16:39:23 4/16/2006 16:54:25 00:15 4/16/2006 17:34:46 4/16/2006 17:48:43 00:13 4/16/2006 17:34:53 4/16/2006 18:09:16 00:34 4/16/2006 17:34:57 4/16/2006 17:42:17 00:07 4/16/2006 18:46:36 4/16/2006 19:33:52 00:47 4/16/2006 18:46:40 4/16/2006 20:02:02 01:15 4/16/2006 18:46:44 4/16/2006 19:32:53 00:46 4/16/2006 19:59:50 4/16/2006 20:28:03 00:28 4/16/2006 20:06:38 4/16/2006 20:21:27 00:14 4/16/2006 20:10:43 4/16/2006 20:58:01 00:47 4/16/2006 21:10:41 4/16/2006 21:48:15 00:37 4/16/2006 21:10:46 4/16/2006 21:59:14 00:48 4/16/2006 21:10:51 4/16/2006 22:22:52 01:12

Emergency Department Testing Manual log of patient activity Activity Time P1 Registered 07:09:47P2 Registered 07:10:55dr 1 visited patient 2 07:18:39dr 2 visit patient 1 08:16:34P3 Registered 07:19:04P1 Registered 07:21:25P2 Registered 07:21:29dr visits patient 3 07:21:14dr visits patient 2 07:27:53dr visits patient 1 08:23:33P1 Registered 07:30:18P2 Registered 07:30:30P3 Registered 07:30:35P3 untreated P3 Registered 07:32:10dr visits patient 1 07:33:36dr visits patient 2 08:31:19dr visits patient 3 07:35:58P1 Registered 07:36:04

47

P2 Registered 07:36:08 Wait time results produced by our program Registration Time Treatment Time Waiting Time

4/26/2006 07:09:47 4/26/2006 07:18:48 00:094/26/2006 07:10:55 4/26/2006 08:16:34 01:054/26/2006 07:19:04 4/26/2006 07:21:14 00:024/26/2006 07:21:25 4/26/2006 08:23:33 01:024/26/2006 07:21:29 4/26/2006 07:26:44 00:054/26/2006 07:30:18 4/26/2006 07:31:07 00:004/26/2006 07:30:30 4/26/2006 08:31:19 01:004/26/2006 07:30:35 not yet treated not treated 4/26/2006 07:32:10 4/26/2006 07:35:58 00:034/26/2006 07:36:04 not yet treated not treated 4/26/2006 07:36:08 not yet treated not treated

48

Appendix I: Program Code /** BrowserControl.java */ import java.io.IOException; public class BrowserControl { /** * Display a file in the system browser. If you want to display a * file, you must include the absolute path name. * * @param url the file's url (the url must start with either "http://" or * "file://"). */ public static void displayURL(String url) { boolean windows = isWindowsPlatform(); String cmd = null; try { if (windows) { // cmd = 'rundll32 url.dll,FileProtocolHandler http://...' cmd = WIN_PATH + " " + WIN_FLAG + " " + url; Process p = Runtime.getRuntime().exec(cmd); } else { // Under Unix, Netscape has to be running for the "-remote" // command to work. So, we try sending the command and // check for an exit value. If the exit command is 0, // it worked, otherwise we need to start the browser. // cmd = 'netscape -remote openURL(http://www.javaworld.com)' cmd = UNIX_PATH + " " + UNIX_FLAG + "(" + url + ")"; Process p = Runtime.getRuntime().exec(cmd); try { // wait for exit code -- if it's 0, command worked, // otherwise we need to start the browser up. int exitCode = p.waitFor(); if (exitCode != 0) { // Command failed, start up the browser // cmd = 'netscape http://www.javaworld.com' cmd = UNIX_PATH + " " + url; p = Runtime.getRuntime().exec(cmd); } } catch(InterruptedException x) { System.err.println("Error bringing up browser, cmd='" + cmd + "'");

49

System.err.println("Caught: " + x); } } } catch(IOException x) { // couldn't exec browser System.err.println("Could not invoke browser, command=" + cmd); System.err.println("Caught: " + x); } } /** * Try to determine whether this application is running under Windows * or some other platform by examing the "os.name" property. * * @return true if this application is running under a Windows OS */ public static boolean isWindowsPlatform() { String os = System.getProperty("os.name"); if ( os != null && os.startsWith(WIN_ID)) return true; else return false; } // Used to identify the windows platform. private static final String WIN_ID = "Windows"; // The default system browser under windows. private static final String WIN_PATH = "rundll32"; // The flag to display a url. private static final String WIN_FLAG = "url.dll,FileProtocolHandler"; // The default browser under unix. private static final String UNIX_PATH = "netscape"; // The flag to display a url. private static final String UNIX_FLAG = "-remote openURL"; } /** CallPyton.java */ /* Write a Java class which calls the python script, the class takes in the data range, data.txt location, and bin size, and calls the python script with these prams. */ import java.io.*; public class CallPython { public void callPython(double dRange, String dLocation, double binSize, String start, String end, int untreated) throws IOException { Runtime runtime = Runtime.getRuntime();

50

double modulo = binSize; Process reportmaker = runtime.exec("python "+dLocation+" "+modulo+" "+start+" "+end+" " + untreated); } } public class Doctor extends Person { public Doctor( String tagID ) { super( tagID ); } public String toString() { String s = "Doctor " + tagID + "\n"; for ( Visit v : visits ) { s += v + "\n"; } return s; } } /** Patient.java */ import java.sql.Timestamp; public class Patient extends Person { Timestamp time_registration = null; Timestamp time_treatmentStart = null; public Patient( String tagID, Timestamp registration ) { super( tagID ); this.time_registration = registration; } public Patient( Patient p ) { super( p.tagID ); this.time_registration = p.time_registration; this.time_treatmentStart = p.time_treatmentStart; } public String toString() { String s = "Patient " + tagID + ": "+ time_registration + "\n"; for ( Visit v : visits ) { s += v + "\n"; } if ( time_treatmentStart == null ) s += "not treated yet\n"; else s += "treated at " + time_treatmentStart + "\n"; return s; } public void getTreatmentTime( Iterable<Doctor> doctors ) { for ( Doctor d : doctors ) {

51

Timestamp time = this.firstEncounter( d ); if ( time != null ) { System.out.println( this.tagID + " and " + d.tagID + " met at " + time.toLocaleString() ); if ( this.time_treatmentStart == null ) { this.time_treatmentStart = time; } else { if ( this.time_treatmentStart.after( time ) ) this.time_treatmentStart = time; } } } } } /** Person.java */ import java.util.ArrayList; import java.util.Date; import java.util.LinkedList; import java.sql.Timestamp; public class Person { String tagID; ArrayList<Visit> visits; public Person() {} public Person( String tagID ) { this.tagID = tagID; visits = new ArrayList<Visit>(); } public void addVisit( String location, Timestamp entry, Timestamp exit ) { // first visit if ( visits.isEmpty() ) { visits.add( new Visit( location, entry, exit ) ); return; } int k = visits.size() - 1; // last index // new location. check interference at old location if ( !visits.get( visits.size() - 1 ).location.equals( location ) ) { if ( timeDiff( visits.get( k ).exit, visits.get( k ).entry ) <= 20 ) { visits.remove( k ); } visits.add( new Visit( location, entry, exit ) ); return; } else if ( timeDiff( visits.get( k ).exit, entry ) > 25 ) { if ( timeDiff( visits.get( k ).entry, visits.get( k ).exit ) <= 20 ) { visits.remove( visits.size() - 1 );

52

visits.add( new Visit( location, entry, exit ) ); return; } } else { visits.set( visits.size() - 1, new Visit( location, visits.get( visits.size() - 1 ).entry, exit ) ); return; } visits.add( new Visit( location, entry, exit ) ); } public long timeDiff( Timestamp time1, Timestamp time2 ) { return (time1.getTime() - time2.getTime())/1000; } public void deleteInterference() { int k = 0; while( k < visits.size() ) { if ( timeDiff( visits.get(k).exit, visits.get(k).entry ) <= 20 ) { visits.remove( k ); } else k++; } } // returns the time at which two people first meet public Timestamp firstEncounter( Person p ) { if ( this.visits.isEmpty() || p.visits.isEmpty() ) return null; for ( Visit v2 : p.visits ) { if ( visits.get(0).entry.before( v2.exit ) ) { for ( Visit v1 : visits ) { Timestamp t = firstEncounter( v1, v2 ); if ( t != null ) return t; } } } return null; } // returns the time at which two visits overlap for the first time public Timestamp firstEncounter( Visit v1, Visit v2 ) { if ( !v1.location.equals( v2.location ) ) return null; if ( v1.entry.after( v2.entry ) && v1.entry.before( v2.exit ) ) { return v1.entry; } else if ( v2.entry.after( v1.entry ) && v2.entry.before( v1.exit) ) { return v2.entry;

53

} else if ( v1.entry.equals( v2.entry ) ) { return v1.entry; } else return null; } } /** MainWindow.java. */ import javax.swing.*; import java.awt.*; import java.awt.event.*; public class MainWindow extends JPanel { private static final long serialVersionUID = 1L; private JComboBox tagID; private JTabbedPane tabbedPane; private PatientTableModel tableModel; private SQL sql; public MainWindow() { //this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); tabbedPane = new JTabbedPane(); tabbedPane.addTab("Main", makeMainTab()); // add main tab tabbedPane.addTab("About", makeAboutTab()); // add about tab setLayout(new GridLayout(1, 1)); add( tabbedPane ); sql = new SQL( tableModel ); } // make the main tab which contains add patient panel and statistics panel protected JPanel makeMainTab() { JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS)); panel.add( makeAddPatientPanel() ); panel.add( makeStatisticsPanel() ); panel.add( makeAnalysisPanel() ); //panel.add( makeReportPanel() ); return panel; } // make about tab protected JPanel makeAboutTab() {

54

JPanel panel = new JPanel(false); //panel.add(tabbedPane); JLabel filler = new JLabel(); filler.setHorizontalAlignment(JLabel.CENTER); panel.setLayout(new GridLayout(1, 1)); filler.setIcon(new ImageIcon("logo.gif")); panel.add(filler); return panel; } // make add patient panel which contains tagID drop down menu and add patient button private JPanel makeAddPatientPanel() { JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createEmptyBorder(0,0,0,8), BorderFactory.createTitledBorder("Add Patient"))); //panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS)); panel.setLayout(new FlowLayout() ); // tag id drop down menu tagID = new JComboBox(); tagID.addItem("1"); tagID.addItem("2"); tagID.addItem("3"); tagID.setPreferredSize( new Dimension( 100, 25 ) ); panel.add(tagID); // add patient button JButton addPatientButton = new JButton("Add Patient"); addPatientButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { try { sql.addPatient( (String) SQL.PATIENT[ Integer.parseInt((String)tagID.getSelectedItem()) - 1 ] ); } catch (Exception error) { error.printStackTrace(); } //tableModel.newPatient(); } }); panel.add( addPatientButton ); return panel; } // make statistics panel which contains a table private JPanel makeStatisticsPanel() { JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createCompoundBorder(

55

BorderFactory.createEmptyBorder(0,0,0,8), BorderFactory.createTitledBorder("Statistics"))); panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS)); panel.add( makeTable() ); panel.setPreferredSize( new Dimension(800,2000)); return panel; } // make table for statistics private Component makeTable() { tableModel = new PatientTableModel(); TableSorter sorter = new TableSorter(tableModel); JTable table = new JTable(sorter); sorter.setTableHeader(table.getTableHeader()); //ADDED THIS table.setPreferredScrollableViewportSize(new Dimension(240, 100)); JScrollPane comm_scrollPane = new JScrollPane(table); return comm_scrollPane; } // make analysis panel protected JPanel makeAnalysisPanel() { JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createEmptyBorder(0,0,0,8), BorderFactory.createTitledBorder("Analysis"))); // Analyze button JButton button = new JButton( "Analyze" ); button.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { try { sql.analyze(); } catch (Exception e1) { e1.printStackTrace(); } } }); // Report button JButton reportButton = new JButton( "Report" ); reportButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { JFrame frame = new JFrame("Report"); JPanel pane = new JPanel(new BorderLayout()); frame.getContentPane().add(pane, BorderLayout.SOUTH);

56

frame.getContentPane().add(new ReportWindow( sql ), BorderLayout.CENTER); frame.setSize(420, 350); frame.setVisible(true); } }); panel.add( button ); panel.add( reportButton ); return panel; } // main function. makes frame public static void main(String[] args) { JFrame frame = new JFrame("Patient Tracking System"); JPanel pane = new JPanel(new BorderLayout()); frame.getContentPane().add(pane, BorderLayout.SOUTH); frame.getContentPane().add(new MainWindow(), BorderLayout.CENTER); frame.setSize(800, 575); frame.setVisible(true); } } /** ReportWindow.java */ import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; import javax.swing.table.*; import java.awt.event.*; import java.io.File; import java.io.FilenameFilter; import java.util.Date; public class ReportWindow extends JPanel { private static final long serialVersionUID = 1L; private MyCalendar from; private MyCalendar to; private SQL sql; private ReportTableModel tableModel; private JTable table; public ReportWindow( SQL sql ) { this.sql = sql;

57

this.setLayout(new BoxLayout(this,BoxLayout.Y_AXIS)); this.add( makeCreateReportPanel() ); this.add( makeViewReportPanel() ); } private JPanel makeCreateReportPanel() { JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createEmptyBorder(0,0,0,8), BorderFactory.createTitledBorder("Create report"))); //panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS)); panel.setLayout(new FlowLayout() ); // simple calendars from = new MyCalendar( "from", true ); to = new MyCalendar( "to", false ); panel.add( from ); panel.add( to ); JButton createReport = new JButton( "Create Report" ); createReport.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { try { SQL.getData( from.toTimestamp(), to.toTimestamp() ); CallPython cp= new CallPython(); cp.callPython(1.0, "publish.py", 0.5, from.toString(), to.toString(), 1 ); System.out.println("Python call was completed."); String filename = "From_"+from.toString()+"_to_"+to.toString()+".html"; String curDir = System.getProperty("user.dir"); File dir = new File( curDir + "\\output" ); FilenameFilter filter = new FilenameFilter() { public boolean accept( File dir, String name ) { return name.endsWith( ".html" ); } }; String[] reports = dir.list( filter ); for ( String s : reports ) if( s.equals( filename ) ) { return; } System.out.println( "adding" + filename ); tableModel.addReport( filename, false ); tableModel.fireTableDataChanged(); } catch (Exception e1) { e1.printStackTrace(); } } }); panel.add( createReport ); return panel; }

58

private JPanel makeViewReportPanel() { JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createEmptyBorder(0,0,0,8), BorderFactory.createTitledBorder("View report"))); //panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS)); panel.setLayout(new FlowLayout() ); // get reports in \output directory String curDir = System.getProperty("user.dir"); File dir = new File( curDir + "\\output" ); FilenameFilter filter = new FilenameFilter() { public boolean accept( File dir, String name ) { return name.endsWith( ".html" ); } }; String[] reports = dir.list( filter ); panel.add( makeTable() ); return panel; } private class MouseListener extends MouseAdapter { public void mouseClicked( MouseEvent e ) { if ( e.getClickCount() == 2 ) { // double clicked on the row int row = table.getSelectedRow(); String from = (String) table.getValueAt( row, 0 ); String to = (String) table.getValueAt( row,1 ); String curDir = System.getProperty("user.dir"); curDir += "\\output\\" + "from_" + from + "_to_" + to + ".html"; System.out.println(curDir); //Calls the Open Browser Function BrowserControl.displayURL(curDir); } } } private Component makeTable() { tableModel = new ReportTableModel(); // get reports in \output directory updateTable(); TableSorter sorter = new TableSorter(tableModel); table = new JTable(sorter); sorter.setTableHeader(table.getTableHeader()); //ADDED THIS table.addMouseListener( new MouseListener() ); table.setPreferredScrollableViewportSize(new Dimension(240, 100)); JScrollPane comm_scrollPane = new JScrollPane(table); return comm_scrollPane;

59

} private void updateTable() { tableModel.clear(); String curDir = System.getProperty("user.dir"); File dir = new File( curDir + "\\output" ); FilenameFilter filter = new FilenameFilter() { public boolean accept( File dir, String name ) { return name.endsWith( ".html" ); } }; String[] reports = dir.list( filter ); for ( String s : reports ) tableModel.addReport( s, true ); } } /** MyCalendar.java */ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; import java.util.Date; import java.util.Calendar; import java.util.GregorianCalendar; import java.sql.Timestamp; import java.text.DecimalFormat; public class MyCalendar extends JPanel { private static final long serialVersionUID = 1L; private static final String[] MONTHS = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; private JComboBox year; private JComboBox month; private JComboBox day; private boolean isFrom; public MyCalendar( String name, boolean isFrom ) { this.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createEmptyBorder(0,0,0,8), BorderFactory.createTitledBorder( name ))); this.setLayout(new FlowLayout() ); Date d = new Date(); year = new JComboBox(); month = new JComboBox(); day = new JComboBox(); month.addActionListener( new Update( isFrom ) ); year.addActionListener( new Update( isFrom ) );

60

for( String s : MONTHS ) month.addItem( s ); for ( int i = 2005; i <= 2010; i++ ) year.addItem( i ); int max = getMaxDay(d.getYear(), d.getMonth()); for ( int i = 1; i <= max; i++ ) day.addItem(i); Calendar cal = new GregorianCalendar(); year.setSelectedItem( cal.get( Calendar.YEAR ) ); month.setSelectedIndex( cal.get( Calendar.MONTH ) ) ; this.add( month ); this.add( day ); this.add( year ); } public int getYear() { return (Integer) year.getSelectedItem(); } public int getMonth() { return month.getSelectedIndex() + 1; } public int getDay() { return (Integer) day.getSelectedItem(); } public void setDate( Date d ) { } public int getMaxDay( int year, int month ) { switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: return 31; case 4: case 6: case 9: case 11: return 30; case 2: if ( year % 4 != 0 ) return 28; if ( year % 400 == 0 ) return 29;

61

if ( year % 100 == 0 ) return 28; return 29; default: return 30; } } // Update action listener for year, day combo boxes private class Update implements ActionListener { boolean from; public Update( boolean isFrom ) { from = isFrom; } public void actionPerformed(ActionEvent arg0) { if ( year.getSelectedItem() == null || month.getSelectedItem() == null ) return; day.removeAllItems(); int max = getMaxDay( (Integer)year.getSelectedItem(), month.getSelectedIndex() + 1 ); for ( int i = 1; i <= max; i++ ) day.addItem( i ); if ( from ) day.setSelectedIndex( 0 ); else day.setSelectedIndex( max - 1 ); } } public Timestamp toTimestamp() { DecimalFormat df = new DecimalFormat(); df.applyPattern( "0000" ); String year = df.format( this.year.getSelectedItem() ); df.applyPattern( "00" ); String month = df.format( this.month.getSelectedIndex() + 1 ); String day = df.format( this.day.getSelectedIndex() + 1 ); String date = year + "-" + month + "-" + day; String time; if ( isFrom ) time = "00:00:00.0"; else time = "23:59:59.9"; return Timestamp.valueOf( date + " " + time ); } public String toString() { return MONTHS[ month.getSelectedIndex() ] + "-" + day.getSelectedItem() + "-" + year.getSelectedItem(); } public static void main(String[] args) {

62

JFrame frame = new JFrame("Calendar"); JPanel pane = new JPanel(new BorderLayout()); frame.getContentPane().add(pane, BorderLayout.SOUTH); frame.getContentPane().add(new MyCalendar("Calendar", true), BorderLayout.CENTER); frame.setSize(800, 575); frame.setVisible(true); } } /** PatientTableModel.java */ import javax.swing.table.AbstractTableModel; import java.sql.Timestamp; import java.util.LinkedList; public class PatientTableModel extends AbstractTableModel { private String[] columnNames = { "Registration time", "Treatment time", "Waiting time" }; private LinkedList<Object>[] data; public PatientTableModel() { data = new LinkedList[ getColumnCount() ]; for ( int i = 0; i < getColumnCount(); i++ ) { data[i] = new LinkedList<Object>(); } } public int getColumnCount() { return columnNames.length; } public int getRowCount() { return data[0].size(); } public String getColumnName( int col ) { return columnNames[col]; } public Object getValueAt( int row, int col) { return data[ col ].get( row ); } public boolean isCellEditable( int row, int col ) { return false; } public void setValueAt( Object value, int row, int col)

63

{ data[col].set( row, value ); } public Class getColumnClass(int c) { return getValueAt(0,c).getClass(); } public void setData( LinkedList<Object>[] data ) { this.data = data; } public void newPatient() { data[0].add( getRowCount() + 1 ); java.util.Date cur = new java.util.Date(); Timestamp current = new Timestamp(cur.getTime()); data[1].add( current.toLocaleString() ); // Wilfred: how to show time... this.fireTableDataChanged(); } public void addRow( Patient p ) { java.util.Date cur = new java.util.Date(); Timestamp current = new Timestamp(cur.getTime()); data[0].add( p.time_registration.toLocaleString() ); if ( p.time_treatmentStart != null ) { data[1].add( p.time_treatmentStart.toLocaleString() ); long time = p.timeDiff( p.time_treatmentStart, p.time_registration ); long min = time /60; data[2].add( min/60 + ":" + min % 60 ); } else { data[1].add( "not yet treated" ); long time = p.timeDiff( current, p.time_registration ); long min = time / 60; if ( min < 24 * 60 ) data[2].add( min/60 + ":" + min % 60 ); else data[2].add( "not treated" ); } } public void clear() { for ( int i = 0; i < data.length; i++ ) data[i].clear(); } }

64

/** ReportTableModel.java */ import java.util.LinkedList; import java.io.File; import javax.swing.table.AbstractTableModel; import java.util.Date; public class ReportTableModel extends AbstractTableModel { private String[] columnNames = { "From", "To", "Creation date" }; private LinkedList<Object>[] data; private static final String[] MONTHS = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; public ReportTableModel() { data = new LinkedList[ getColumnCount() ]; for ( int i = 0; i < getColumnCount(); i++ ) { data[i] = new LinkedList<Object>(); } } public int getColumnCount() { return columnNames.length; } public int getRowCount() { return data[0].size(); } public String getColumnName( int col ) { return columnNames[col]; } public Object getValueAt( int row, int col) { return data[ col ].get( row ); } public boolean isCellEditable( int row, int col ) { return false; } public void setValueAt( Object value, int row, int col) { data[col].set( row, value ); } public Class getColumnClass(int c) { return getValueAt(0,c).getClass(); }

65

public void setData( LinkedList<Object>[] data ) { this.data = data; } public void clear() { for ( int i = 0; i < data.length; i++ ) { data[i].clear(); } } /** * add the info of the file to the table * @param name file name in the format of mm-dd-yyyy_mm-dd-yyyy.html */ public void addReport( String name, boolean flag ) { File f = new File("output\\" + name ); System.out.println( f.getName() ); Date date_creation = new Date( f.lastModified() ); Date cur = new Date(); String[] s = name.substring( 0, name.length() - 5 ).split( "_" ); String from = s[1]; String to = s[3]; data[0].addFirst( from ); data[1].addFirst( to ); if( flag == true ) data[2].addFirst( date_creation ); else data[2].addFirst( cur ); } private Date toDate( String s ) { String[] t = s.split("-" ); int month = 1; for ( int i = 0; i < 12; i++ ) if ( t[0].equals( MONTHS[i] ) ) month = i+1; return new Date( month, Integer.parseInt(t[1]), Integer.parseInt(t[2]) ); } } /** SQL.java */ import java.sql.*; import java.util.LinkedList; import java.util.HashMap; import java.io.*; public class SQL { private Connection con = null; private HashMap<String,Doctor> doctors; private HashMap<String,Patient> cur_patients; // current patients

66

private LinkedList<Patient> old_patients; private PatientTableModel tableModel; public static final String[] DOCTOR = { "0020000461", "0020000598" }; // list of doctors' tags public static final String[] PATIENT = { "0020001027", "0020001029", "0020001024" }; // list of patients' tags public SQL( PatientTableModel model ) { tableModel = model; //con = this.getConnection(); doctors = new HashMap<String,Doctor>(); for ( int i = 0; i < DOCTOR.length; i++ ) doctors.put( DOCTOR[i], new Doctor( DOCTOR[i] ) ); cur_patients = new HashMap<String,Patient>(); old_patients = new LinkedList<Patient>(); } private Connection getConnection() { try { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); String connectionUrl = "jdbc:sqlserver://localhost\\SQLEXPRESS:1433;database=AccessTrakker;user=sa;password=password1"; con = DriverManager.getConnection(connectionUrl); if(con!=null) System.out.println("Connection Successful!"); } catch(Exception e){ e.printStackTrace(); System.out.println("\nError Trace in getConnection() : " + e.getMessage()); } return con; } // Display the driver properties, database details public void displayDbProperties() { DatabaseMetaData dm = null; ResultSet rs = null; try { con= this.getConnection(); if(con!=null) { dm = con.getMetaData(); System.out.println("Driver Information"); System.out.println("\tDriver Name: "+ dm.getDriverName()); System.out.println("\tDriver Version: "+ dm.getDriverVersion ());

67

System.out.println("\nDatabase Information "); System.out.println("\tDatabase Name: "+ dm.getDatabaseProductName()); System.out.println("\tDatabase Version: "+ dm.getDatabaseProductVersion()); System.out.println("Avalilable Catalogs "); rs = dm.getCatalogs(); while(rs.next()){ System.out.println("\tcatalog: "+ rs.getString(1)); } rs.close(); rs = null; con.close(); } else System.out.println("Error: No active Connection"); } catch(Exception e){ e.printStackTrace(); } dm=null; } // execute SQL statement public void executeStatement( String statement ) { try { con = this.getConnection(); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(statement); rs.close(); stmt.close(); con.close(); } catch (Exception e) {} } public void addPatient(String tagID) { try { java.util.Date cur = new java.util.Date(); Timestamp current = new Timestamp(cur.getTime()); executeStatement( "INSERT INTO dbo.Avante_RFID_CapturedTag values ( '999', '" + tagID + "', '" + current + "','" + current + "')" ); } catch (Exception e) {} } public void analyze() throws Exception {

68

con = this.getConnection(); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM dbo.Avante_RFID_CapturedTag ORDER BY SeqID"); System.out.println("Output result for query:"); cur_patients.clear(); old_patients.clear(); File file = new File( "current_patients.dat" ); if( file != null ) { try { String line; BufferedReader inFile = new BufferedReader( new FileReader( file ) ); System.out.println( "reading " + file.getName() ); line = inFile.readLine(); // tag id for(;;) { if ( line == null ) break; String tagID = line; line = inFile.readLine(); // registration System.out.println( line ); Timestamp registration = Timestamp.valueOf( line ); line = inFile.readLine(); if( line == null ) break; String[] s = line.split( " " ); Patient p = new Patient( tagID, registration ); if( s.length != 1 ) { // visits String reader = s[0]; Timestamp start = Timestamp.valueOf( s[1] + " " + s[2] ); Timestamp end = Timestamp.valueOf( s[3] + " " + s[4] ); p.addVisit( reader, start, end ); line = inFile.readLine(); } cur_patients.put( tagID, p ); } inFile.close(); } catch(IOException e ) { System.out.println( e.toString() ); } } file = new File( "current_doctors.dat" ); if ( file != null ) { try { String line; BufferedReader inFile = new BufferedReader( new FileReader( file ) ); System.out.println( "reading " + file.getName() ); line = inFile.readLine(); // tag id for(;;) { if ( line == null ) break;

69

String tagID = line; line = inFile.readLine(); // registration String[] s = line.split( " " ); Doctor d = new Doctor( tagID ); if( s.length != 1 ) { // visits String reader = s[0]; Timestamp start = Timestamp.valueOf( s[1] + " " + s[2] ); Timestamp end = Timestamp.valueOf( s[3] + " " + s[4] ); d.addVisit( reader, start, end ); line = inFile.readLine(); } doctors.put( tagID, d ); } inFile.close(); } catch(IOException e ) { System.out.println( e.toString() ); } } file = null; while (rs.next()) { String column2 = rs.getString(2); // reader id String column3 = rs.getString(3).trim(); // tag id Timestamp column4 = rs.getTimestamp(4); // start time Timestamp column5 = rs.getTimestamp(5); // end time if ( doctors.containsKey( column3 ) ) { // doctor found doctors.get( column3 ).addVisit( column2, column4, column5 ); } else { // patient found if ( column2.equals( "999" ) ) { //new patient System.out.println( "new patient: " + column3 + " at " + column4.toLocaleString() ); //patients.add( new Patient( column3, column4 ) ); if ( cur_patients.containsKey( column3 ) ) { // identical tagID exists Patient p = cur_patients.get( column3 ); p.getTreatmentTime( doctors.values() ); old_patients.add( p ); } Patient p = new Patient( column3, column4 ); cur_patients.put( column3, p ); } else { // registered patient Patient p = cur_patients.get( column3 ); if ( p != null ) { // ideally, p is never null p.addVisit( column2, column4, column5 ); } else { System.out.println( "not registered patient " + column3 ); } }

70

} } stmt.close(); rs.close(); // get treatment time for current patients for ( Patient p : cur_patients.values() ) { p.getTreatmentTime( doctors.values() ); } tableModel.clear(); for ( Patient p : cur_patients.values() ) { tableModel.addRow( p ); } for ( Patient p : old_patients ) { tableModel.addRow( p ); } tableModel.fireTableDataChanged(); // print old patients for ( Patient p : old_patients ) { System.out.println( p ); } // print current patients for ( Patient p : cur_patients.values() ) { System.out.println( p ); } // print current doctors for ( Doctor d : doctors.values() ) { System.out.println( d ); } // current patients PrintWriter out = new PrintWriter( new FileWriter( "current_patients.dat" ) ); for ( Patient p : cur_patients.values() ) { out.println( p.tagID ); out.println( p.time_registration ); if ( !p.visits.isEmpty() ) { Visit v = p.visits.get( p.visits.size() - 1 ); out.println( v.location + " " + v.entry.toString() + " " + v.exit.toString() ); } } out.close(); // doctors out = new PrintWriter( new FileWriter( "current_doctors.dat" ) ); for ( Doctor d : doctors.values() ) { out.println( d.tagID ); if ( !d.visits.isEmpty() ) {

71

Visit v = d.visits.get( d.visits.size() - 1 ); out.println( v.location + " " + v.entry.toString() + " " + v.exit.toString() ); } } out.close(); // graph input data out = new PrintWriter( new FileWriter( "input\\rawdata.txt", true ) ); int counter = 0; for ( Patient p : old_patients ) { counter++; if ( p.time_treatmentStart != null ) { long time = p.timeDiff( p.time_treatmentStart, p.time_registration ); double hour = (double) time /3600; //out.append( counter + "\t" + hour + "\t" + p.time_registration.toLocaleString() ); out.println( hour + "\t" + p.time_registration.toString() ); } else { //out.append( counter + "\t24" + "\t" + p.time_registration.toLocaleString() ); out.println( "24.0" + "\t" + p.time_registration.toString() ); } } out.close(); // uncomment this line //stmt.executeQuery("DELETE * FROM dbo.Avante_RFID_CapturedTag"); con.close(); } public static void getData( Timestamp from, Timestamp to ) throws Exception { String line; BufferedReader inFile = new BufferedReader( new FileReader( "input\\rawdata.txt" ) ); PrintWriter out = new PrintWriter( new FileWriter( "input\\data.txt" ) ); int counter = 0; while ( ( line = inFile.readLine() ) != null ) { String[] s = line.split( "\t" ); Timestamp t = Timestamp.valueOf( s[1] ); if ( ( t.after( from ) || t.equals( from ) ) && ( t.before( to ) || t.equals( to ) ) ) { counter++; out.println( counter + " \t " + s[0] ); } } inFile.close(); out.close(); } }

72

/** Visit.java */ import java.sql.Timestamp; import java.util.LinkedList; public class Visit { String location; Timestamp entry; Timestamp exit; public Visit( String location, Timestamp entry, Timestamp exit ) { this.location = location; this.entry = entry; this.exit = exit; } public String toString() { return location + ": " + entry + " ~ " + exit; } /* // returns null if doesn;t intersects public Visit intersection( Visit v1, Visit v2 ) { if ( v1.entry.after( v2.entry ) && v1.entry.before( v2.exit ) ) { } } */ /* // require: visit1, visit2 are in order public LinkedList<Visit> intersection( Iterable<Visit> visit1, Iterable<Visit> visit2 ) { for ( Visit v2 : visit2 ) { for ( Visit v1 : visit1 ) { } } } */ }

73

/** TableSorter.java */ import java.awt.*; import java.awt.event.*; import java.util.*; import java.util.List; import javax.swing.*; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.*; /** * TableSorter is a decorator for TableModels; adding sorting * functionality to a supplied TableModel. TableSorter does * not store or copy the data in its TableModel; instead it maintains * a map from the row indexes of the view to the row indexes of the * model. As requests are made of the sorter (like getValueAt(row, col)) * they are passed to the underlying model after the row numbers * have been translated via the internal mapping array. This way, * the TableSorter appears to hold another copy of the table * with the rows in a different order. * This is a long overdue rewrite of a class of the same name that * first appeared in the swing table demos in 1997. * * @author Philip Milne * @author Brendon McLean * @author Dan van Enckevort * @author Parwinder Sekhon * @version 2.0 02/27/04 */ public class TableSorter extends AbstractTableModel { protected TableModel tableModel; public static final int DESCENDING = -1; public static final int NOT_SORTED = 0; public static final int ASCENDING = 1; private static Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED); public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() { public int compare(Object o1, Object o2) { return ((Comparable) o1).compareTo(o2); } }; public static final Comparator LEXICAL_COMPARATOR = new Comparator() { public int compare(Object o1, Object o2) { return o1.toString().compareTo(o2.toString()); } }; private Row[] viewToModel; private int[] modelToView;

74

private JTableHeader tableHeader; private MouseListener mouseListener; private TableModelListener tableModelListener; private Map columnComparators = new HashMap(); private List sortingColumns = new ArrayList(); public TableSorter() { this.mouseListener = new MouseHandler(); this.tableModelListener = new TableModelHandler(); } public TableSorter(TableModel tableModel) { this(); setTableModel(tableModel); } public TableSorter(TableModel tableModel, JTableHeader tableHeader) { this(); setTableHeader(tableHeader); setTableModel(tableModel); } private void clearSortingState() { viewToModel = null; modelToView = null; } public TableModel getTableModel() { return tableModel; } public void setTableModel(TableModel tableModel) { if (this.tableModel != null) { this.tableModel.removeTableModelListener(tableModelListener); } this.tableModel = tableModel; if (this.tableModel != null) { this.tableModel.addTableModelListener(tableModelListener); } clearSortingState(); fireTableStructureChanged(); } public JTableHeader getTableHeader() { return tableHeader; } public void setTableHeader(JTableHeader tableHeader) { if (this.tableHeader != null) { this.tableHeader.removeMouseListener(mouseListener); TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer();

75

if (defaultRenderer instanceof SortableHeaderRenderer) { this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer); } } this.tableHeader = tableHeader; if (this.tableHeader != null) { this.tableHeader.addMouseListener(mouseListener); this.tableHeader.setDefaultRenderer( new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer())); } } public boolean isSorting() { return sortingColumns.size() != 0; } private Directive getDirective(int column) { for (int i = 0; i < sortingColumns.size(); i++) { Directive directive = (Directive)sortingColumns.get(i); if (directive.column == column) { return directive; } } return EMPTY_DIRECTIVE; } public int getSortingStatus(int column) { return getDirective(column).direction; } private void sortingStatusChanged() { clearSortingState(); fireTableDataChanged(); if (tableHeader != null) { tableHeader.repaint(); } } public void setSortingStatus(int column, int status) { Directive directive = getDirective(column); if (directive != EMPTY_DIRECTIVE) { sortingColumns.remove(directive); } if (status != NOT_SORTED) { sortingColumns.add(new Directive(column, status)); } sortingStatusChanged(); } protected Icon getHeaderRendererIcon(int column, int size) { Directive directive = getDirective(column); if (directive == EMPTY_DIRECTIVE) { return null;

76

} return new Arrow(directive.direction == DESCENDING, size, sortingColumns.indexOf(directive)); } private void cancelSorting() { sortingColumns.clear(); sortingStatusChanged(); } public void setColumnComparator(Class type, Comparator comparator) { if (comparator == null) { columnComparators.remove(type); } else { columnComparators.put(type, comparator); } } protected Comparator getComparator(int column) { Class columnType = tableModel.getColumnClass(column); Comparator comparator = (Comparator) columnComparators.get(columnType); if (comparator != null) { return comparator; } if (Comparable.class.isAssignableFrom(columnType)) { return COMPARABLE_COMAPRATOR; } return LEXICAL_COMPARATOR; } private Row[] getViewToModel() { if (viewToModel == null) { int tableModelRowCount = tableModel.getRowCount(); viewToModel = new Row[tableModelRowCount]; for (int row = 0; row < tableModelRowCount; row++) { viewToModel[row] = new Row(row); } if (isSorting()) { Arrays.sort(viewToModel); } } return viewToModel; } public int modelIndex(int viewIndex) { return getViewToModel()[viewIndex].modelIndex; } private int[] getModelToView() { if (modelToView == null) { int n = getViewToModel().length; modelToView = new int[n]; for (int i = 0; i < n; i++) {

77

modelToView[modelIndex(i)] = i; } } return modelToView; } // TableModel interface methods public int getRowCount() { return (tableModel == null) ? 0 : tableModel.getRowCount(); } public int getColumnCount() { return (tableModel == null) ? 0 : tableModel.getColumnCount(); } public String getColumnName(int column) { return tableModel.getColumnName(column); } public Class getColumnClass(int column) { return tableModel.getColumnClass(column); } public boolean isCellEditable(int row, int column) { return tableModel.isCellEditable(modelIndex(row), column); } public Object getValueAt(int row, int column) { return tableModel.getValueAt(modelIndex(row), column); } public void setValueAt(Object aValue, int row, int column) { tableModel.setValueAt(aValue, modelIndex(row), column); } // Helper classes private class Row implements Comparable { private int modelIndex; public Row(int index) { this.modelIndex = index; } public int compareTo(Object o) { int row1 = modelIndex; int row2 = ((Row) o).modelIndex; for (Iterator it = sortingColumns.iterator(); it.hasNext();) { Directive directive = (Directive) it.next(); int column = directive.column; Object o1 = tableModel.getValueAt(row1, column);

78

Object o2 = tableModel.getValueAt(row2, column); int comparison = 0; // Define null less than everything, except null. if (o1 == null && o2 == null) { comparison = 0; } else if (o1 == null) { comparison = -1; } else if (o2 == null) { comparison = 1; } else { comparison = getComparator(column).compare(o1, o2); } if (comparison != 0) { return directive.direction == DESCENDING ? -comparison : comparison; } } return 0; } } private class TableModelHandler implements TableModelListener { public void tableChanged(TableModelEvent e) { // If we're not sorting by anything, just pass the event along. if (!isSorting()) { clearSortingState(); fireTableChanged(e); return; } // If the table structure has changed, cancel the sorting; the // sorting columns may have been either moved or deleted from // the model. if (e.getFirstRow() == TableModelEvent.HEADER_ROW) { cancelSorting(); fireTableChanged(e); return; } // We can map a cell event through to the view without widening // when the following conditions apply: // // a) all the changes are on one row (e.getFirstRow() == e.getLastRow()) and, // b) all the changes are in one column (column != TableModelEvent.ALL_COLUMNS) and, // c) we are not sorting on that column (getSortingStatus(column) == NOT_SORTED) and, // d) a reverse lookup will not trigger a sort (modelToView != null) // // Note: INSERT and DELETE events fail this test as they have column == ALL_COLUMNS. // // The last check, for (modelToView != null) is to see if modelToView // is already allocated. If we don't do this check; sorting can become // a performance bottleneck for applications where cells // change rapidly in different parts of the table. If cells

79

// change alternately in the sorting column and then outside of // it this class can end up re-sorting on alternate cell updates - // which can be a performance problem for large tables. The last // clause avoids this problem. int column = e.getColumn(); if (e.getFirstRow() == e.getLastRow() && column != TableModelEvent.ALL_COLUMNS && getSortingStatus(column) == NOT_SORTED && modelToView != null) { int viewIndex = getModelToView()[e.getFirstRow()]; fireTableChanged(new TableModelEvent(TableSorter.this, viewIndex, viewIndex, column, e.getType())); return; } // Something has happened to the data that may have invalidated the row order. clearSortingState(); fireTableDataChanged(); return; } } private class MouseHandler extends MouseAdapter { public void mouseClicked(MouseEvent e) { JTableHeader h = (JTableHeader) e.getSource(); TableColumnModel columnModel = h.getColumnModel(); int viewColumn = columnModel.getColumnIndexAtX(e.getX()); int column = columnModel.getColumn(viewColumn).getModelIndex(); if (column != -1) { int status = getSortingStatus(column); if (!e.isControlDown()) { cancelSorting(); } // Cycle the sorting states through {NOT_SORTED, ASCENDING, DESCENDING} or // {NOT_SORTED, DESCENDING, ASCENDING} depending on whether shift is pressed. status = status + (e.isShiftDown() ? -1 : 1); status = (status + 4) % 3 - 1; // signed mod, returning {-1, 0, 1} setSortingStatus(column, status); } } } private static class Arrow implements Icon { private boolean descending; private int size; private int priority; public Arrow(boolean descending, int size, int priority) { this.descending = descending; this.size = size; this.priority = priority; }

80

public void paintIcon(Component c, Graphics g, int x, int y) { Color color = c == null ? Color.GRAY : c.getBackground(); // In a compound sort, make each succesive triangle 20% // smaller than the previous one. int dx = (int)(size/2*Math.pow(0.8, priority)); int dy = descending ? dx : -dx; // Align icon (roughly) with font baseline. y = y + 5*size/6 + (descending ? -dy : 0); int shift = descending ? 1 : -1; g.translate(x, y); // Right diagonal. g.setColor(color.darker()); g.drawLine(dx / 2, dy, 0, 0); g.drawLine(dx / 2, dy + shift, 0, shift); // Left diagonal. g.setColor(color.brighter()); g.drawLine(dx / 2, dy, dx, 0); g.drawLine(dx / 2, dy + shift, dx, shift); // Horizontal line. if (descending) { g.setColor(color.darker().darker()); } else { g.setColor(color.brighter().brighter()); } g.drawLine(dx, 0, 0, 0); g.setColor(color); g.translate(-x, -y); } public int getIconWidth() { return size; } public int getIconHeight() { return size; } } private class SortableHeaderRenderer implements TableCellRenderer { private TableCellRenderer tableCellRenderer; public SortableHeaderRenderer(TableCellRenderer tableCellRenderer) { this.tableCellRenderer = tableCellRenderer; } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,

81

int row, int column) { Component c = tableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (c instanceof JLabel) { JLabel l = (JLabel) c; l.setHorizontalTextPosition(JLabel.LEFT); int modelColumn = table.convertColumnIndexToModel(column); l.setIcon(getHeaderRendererIcon(modelColumn, l.getFont().getSize())); } return c; } } private static class Directive { private int column; private int direction; public Directive(int column, int direction) { this.column = column; this.direction = direction; } } }

82

Appendix J: Proposal

Emergency Department Patient Tracking System

A Project Proposal by Homewood Biomedical Design Associates

Johns Hopkins University

E-Team Members Dhondup Pemba, E-Team Leader Stephanie Keung, Business Leader

Annand Sharma, Programming Leader Brian Miller, Device Testing Leader

Kihyuk Hong, Hardware Leader Robert Dewan, Staff Engineer William Diplas, Staff Engineer

Worawan Limpitikul, Staff Engineer Jai Madhok, Staff Engineer

Sho-Yu Wang, Staff Engineer Brigitte Warner, Staff Engineer

Advisors Robert Allen, Ph.D., Principle Investigator

Dickson Cheung, M.D., Co-Principle Investigator Lawrence Aronhime, Business Advisor

83

ABSTRACT

To help reduce emergency-room overcrowding in our nation's hospitals, we propose to design, develop and test a patient tracking system that can help hospital administrators make managerial decisions. An inexpensive, portable tracking system is needed to passively but transparently collect data of individual patients’ activities. Various object-tracking devices and systems currently exist, from the satellite-based Global Positioning System, to Barcodes and Radio Frequency Identification. For our system, we chose to implement RFID tracking, due to its ability to provide meaningful information that can be accessed in a passive manner.

The key metrics that we obtain are the time intervals for the door to triage, the door to the doctor, the door to discharge. We also hope to get time intervals for other common procedures such as the time from the door to lab draw, door to radiology study, and etc. The specifications of our system will require it to be small, inexpensive, able to passively collect data, and capable of operating without interfering with external electrical signals. The system will consist of each patient being issued a unique RFID tag, and RFID readers, which will be placed at various locations in the Emergency Department, as well as given to the attending physicians. The data output would be downloadable to common statistical analysis programs such as Microsoft Excel. We will first test the system's performance at Johns Hopkins’ Homewood campus; once we have achieved a fully functional system, we will implement it at the Johns Hopkins Hospital Emergency Department. BACKGROUND The 2001 report "To Err is Human," by the Institute of Medicine describes the prevalence of widespread and often preventable medical errors throughout the U.S. healthcare industry; hospital Emergency Departments (ED) are no exception. One of the main sources of preventable medical error is ED overcrowding. The following definition of ED crowding was developed by the American College of Emergency Physicians (ACEP) task force: A situation in which the identified need for emergency services outstrips available resources in the ED. This situation occurs in hospital EDs when there are more patients than staffed ED treatment beds and wait times exceed a reasonable period. Crowding typically involves patients being monitored in nontreatment areas (eg, hallways) and awaiting ED treatment beds or inpatient beds. Crowding may also involve an inability to appropriately triage patients, with large numbers of patients in the ED waiting area of any triage assessment category [i]. Currently, EDs in the United States experience a serious problem with overcrowding. One study cites that over 90% of hospitals, both private and academic, experience problems with overcrowding and in recent years, this problem has only continued to grow. [ii] In 1992, there were approximately 6000 emergency room departments in hospitals

84

nationwide; by 2002, this number had decreased to only 4000. Meanwhile, the annual number of ED visits increased from 89.9 million to 108 million. [iii] Research has indicated that the explosive number of ED patients in the US is due to both an increasing proportion of seriously ill and injured patients as well as an ever-broadening definition of who qualifies for emergency care. One study reports that there has been a 59% increase in those ED cases classified as critical care during the 1990s [iv]. This strain created by this increasing number of patients and the decreasing number of EDs within the United States has caused massive problems with waiting times and overcrowding in the nation's emergency care system. Statistics from the ACEP reveal that visits for minor illness or injury may for last for 1-2 hours even when the ED is not crowded. Data collected in by the Center for Disease Control and Prevention indicates that patients spend three hours on average from arrival in the ED to discharge, and almost 400,000 patients nationwide have waited 24 hours or more before they were discharged. Johns Hopkins Hospital’s ED has reported average wait times of 30 minutes between entering the waiting room and evaluation at triage and an average of 6.5 hours wait time before seeing an attending physician. EDs in Tucson and Phoenix commonly report wait times of 6-8 hours [v]. Such extreme waiting times in the ED can often be detrimental to the health of patients. Excessive waiting has resulted in an additional problem with patients leaving the ED. On average, most patients will wait about 90 minutes before leaving the ED at various stages of treatment [vi] . Estimates from Johns Hopkins ED indicate that some 10% of patients check in and leave without ever having seen a doctor. Researchers hypothesize that a large majority of these patients leave due to extraordinarily long wait times after triage. However, it is not only the wait time before and after triage that influences a patient's decision to leave the ED; some patients may leave before being treated at all, while others may leave before the treatment is completed, often ignoring medical advice. Studies performed in Los Angeles and San Francisco, CA revealed that leaving the ED prematurely can lead to serious consequences. About 11% of patients that left the ED without being treated were re-admitted to the hospital within one week of their initial ED visit; these patients experienced more severe symptoms and were more likely to require emergency surgery due to delay in ED treatment[vii]. Obviously, ED overcrowding needs a solution; but the economics of running a hospital impose a number of serious limitations on steps that can be taken towards improvement. Thus, simply expanding the nation's existing EDs is not a valid solution to the crisis. Instead, hospitals must optimize efficiency in their use of resources. In order to determine the means to maximize efficiency, efficiency itself needs to be measured, and currently there is no effective way to measure hospital efficiency. Therefore, we propose to develop a data collection system that will measure a hospital’s productivity by tracking patient flow in the ED. Data from such a system would allow for the determination of bottlenecks in patient flow, revealing to where we should divert resources in order to maximize the efficiency and thus increase the overall quality of patient care. Many hospitals have attempted to implement some form of patient tracking for this purpose. Most hospitals however, do not have the resources to support a patient tracking system and they must instead hire temporary help to watch the patients and manually input data. This method often suffers from problems such as: (1) highly variable and often high cost, (2) human error due to manual data entry, and (3) increasing cost of data collection in larger hospitals due to need for greater number of people to collect data.

85

The cost of data collection can rise exponentially with each additional employee hired. Due to limited resources, hospitals will commonly only hire a few statisticians to collect data. Accordingly, data collection is limited to focusing on only a small sample of patients who are closely monitored, rather than an expansive group of patients, who, if monitored, could provide more accurate and extensive data. In effect, hospitals are forced to choose data accuracy over data completeness as the sample size must be very small in order to minimize cost. In attempts to modernize and automate this process, a few firms have applied modern technology to try and solve this problem. Technological Solutions Raytheon has implemented a bar code system to keep track of patient information while the patient is heading towards or is in a hospital. This system was designed primarily to ensure that patients are directed to different hospitals during emergencies such as hurricanes. According to a press release issued after Raytheon’s system went live in St. Louis, "the Emergency Patient Tracking System provides rapid communication of patient information from the casualty site by using wireless personal digital assistants connected to a central data base accessible over the Internet. This bar-code technology is off-the-shelf and similar to what UPS(TM) and FedEx(R) use – it provides the medical staff with casualty information while en route to ensure that any one hospital is not overwhelmed" [viii]. The purpose behind the development of this system was to deal with emergency care under special circumstances, not for day-to-day use, and as a result it lacks critical refinements, such as automatic time-logging. Recently, major retailers and shipping firms have upgraded from using barcodes to using Radio Frequency Identification (RFID). This system is capable of logging the time when the tagged object has passed through specific, scanner-equipped areas of interest; the tag may also contain and transmit relevant package information. Due to new HIPAA regulations, this aspect of RFID may be severely restricted out of privacy concerns; however, it is incredibly useful in keeping track of things in a completely passive manner. It is extremely feasible to use this technology in various industries, from shipping to health care. Some organizations such as Georgetown University Hospital, Tracemed and CPBEmergent have even created preliminary patient-tracking systems using this RFID-based system [ix]. Georgetown University Hospital and Tracemed have both created systems which immediately affect an individual hospital's efficiency and reliability by providing patient information in real time, letting doctors and nurses communicate pertinent patient information, and optimize an individual patient's safety. However, these systems were designed in such a way that it is not possible to collect data that can potentially affect long-term managerial decisions. CPBEmergent has created a system that involves applying active tags to each patient's file. When a patient is moved within the ED, nurses place the patient’s file into a bin located in the area where the patient was moved. This bin contains an RFID reader that will log the event time and location. Although this system is useful, a large majority of hospitals are looking to implement a completely passive system where the doctors and nurses are required to provide little or no interaction. The system created by CPBEmergent is an active system which adds yet another procedure that must be preformed within the often hectic ED and relies fully on the cooperation and effectiveness of the nurses in the ED.

86

RFID is a very powerful, simple, and fairly economical solution to tracking any tagged object. In the case of patient-flow, the object would be a patient, and the object will be tracked in the ED. However, the current solutions that implement RFID are not ideal for application to patient flow tracking, as they either are not designed to track large-scale patient flow, or require the nurses or doctors to manually participate in the tracking process. In a busy ED, doctors often do not have the time to incorporate any sort of a ‘logging’ procedure into their routine. We propose to create and implement a system of readers and economical passive tags to collect data in addition to software and a server to allow for easy data analysis.

Other Passive Solutions

An example of RFID in operation Taken from http://www.belacorp.com/images/RFID_diagram.jpg

Face-matching algorithms can be used in conjunction with the hospital's existing security cameras to passively track patient movement within the ED. Face matching was a technology once reserved for the ultra high-end security sector because of its calculation-intensive procedures. Due to the constantly-increasing power of computers, their utility has grown over the years. This technology could be adapted to track when a patient enters the hospital, where the patient goes, and the time intervals the patient spends waiting to see a caregiver. In applying this technology, the major cost involved would be purchase of the algorithm and a server side-database that identifies the individual on camera. The Global Positioning System (GPS) uses satellite triangulation to determine where a particular object is in relation to the rest of the world. These days GPS is being extensively used by many private and governmental bodies to keep track of school buses, aircrafts, automobiles, and missiles. Due to the increase in its popularity and widespread adoption over the past few years, GPS has grown more accurate and cost-effective; also, the miniaturization of GPS receivers have improved their accessibility. Conclusion Bar codes, RFID, face-matching and GPS are all technologies that could allow a hospital to benefit from electronic data collection. Even though bar code systems are widely used and their advantages have been well understood, the current environment necessitates that further

87

improvements be made. Face matching technology is a very innovative choice for patient tracking; however, firms have yet to apply this technology in hospitals. As there are many algorithms and methods that can be applied when using face-recognition systems, there are infinite possibilities for this means of patient tracking. On the downside not all hospitals have security cameras everywhere so this would call for the installation of a number of cameras, consequently increasing the cost involved. The major drawback of using this technology, beside its cost, is the technique’s existing in a stage of infancy. After weighing the pros and cons of each of the available options, the use of RFID’s for patient tracking appears to be the most practical approach. The system will be cost-effective and convenient and, with the exception of the initial tagging process, completely automated. MISSION To develop an inexpensive, portable and passive tracking system that requires little or no interaction from the user. SPECIFIC GOALS:

• Determine the most important parameters to be measured- i.e Door to Doctor time, Door to Discharge, and Door to Triage Time

• Build a completely Passive system- Requires no attention and actions from the operator

• Improve existing hardware to be able track patients in the ER 100% of the time • Develop software to track patients in the ER 100% of the time • Determine the project’s social and environmental impact. • Develop a business plan and overall strategy to bring the tracking system to the

market BUSINESS PLAN Target Market and Need Currently only a small percentage of hospitals have emergency department tracking systems. The majority of those that do use bar code technology. The clinical utility, cost advantages, and collateral benefits of bar coding will spur the growth of these technology solutions and industry-wide adoption is likely to occur within the next four to six years.1 However, our system will be able to go beyond the capabilities of barcodes and penetrate many more hospitals. All hospitals need some kind of an emergency tracking system in order to make useful managerial decisions concerning the EDs. The system will provide information that can help the EDs of hospitals in the following ways:

• Persuade the senior management of hospitals to better allocate the necessary resources which will help to increase efficiency.

• Calculate productivity ratios (care-giver time/ total time spent in ED) and interpret them to measure productivity and efficiency.

Competitive Advantage of Product

88

Our system will be less expensive than the bar code equivalent. The passive nature of the scanning system will do away with majority of the barcode system’s costs, since their costs include data management, servicing, and user training. Our proposed design is not an administrative system, but, due to its benefits in helping humans make informed administrative decisions, it would be fairly reasonable for many hospitals to consider it in their budget. Phases of Development

• Phase I: Designing of the prototype(February 2006 to April 2006) • Phase II: Refinement and testing of the product (April 2006 to May 2006) • Phase III: Implementation at Johns Hopkins Emergency Department and further

improvements, if necessary (May 2006- September 2006) • Phase IV: Outline corporate strategy and prepare business plan.(September 2006-

October 2006) • Phase V: Market the product and negotiate with manufactures for mass production

(October 2006-December 2006) • Phase VI: Entry into the market (Jan 2007- and on) • Phase VII: Normalize operations to bring in profit (Begin January 2008)

DEVELOPMENT STRATEGY:

In order to develop a passive tracking system capable of monitoring and quantifying large-scale patient flow in the ED, it will be necessary to first develop a simple prototype. This basic prototype must be able to collect relevant data from a single patient and communicate it to doctors in the ED. We plan to implement existing RFID technology to retrieve data from patients and will create unique software to analyze this. The software package will output this data in the form of easy-to-read reports, which will allow for rapid analysis of patient flow in the ED. Once appropriate hardware has been coupled with this software, we will apply for IRB approval. Pending approval from the IRB, we will begin preliminary testing of our prototype on human subjects, thereby confirming the capabilities of our system. After creation of a successful prototype with promising results, we will expand our system to track many patients and doctors. To be realistically implemented in the Johns Hopkins Hospital, the system must be capable of tracking at least sixteen rooms and two doctors. Expansion of our system to accommodate a greater number of variables can be easily achieved by simply manufacturing more hardware and making minor adjustments to the software. Our team’s upperclassman will be responsible for ordering materials and overall team coordination. We will have a business team responsible for assessing finances and marketing opportunities, as well as a dedicated software development group. The team leader and upperclassman will delegate other responsibilities as the project unfolds. Throughout the summer, a number of freshman and sophomores will continue to work to improve the capacities of the device. DEVELOPMENT TIMELINE:

Task Duration (hours)

Start Finish % Complete

Resource Leader

Resources

89

Research Competing Products/ Preliminary Patent Search

35 11/1/05 11/23/05 100 Dhonam Team 5

Primary Brainstorm Design

35 11/1/05 11/23/05 100 Dhonam Team 5

Project Proposal 35 11/23/05 12/1/05 100 Dhonam Team 5 Present Project Proposal

40 12/3/05 12/12/05 0 Dhonam Team 5

Order Materials 20 12/10/05 3/15/06 0 Stephanie Upperclassmen Assemble the Device (Hardware)

50 1/1/06 1/14/06 0 Dhonam Team 5

Prepare and Submit Experiment Design to Johns Hopkins IRB

50 1/3/06 2/28/06 0 Dhonam Team 5

Test the Hardware 50 1/15/06 1/31/06 0 Dhonam Team 5 Develop Software Package to read and output data

300 1/5/06 2/5/06 0 Brian Programming subgroup

Develop Business/ Market Plan

40 1/20/06 4/27/06 0 Annand Team 5, Business Mentor

Conceptual Design Presentation

30 2/1/06 2/13/06 0 Stephanie Team 5

Testing Software with Sample Data

150 2/5/06 2/20/06 0 Ki Team 5

Analyze Data 40 2/20/06 2/27/06 0 Ki Team 5 Brainstorm Data Output, Analyze Software

40 2/27/06 3/7/06 0 Ki Team 5

Preliminary Design Presentation

40 3/6/06 3/13/06 0 Stephanie Team 5

Brainstorm Improvements

20 3/7/06 3/11/06 0 Dhonam Team 5

Build Devices and Improve Software Code

200 3/13/06 4/8/06 0 Dhonam Team 5

Spring Break 3/20/06-3/26/06 Test and Calibrate Prototype

100 4/1/06 4/22/06 0 Dhonam Team 5

Final Improvements 150 4/15/06 5/8/06 0 Dhonam Team 5, Mentors

Final Report Presentation

50 5/1/06 5/16/06 0 Dhonam Team 5

Final Report 50 5/1/06 5/22/06 0 Dhonam Team 5 E-TEAM MEMBERS AND MENTORS

90

The E-Team is composed of eleven biomedical engineering students including a senior team leader, four upperclassmen, and six freshmen, all of whom will be enrolled in BME Design Group, a year-long course at the Johns Hopkins University. Additionally, the group members are all involved in Homewood Biomedical Design Associates (HBDA), a university-based corporation which serves to provide an industrial management structure and professional background with design project activities. The E-Team has already completed three previous HBDA design projects and performed exceptionally in each project.

Our E-Team will be divided into two subgroups, one which focuses on programming and the other on business aspects of our Patient Tracking System. The entire E-Team is to be lead and organized by senior Dhondup Pemba. He specializes in computer science and electrical engineering and has had previous experience designing devices in BME design group. He will be an invaluable resource as he has extensive knowledge and experience in programming in both academia and industry. Senior Annand Sharma will lead the programming subgroup which will analyze and code all necessary software for the Patient Tracking System. Annand specializes in computer science and has had extensive experience in both circuit analysis and programming. He has taken courses such as Medical Imaging Systems and Computer Integrated Surgery and has had long-term experience working in a vestibular occulomotor laboratory. Sophomores Kihyuk Hong and Brian Miller, as well as freshmen Jai Madhok, Bill Diplas, and Boombim Limpitikul will also make essential contributions to the programming team. Both Kihyuk and Brian have had extensive experience in electrical engineering and will provide insight into programming, circuits design, and use of various software packages. Jai, Boombim, and Bill are all knowledgeable and have had comprehensive experience in many programming languages including Java and C++. Senior Stephanie Keung has had previous experience in another JHU class designing a business plan and will use her experience to lead the business subgroup of our E-Team. The business subgroup will organize the product development process as well as develop a business plan for the engineered device. Freshmen Robert Dewan, Sandy Wang, and Brigitte Warner will contribute to the business subteam through their exhaustive knowledge of hospital activity and organization gained through various internships. All E-Team members will participate in preliminary brainstorming, research, design, and development of the Patient Tracking System prototype.

Principle investigator Harry Goldberg works to design and incorporate educational technology into the BME curriculum; he will be a source of valuable support and guidance throughout the design process. Co-Principle investigator Dr. Dickson Cheung works in the Emergency Department (ED) at Johns Hopkins Bayview Medical Center and has a strong interest and vast knowledge in current ED efficiency problems; his guidance, experience, and interest in ED improvement will be invaluable to the team. Project Advisor Dr. Robert Allen is the co-director for Biomedical Design Group course and will provide guidance and advice to the team during various stages of product development. Larry Aronhime will serve as our business advisor. He has an extensive background in engineering entrepreneurship and technology commercialization and will work closely with our team to bring our product to market.

AVAILABLE EQUIPMENT AND RESOURCES

91

• Johns Hopinks Academic Computing Lab- Includes computers with various software applications such as Visual Studio, Matlab, Microsoft office, and AutoCad.

• Capstone Design Lab-includes computers with Labview • Johns Hopkins Hospital- Testing Facility

Milton S. Eisenhower Library- Access to relevant publications and literature

92