SlideShare a Scribd company logo
1 of 94
Download to read offline
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.
0
5
10
15
20
25
30
35
40
45
50
0.0-1.01.0-2.02.0-3.03.0-4.04.0-5.05.0-6.07.0-8.08.0-9.09.0-10.010.0-11.011.0-12.012.0-13.013.0-14.014.0-15.015.0-16.016.0-17.017.0-18.018.0-19.019.0-20.0
Waiting time(hours)
NumberofPatients
0
10
20
30
40
50
60
0.0-1.01.0-2.02.0-3.03.0-4.04.0-5.05.0-6.07.0-8.08.0-9.09.0-10.010.0-11.011.0-12.012.0-13.013.0-14.014.0-15.015.0-16.016.0-17.017.0-18.018.0-19.019.0-20.0
Waiting time(hours)
NumberofPatients
0
10
20
30
40
50
60
0.0-1.01.0-2.02.0-3.03.0-4.04.0-5.05.0-6.07.0-8.08.0-9.09.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)
NumberofPatients
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:47
P2 Registered 07:10:55
dr 1 visited patient 2 07:18:39
dr 2 visit patient 1 08:16:34
P3 Registered 07:19:04
P1 Registered 07:21:25
P2 Registered 07:21:29
dr visits patient 3 07:21:14
dr visits patient 2 07:27:53
dr visits patient 1 08:23:33
P1 Registered 07:30:18
P2 Registered 07:30:30
P3 Registered 07:30:35
P3 untreated
P3 Registered 07:32:10
dr visits patient 1 07:33:36
dr visits patient 2 08:31:19
dr visits patient 3 07:35:58
P1 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:09
4/26/2006 07:10:55 4/26/2006 08:16:34 01:05
4/26/2006 07:19:04 4/26/2006 07:21:14 00:02
4/26/2006 07:21:25 4/26/2006 08:23:33 01:02
4/26/2006 07:21:29 4/26/2006 07:26:44 00:05
4/26/2006 07:30:18 4/26/2006 07:31:07 00:00
4/26/2006 07:30:30 4/26/2006 08:31:19 01:00
4/26/2006 07:30:35 not yet treated not treated
4/26/2006 07:32:10 4/26/2006 07:35:58 00:03
4/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 yetn";
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://localhostSQLEXPRESS: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( "inputrawdata.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( "inputrawdata.txt" ) );
PrintWriter out =
new PrintWriter( new FileWriter( "inputdata.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
RFID-Based Patient Tracking System Report Summary
RFID-Based Patient Tracking System Report Summary
RFID-Based Patient Tracking System Report Summary
RFID-Based Patient Tracking System Report Summary
RFID-Based Patient Tracking System Report Summary
RFID-Based Patient Tracking System Report Summary
RFID-Based Patient Tracking System Report Summary
RFID-Based Patient Tracking System Report Summary
RFID-Based Patient Tracking System Report Summary
RFID-Based Patient Tracking System Report Summary
RFID-Based Patient Tracking System Report Summary
RFID-Based Patient Tracking System Report Summary
RFID-Based Patient Tracking System Report Summary
RFID-Based Patient Tracking System Report Summary

More Related Content

What's hot

IRJET- Hiding Sensitive Medical Data using Encryption
IRJET- Hiding Sensitive Medical Data using EncryptionIRJET- Hiding Sensitive Medical Data using Encryption
IRJET- Hiding Sensitive Medical Data using EncryptionIRJET Journal
 
Trial x allscripts_submission
Trial x allscripts_submissionTrial x allscripts_submission
Trial x allscripts_submissionnycgwb
 
N P H L L I S Presentation
N P H L  L I S  PresentationN P H L  L I S  Presentation
N P H L L I S Presentationmarkd656
 
Clinical cube customer presentation30 04 14
Clinical cube customer presentation30 04 14Clinical cube customer presentation30 04 14
Clinical cube customer presentation30 04 14Clinical Cube Ltd
 
BOORSTEIN.tue_.4pm.Final_
BOORSTEIN.tue_.4pm.Final_BOORSTEIN.tue_.4pm.Final_
BOORSTEIN.tue_.4pm.Final_Hidee Cyd
 
Quality Assurance and Medico Legal Issues - Teleradiology Solutions
Quality Assurance and Medico Legal Issues - Teleradiology SolutionsQuality Assurance and Medico Legal Issues - Teleradiology Solutions
Quality Assurance and Medico Legal Issues - Teleradiology SolutionsMathew B R
 
E-Scribe: Centralized Electronic Healthcare Record Management System
E-Scribe: Centralized Electronic Healthcare Record Management SystemE-Scribe: Centralized Electronic Healthcare Record Management System
E-Scribe: Centralized Electronic Healthcare Record Management SystemNilabja GhoshChowdhury
 
WhatsApp Telemedicine For the Developing World: What Can We Learn From India ...
WhatsApp Telemedicine For the Developing World: What Can We Learn From India ...WhatsApp Telemedicine For the Developing World: What Can We Learn From India ...
WhatsApp Telemedicine For the Developing World: What Can We Learn From India ...Apollo Hospitals Group and ATNF
 
Theranos Original Pitchdeck 2006
Theranos Original Pitchdeck 2006Theranos Original Pitchdeck 2006
Theranos Original Pitchdeck 2006Renata George
 
IRJET- Smart ICU using IoT
IRJET- Smart ICU using IoTIRJET- Smart ICU using IoT
IRJET- Smart ICU using IoTIRJET Journal
 
Best Practices on Medical Coding in MedDRA
Best Practices on Medical Coding in MedDRABest Practices on Medical Coding in MedDRA
Best Practices on Medical Coding in MedDRAPerficient
 
Novel telemedicine
Novel telemedicineNovel telemedicine
Novel telemedicine3GDR
 
Starting with SNOMED CT and practical use of SNOMED CT
Starting with SNOMED CT and practical use of SNOMED CTStarting with SNOMED CT and practical use of SNOMED CT
Starting with SNOMED CT and practical use of SNOMED CTTHL
 
ACR Select: Clinical Decision Support Imaging Guidelines
ACR Select: Clinical Decision Support Imaging GuidelinesACR Select: Clinical Decision Support Imaging Guidelines
ACR Select: Clinical Decision Support Imaging GuidelinesACR Select
 
RFID Applications for Healthcare
RFID Applications for Healthcare RFID Applications for Healthcare
RFID Applications for Healthcare 栄治 村社
 

What's hot (20)

Philips Implementing Wireless in the Hospital Enterprise: Medical Device Cons...
Philips Implementing Wireless in the Hospital Enterprise: Medical Device Cons...Philips Implementing Wireless in the Hospital Enterprise: Medical Device Cons...
Philips Implementing Wireless in the Hospital Enterprise: Medical Device Cons...
 
IRJET- Hiding Sensitive Medical Data using Encryption
IRJET- Hiding Sensitive Medical Data using EncryptionIRJET- Hiding Sensitive Medical Data using Encryption
IRJET- Hiding Sensitive Medical Data using Encryption
 
Trial x allscripts_submission
Trial x allscripts_submissionTrial x allscripts_submission
Trial x allscripts_submission
 
N P H L L I S Presentation
N P H L  L I S  PresentationN P H L  L I S  Presentation
N P H L L I S Presentation
 
Clinical cube customer presentation30 04 14
Clinical cube customer presentation30 04 14Clinical cube customer presentation30 04 14
Clinical cube customer presentation30 04 14
 
BOORSTEIN.tue_.4pm.Final_
BOORSTEIN.tue_.4pm.Final_BOORSTEIN.tue_.4pm.Final_
BOORSTEIN.tue_.4pm.Final_
 
Quality Assurance and Medico Legal Issues - Teleradiology Solutions
Quality Assurance and Medico Legal Issues - Teleradiology SolutionsQuality Assurance and Medico Legal Issues - Teleradiology Solutions
Quality Assurance and Medico Legal Issues - Teleradiology Solutions
 
E-Scribe: Centralized Electronic Healthcare Record Management System
E-Scribe: Centralized Electronic Healthcare Record Management SystemE-Scribe: Centralized Electronic Healthcare Record Management System
E-Scribe: Centralized Electronic Healthcare Record Management System
 
E healthcare ppt
E healthcare pptE healthcare ppt
E healthcare ppt
 
WhatsApp Telemedicine For the Developing World: What Can We Learn From India ...
WhatsApp Telemedicine For the Developing World: What Can We Learn From India ...WhatsApp Telemedicine For the Developing World: What Can We Learn From India ...
WhatsApp Telemedicine For the Developing World: What Can We Learn From India ...
 
Electronic Submissions
Electronic SubmissionsElectronic Submissions
Electronic Submissions
 
Theranos Biz Model
Theranos Biz ModelTheranos Biz Model
Theranos Biz Model
 
Theranos Original Pitchdeck 2006
Theranos Original Pitchdeck 2006Theranos Original Pitchdeck 2006
Theranos Original Pitchdeck 2006
 
IRJET- Smart ICU using IoT
IRJET- Smart ICU using IoTIRJET- Smart ICU using IoT
IRJET- Smart ICU using IoT
 
Global Med Solutione
Global Med SolutioneGlobal Med Solutione
Global Med Solutione
 
Best Practices on Medical Coding in MedDRA
Best Practices on Medical Coding in MedDRABest Practices on Medical Coding in MedDRA
Best Practices on Medical Coding in MedDRA
 
Novel telemedicine
Novel telemedicineNovel telemedicine
Novel telemedicine
 
Starting with SNOMED CT and practical use of SNOMED CT
Starting with SNOMED CT and practical use of SNOMED CTStarting with SNOMED CT and practical use of SNOMED CT
Starting with SNOMED CT and practical use of SNOMED CT
 
ACR Select: Clinical Decision Support Imaging Guidelines
ACR Select: Clinical Decision Support Imaging GuidelinesACR Select: Clinical Decision Support Imaging Guidelines
ACR Select: Clinical Decision Support Imaging Guidelines
 
RFID Applications for Healthcare
RFID Applications for Healthcare RFID Applications for Healthcare
RFID Applications for Healthcare
 

Similar to RFID-Based Patient Tracking System Report Summary

Building a Smart Hospital using RFID technologiesPatrik Fu.docx
Building a Smart Hospital using RFID technologiesPatrik Fu.docxBuilding a Smart Hospital using RFID technologiesPatrik Fu.docx
Building a Smart Hospital using RFID technologiesPatrik Fu.docxhartrobert670
 
Rfid article3 patient care
Rfid article3  patient careRfid article3  patient care
Rfid article3 patient careRabia Naushad
 
IRJET-Cloud based Patient Referral System with RFID Based Clinical Informatio...
IRJET-Cloud based Patient Referral System with RFID Based Clinical Informatio...IRJET-Cloud based Patient Referral System with RFID Based Clinical Informatio...
IRJET-Cloud based Patient Referral System with RFID Based Clinical Informatio...IRJET Journal
 
A novel integrated approach for handling anomalies in RFID data
A novel integrated approach for handling anomalies in RFID dataA novel integrated approach for handling anomalies in RFID data
A novel integrated approach for handling anomalies in RFID dataijujournal
 
A novel integrated approach for handling anomalies in RFID data
A novel integrated approach for handling anomalies in RFID dataA novel integrated approach for handling anomalies in RFID data
A novel integrated approach for handling anomalies in RFID dataijujournal
 
Comparative Effectiveness
Comparative EffectivenessComparative Effectiveness
Comparative EffectivenessBhumika Sharma
 
Healthcare trends and information management strategy
Healthcare trends and information management strategyHealthcare trends and information management strategy
Healthcare trends and information management strategyChristopher Wynder
 
Ensuring Integrity for Medical Tissues and Devices
Ensuring Integrity for Medical Tissues and DevicesEnsuring Integrity for Medical Tissues and Devices
Ensuring Integrity for Medical Tissues and DevicesTerso Solutions
 
Perceptive planning for smart healthcare system through the internet of things
Perceptive planning for smart healthcare system through the internet of thingsPerceptive planning for smart healthcare system through the internet of things
Perceptive planning for smart healthcare system through the internet of thingsDr. Raghavendra GS
 
SPHMS : Smart Patient m-Healthcare Monitoring System with NFC and IOT
SPHMS : Smart Patient m-Healthcare Monitoring System with NFC and IOTSPHMS : Smart Patient m-Healthcare Monitoring System with NFC and IOT
SPHMS : Smart Patient m-Healthcare Monitoring System with NFC and IOTEditor IJCATR
 
Lowlesh N Yadav-Paper ID 208 - Copy.pptx
Lowlesh N Yadav-Paper ID 208 - Copy.pptxLowlesh N Yadav-Paper ID 208 - Copy.pptx
Lowlesh N Yadav-Paper ID 208 - Copy.pptxlowlesh1
 
100427 tsb paper_zig_bee_location
100427 tsb paper_zig_bee_location100427 tsb paper_zig_bee_location
100427 tsb paper_zig_bee_locationSerafin Arroyo
 
IRJET- EMR using RFID Technology
IRJET- EMR using RFID TechnologyIRJET- EMR using RFID Technology
IRJET- EMR using RFID TechnologyIRJET Journal
 
Interenet of Things Based Cloud for Healthcare Network
Interenet of Things Based Cloud for Healthcare NetworkInterenet of Things Based Cloud for Healthcare Network
Interenet of Things Based Cloud for Healthcare NetworkIstabraq M. Al-Joboury
 
Systematic review of quality standards for medical devices and practice measu...
Systematic review of quality standards for medical devices and practice measu...Systematic review of quality standards for medical devices and practice measu...
Systematic review of quality standards for medical devices and practice measu...Pubrica
 

Similar to RFID-Based Patient Tracking System Report Summary (20)

Building a Smart Hospital using RFID technologiesPatrik Fu.docx
Building a Smart Hospital using RFID technologiesPatrik Fu.docxBuilding a Smart Hospital using RFID technologiesPatrik Fu.docx
Building a Smart Hospital using RFID technologiesPatrik Fu.docx
 
Rfid article3 patient care
Rfid article3  patient careRfid article3  patient care
Rfid article3 patient care
 
IRJET-Cloud based Patient Referral System with RFID Based Clinical Informatio...
IRJET-Cloud based Patient Referral System with RFID Based Clinical Informatio...IRJET-Cloud based Patient Referral System with RFID Based Clinical Informatio...
IRJET-Cloud based Patient Referral System with RFID Based Clinical Informatio...
 
A novel integrated approach for handling anomalies in RFID data
A novel integrated approach for handling anomalies in RFID dataA novel integrated approach for handling anomalies in RFID data
A novel integrated approach for handling anomalies in RFID data
 
A novel integrated approach for handling anomalies in RFID data
A novel integrated approach for handling anomalies in RFID dataA novel integrated approach for handling anomalies in RFID data
A novel integrated approach for handling anomalies in RFID data
 
Comparative Effectiveness
Comparative EffectivenessComparative Effectiveness
Comparative Effectiveness
 
Healthcare trends and information management strategy
Healthcare trends and information management strategyHealthcare trends and information management strategy
Healthcare trends and information management strategy
 
Embedded Patient Monitoring System
Embedded Patient Monitoring SystemEmbedded Patient Monitoring System
Embedded Patient Monitoring System
 
Ensuring Integrity for Medical Tissues and Devices
Ensuring Integrity for Medical Tissues and DevicesEnsuring Integrity for Medical Tissues and Devices
Ensuring Integrity for Medical Tissues and Devices
 
MedRPM
MedRPMMedRPM
MedRPM
 
Perceptive planning for smart healthcare system through the internet of things
Perceptive planning for smart healthcare system through the internet of thingsPerceptive planning for smart healthcare system through the internet of things
Perceptive planning for smart healthcare system through the internet of things
 
Research Poster
Research PosterResearch Poster
Research Poster
 
SPHMS : Smart Patient m-Healthcare Monitoring System with NFC and IOT
SPHMS : Smart Patient m-Healthcare Monitoring System with NFC and IOTSPHMS : Smart Patient m-Healthcare Monitoring System with NFC and IOT
SPHMS : Smart Patient m-Healthcare Monitoring System with NFC and IOT
 
Lowlesh N Yadav-Paper ID 208 - Copy.pptx
Lowlesh N Yadav-Paper ID 208 - Copy.pptxLowlesh N Yadav-Paper ID 208 - Copy.pptx
Lowlesh N Yadav-Paper ID 208 - Copy.pptx
 
Chowdhury rfidhpms
Chowdhury rfidhpmsChowdhury rfidhpms
Chowdhury rfidhpms
 
Rfid in health care
Rfid in health careRfid in health care
Rfid in health care
 
100427 tsb paper_zig_bee_location
100427 tsb paper_zig_bee_location100427 tsb paper_zig_bee_location
100427 tsb paper_zig_bee_location
 
IRJET- EMR using RFID Technology
IRJET- EMR using RFID TechnologyIRJET- EMR using RFID Technology
IRJET- EMR using RFID Technology
 
Interenet of Things Based Cloud for Healthcare Network
Interenet of Things Based Cloud for Healthcare NetworkInterenet of Things Based Cloud for Healthcare Network
Interenet of Things Based Cloud for Healthcare Network
 
Systematic review of quality standards for medical devices and practice measu...
Systematic review of quality standards for medical devices and practice measu...Systematic review of quality standards for medical devices and practice measu...
Systematic review of quality standards for medical devices and practice measu...
 

RFID-Based Patient Tracking System Report Summary

  • 1. 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
  • 2. 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
  • 3. 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.
  • 4. 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
  • 5. 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
  • 6. 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
  • 7. 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
  • 8. 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
  • 9. 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
  • 10. 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
  • 11. 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
  • 12. 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. 0 5 10 15 20 25 30 35 40 45 50 0.0-1.01.0-2.02.0-3.03.0-4.04.0-5.05.0-6.07.0-8.08.0-9.09.0-10.010.0-11.011.0-12.012.0-13.013.0-14.014.0-15.015.0-16.016.0-17.017.0-18.018.0-19.019.0-20.0 Waiting time(hours) NumberofPatients 0 10 20 30 40 50 60 0.0-1.01.0-2.02.0-3.03.0-4.04.0-5.05.0-6.07.0-8.08.0-9.09.0-10.010.0-11.011.0-12.012.0-13.013.0-14.014.0-15.015.0-16.016.0-17.017.0-18.018.0-19.019.0-20.0 Waiting time(hours) NumberofPatients 0 10 20 30 40 50 60 0.0-1.01.0-2.02.0-3.03.0-4.04.0-5.05.0-6.07.0-8.08.0-9.09.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) NumberofPatients Improvement Figure 2a: 433 MHz RFID Reader Figure 2b: Active RFID tags 10
  • 13. 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
  • 14. 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
  • 15. 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
  • 16. 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
  • 17. 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
  • 18. Figure 11: Sample report generated by our program 16
  • 19. 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
  • 20. 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
  • 21. 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
  • 22. 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
  • 23. 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
  • 24. 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
  • 25. 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
  • 26. 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
  • 27. Appendix F: User’s Manual 25
  • 28. 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
  • 29. 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
  • 30. 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
  • 31. 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
  • 32. 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
  • 33. 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
  • 34. 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
  • 35. 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
  • 36. 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
  • 37. 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
  • 38. 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.
  • 39. 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
  • 40. 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
  • 41. 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
  • 42. 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
  • 43. 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
  • 44. 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
  • 45. 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
  • 46. 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
  • 47. 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
  • 48. 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
  • 49. 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:47 P2 Registered 07:10:55 dr 1 visited patient 2 07:18:39 dr 2 visit patient 1 08:16:34 P3 Registered 07:19:04 P1 Registered 07:21:25 P2 Registered 07:21:29 dr visits patient 3 07:21:14 dr visits patient 2 07:27:53 dr visits patient 1 08:23:33 P1 Registered 07:30:18 P2 Registered 07:30:30 P3 Registered 07:30:35 P3 untreated P3 Registered 07:32:10 dr visits patient 1 07:33:36 dr visits patient 2 08:31:19 dr visits patient 3 07:35:58 P1 Registered 07:36:04 47
  • 50. 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:09 4/26/2006 07:10:55 4/26/2006 08:16:34 01:05 4/26/2006 07:19:04 4/26/2006 07:21:14 00:02 4/26/2006 07:21:25 4/26/2006 08:23:33 01:02 4/26/2006 07:21:29 4/26/2006 07:26:44 00:05 4/26/2006 07:30:18 4/26/2006 07:31:07 00:00 4/26/2006 07:30:30 4/26/2006 08:31:19 01:00 4/26/2006 07:30:35 not yet treated not treated 4/26/2006 07:32:10 4/26/2006 07:35:58 00:03 4/26/2006 07:36:04 not yet treated not treated 4/26/2006 07:36:08 not yet treated not treated 48
  • 51. 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
  • 52. 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
  • 53. 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 yetn"; else s += "treated at " + time_treatmentStart + "n"; return s; } public void getTreatmentTime( Iterable<Doctor> doctors ) { for ( Doctor d : doctors ) { 51
  • 54. 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
  • 55. 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
  • 56. } 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
  • 57. 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
  • 58. 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
  • 59. 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
  • 60. 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
  • 61. 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
  • 62. } 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
  • 63. 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
  • 64. 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
  • 65. 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
  • 66. { 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
  • 67. /** 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
  • 68. 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
  • 69. 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://localhostSQLEXPRESS: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
  • 70. 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
  • 71. 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
  • 72. 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
  • 73. } } 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
  • 74. 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( "inputrawdata.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( "inputrawdata.txt" ) ); PrintWriter out = new PrintWriter( new FileWriter( "inputdata.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
  • 75. /** 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
  • 76. /** 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
  • 77. 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
  • 78. 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
  • 79. } 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
  • 80. 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