source: Scheduling/trunk/cs/bsdx0200GUISourceCode/CGDocumentManager.cs@ 1087

Last change on this file since 1087 was 1083, checked in by Sam Habiel, 14 years ago

New exe and dll
calendarGrid: Minor documentation updates.
CGAppointments: Object now supports Deep cloning
CGDocument:

  1. Major changes on how m_pAvArrays is handled. Now it is locked whenever it is updated or queried. Some refactoring to make sure there are no db calls during the locks so that the locks won't be expensive.
  2. Removed ClearResources, an unused method.
  3. Appointment aCopy walkin property is set to true if the appointment is a walkin. This makes sure that the grid draws it correctly between it is added to the appointment array and we fetch new data from the server.
  4. Create appointment is not responsible anymore for requesting updates from the server. All requests to update data must be done through CGView, as it is the only party interested in displaying accurate data on the grid. Just send the create appt event to the server.
  5. CheckInAppointment: Same thing. Now responsible for requesting updates from the server. Just send the checkin event to the server.

CGDocumentManager: Removed tracing. Done in BMX Library only now.
CGView:

  1. CGAppointment fetched from Document, not from the copy maintained by the calendarGrid.
  2. RefreshDocument calls before an appointment is made have been removed (need to find another way to make sure that the appointment has just not been booked). RefreshDocument & UpdateArrays are called async after appointments are made.
  3. Appointment List passed to Calendar grid is now a copy, to prevent issues with concurrent access.
  4. Message if a patient has apppointment at the same time vastly improved.
File size: 43.3 KB
RevLine 
[614]1using System;
2using System.Windows.Forms;
3using System.Collections;
4using System.Data;
5using System.Diagnostics;
[1050]6using System.Threading;
[614]7using IndianHealthService.BMXNet;
[772]8using Mono.Options;
[788]9using System.Runtime.InteropServices;
[614]10
11namespace IndianHealthService.ClinicalScheduling
12{
13 /// <summary>
[1050]14 /// Main Worker. Handles sub-forms.
[614]15 /// </summary>
[1050]16 public class CGDocumentManager //: System.Windows.Forms.Form
[614]17 {
18 #region Member Variables
19
20 private static CGDocumentManager _current;
[1050]21 private Hashtable _views = new Hashtable(); //Returns the list of currently opened documents
22 private Hashtable m_AVViews = new Hashtable(); // List of currently opened CGAVViews
[620]23 private string m_sWindowText = "Clinical Scheduling"; //Default Window Text
[1050]24 private bool m_bSchedManager = false; // Do you have the XUPROGMODE or BSDXZMGR?
25 private bool m_bExitOK = true; // Okay to exit program? Used to control Re-logins. Default true.
26 public string m_sHandle = "0"; // Not Used
[824]27
[1050]28 //Connection variables (tied to command line parameters /a /v /s /p /e)
[772]29 private string m_AccessCode="";
30 private string m_VerifyCode="";
31 private string m_Server="";
32 private int m_Port=0;
[1050]33 private string m_Encoding=""; //Encoding is "" by default;
[614]34
[843]35 //Data Access Layer
36 private DAL _dal = null;
37
[614]38 //M Connection member variables
[1050]39 private DataSet m_dsGlobal = null; // Holds all user data
40 private BMXNetConnectInfo m_ConnectInfo = null; // Connection to VISTA object
41 private BMXNetConnectInfo.BMXNetEventDelegate CDocMgrEventDelegate; // Delegate to respond to messages from VISTA. Responds to event: BMXNetConnectInfo.BMXNetEvent
[614]42
43 #endregion
44
[1050]45 #region Properties
46
[824]47 /// <summary>
[1050]48 /// Returns the document manager's BMXNetConnectInfo member
[824]49 /// </summary>
[1050]50 public BMXNetConnectInfo ConnectInfo
51 {
52 get
53 {
54 return m_ConnectInfo;
55 }
56 }
57
58 /// <summary>
59 /// True if the current user holds the BSDXZMGR or XUPROGMODE keys in RPMS
60 /// </summary>
61 public bool ScheduleManager
62 {
63 get
64 {
65 return m_bSchedManager;
66 }
67 }
68
69 /// <summary>
70 /// Holds the user and division
71 /// </summary>
72 public string WindowText
73 {
74 get
75 {
76 return m_sWindowText;
77 }
78 }
79
80 /// <summary>
81 /// This dataset contains tables used by the entire application
82 /// </summary>
83 public DataSet GlobalDataSet
84 {
85 get
86 {
87 return m_dsGlobal;
88 }
89 set
90 {
91 m_dsGlobal = value;
92 }
93 }
94
95 /// <summary>
96 /// Returns the single CGDocumentManager object
97 /// </summary>
98 public static CGDocumentManager Current
99 {
100 get
101 {
102 return _current;
103 }
104 }
105
106
107 /// <summary>
108 /// Returns the list of currently opened documents
109 /// </summary>
110 public Hashtable Views
111 {
112 get
113 {
114 return _views;
115 }
116 }
117
118 /// <summary>
119 /// Returns the list of currently opened CGAVViews
120 /// </summary>
121 public Hashtable AvailabilityViews
122 {
123 get
124 {
125 return this.m_AVViews;
126 }
127 }
128
129 public DAL DAL
130 {
131 get { return this._dal; }
132 }
133
134
135 #endregion
136
137 /// <summary>
138 /// Constructor. Does absolutely nothing at this point.
139 /// </summary>
[614]140 public CGDocumentManager()
141 {
142 }
143
[1050]144
145#if DEBUG
146 //To write to the console
147 [DllImport("kernel32.dll")]
148 static extern bool AttachConsole(int dwProcessId);
149 private const int ATTACH_PARENT_PROCESS = -1;
150#endif
151 /// <summary>
152 /// Main Entry Point
153 /// </summary>
154 /// <param name="args">We accept the following Arguments:
155 /// /s or -s = Server ip address or name
156 /// /p or -p = port number (must be numeric)
157 /// /a or -a = Access Code
158 /// /v or -v = Verify Code
159 /// /e or -e = Encoding (name of encoding as known to windows, such as windows-1256)
160 /// </param>
161 /// <remarks>
162 /// Encoding decision is complex. This is the order of priority:
163 /// - If the M DB runs in UTF-8, that's what we are going to use.
164 /// - If that's not so, /e sets the default encoding. If /e is a non-existent encoding, move forward.
165 /// - If /e is not supplied or is not recognized, the default encoding is the Windows default Encoding for the user.
166 /// </remarks>
167 [STAThread()]
168 static void Main(string[] args)
169 {
170#if DEBUG
171 // Print console messages to console if launched from console
172 // Note: Imported From kernel32.dll
173 AttachConsole(ATTACH_PARENT_PROCESS);
174#endif
[1065]175
176#if TRACE
177 DateTime startLoadTime = DateTime.Now;
178#endif
179
[1050]180 //Store a class instance of manager. Actual constructor does nothing.
181 _current = new CGDocumentManager();
182
[1071]183 //Get command line options; store in private class wide variables
[1050]184 var opset = new OptionSet() {
185 { "s=", s => _current.m_Server = s },
186 { "p=", p => _current.m_Port = int.Parse(p) },
187 { "a=", a => _current.m_AccessCode = a },
188 { "v=", v => _current.m_VerifyCode = v },
189 { "e=", e => _current.m_Encoding = e}
190 };
191
192 opset.Parse(args);
193
[1071]194 //Init app
[1051]195 bool isEverythingOkay = _current.InitializeApp();
[1050]196
[1071]197 //if an error occurred, break out.
[1051]198 if (!isEverythingOkay) return;
199
[1050]200 //Create the first empty document
[1071]201 //A document holds the resources, appointments, and availabilites
[1070]202 //SAM: Good place for break point
[1050]203 CGDocument doc = new CGDocument();
204 doc.DocManager = _current;
[1071]205
206 //Create new View
207 //A view is a specific arrangement of appointments and availabilites that constitute a document
208 CGView view = new CGView();
[1073]209 view.InitializeDocView(doc, _current, doc.StartDate, _current.WindowText);
[1071]210
211 //Handle BMX Event
[1050]212 Application.DoEvents();
[1071]213
214 //Application wide error handler for unhandled errors
215 Application.ThreadException += new ThreadExceptionEventHandler(App_ThreadException);
216
[1065]217#if TRACE
218 DateTime EndLoadTime = DateTime.Now;
219 TimeSpan LoadTime = EndLoadTime - startLoadTime;
220 Debug.Write("Load Time for GUI is " + LoadTime.Seconds + " s & " + LoadTime.Milliseconds + " ms\n");
221#endif
[1071]222
223 view.Show();
224 view.Activate();
225
[1050]226 Application.Run();
227 }
228
[1065]229 /// <summary>
230 /// Exception handler for application errors. TODO: Test
231 /// </summary>
232 /// <param name="sender"></param>
233 /// <param name="e"></param>
234 static void App_ThreadException(object sender, ThreadExceptionEventArgs e)
235 {
236 if (e.Exception is System.Net.Sockets.SocketException)
237 {
238 MessageBox.Show("Looks like we lost our connection with the server\nClick OK to terminate the application.");
239 Application.Exit();
240 }
[1050]241
[1065]242 string msg = "A problem has occured in this applicaton. \r\n\r\n" +
243 "\t" + e.Exception.Message + "\r\n\r\n" +
244 "Would you like to continue the application?";
245
246 DialogResult res = MessageBox.Show(msg, "Unexpected Error", MessageBoxButtons.YesNo);
247
248 if (res == DialogResult.Yes) return;
249 else Application.Exit();
250 }
251
252
[614]253 #region BMXNet Event Handler
254 private void CDocMgrEventHandler(Object obj, BMXNet.BMXNetEventArgs e)
255 {
256 if (e.BMXEvent == "BSDX CALL WORKSTATIONS")
257 {
258 string sParam = "";
259 string sDelim="~";
260 sParam += this.m_ConnectInfo.UserName + sDelim;
261 sParam += this.m_sHandle + sDelim;
262 sParam += Application.ProductVersion + sDelim;
263 sParam += this._views.Count.ToString();
264 _current.m_ConnectInfo.RaiseEvent("BSDX WORKSTATION REPORT", sParam, true);
265 }
266 if (e.BMXEvent == "BSDX ADMIN MESSAGE")
267 {
268 string sMsg = e.BMXParam;
269 ShowAdminMsgDelegate samd = new ShowAdminMsgDelegate(ShowAdminMsg);
[1050]270 //this.Invoke(samd, new object [] {sMsg});
271 samd.Invoke(sMsg);
[614]272 }
273 if (e.BMXEvent == "BSDX ADMIN SHUTDOWN")
274 {
275 string sMsg = e.BMXParam;
276 CloseAllDelegate cad = new CloseAllDelegate(CloseAll);
[1050]277 //this.Invoke(cad, new object [] {sMsg});
278 cad.Invoke(sMsg);
[614]279 }
280 }
281
282 delegate void ShowAdminMsgDelegate(string sMsg);
283
284 private void ShowAdminMsg(string sMsg)
285 {
286 MessageBox.Show(sMsg, "Message from Scheduling Administrator", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
287 }
288
289 #endregion BMXNet Event Handler
290
291
292 #region Methods & Events
293
[1050]294 /// <summary>
295 /// See InitializeApp(bool) below
296 /// </summary>
[1051]297 private bool InitializeApp()
[614]298 {
[1051]299 return InitializeApp(false);
[614]300 }
301
[1050]302 /// <summary>
303 /// Does a million things:
304 /// 1. Starts Connection and displays log-in dialogs
305 /// 2. Starts Splash screen
306 /// 3. Loads data tables
307 /// </summary>
308 /// <param name="bReLogin">Is the User logging in again from a currently running instance?
309 /// If so, display a dialog to collect access and verify codes.</param>
[1051]310 private bool InitializeApp(bool bReLogin)
[614]311 {
[1050]312 //Set M connection info
[824]313 m_ConnectInfo = new BMXNetConnectInfo(m_Encoding); // Encoding is "" unless passed in command line
[843]314 _dal = new DAL(m_ConnectInfo); // Data access layer
[824]315 //m_ConnectInfo.bmxNetLib.StartLog(); //This line turns on logging of messages
[1050]316
317 //Create a delegate to process events raised by BMX.
[824]318 CDocMgrEventDelegate = new BMXNetConnectInfo.BMXNetEventDelegate(CDocMgrEventHandler);
[1050]319 //Tie delegate to Events generated by BMX.
[824]320 m_ConnectInfo.BMXNetEvent += CDocMgrEventDelegate;
[1050]321 //Disable polling (But does this really work???? I don't see how it gets disabled)
[824]322 m_ConnectInfo.EventPollingEnabled = false;
323
[1051]324 //Show a splash screen while initializing; define delegates to remote thread
[1050]325 DSplash m_ds = new DSplash();
326 DSplash.dSetStatus setStatusDelegate = new DSplash.dSetStatus(m_ds.SetStatus);
327 DSplash.dAny closeSplashDelegate = new DSplash.dAny(m_ds.RemoteClose);
[1051]328 DSplash.dProgressBarSet setMaxProgressDelegate = new DSplash.dProgressBarSet(m_ds.RemoteProgressBarMaxSet);
329 DSplash.dProgressBarSet setProgressDelegate = new DSplash.dProgressBarSet(m_ds.RemoteProgressBarValueSet);
[794]330
[1051]331 //Start new thread for the Splash screen.
[1070]332 Thread threadSplash = new Thread(new ParameterizedThreadStart(frm => ((DSplash)frm).ShowDialog()));
[1051]333 threadSplash.IsBackground = true; //expendable thread -- exit even if still running.
334 threadSplash.Name = "Splash Thread";
335 threadSplash.Start(m_ds); // pass form as parameter.
[794]336
[1062]337 //There are 19 steps to load the application. That's max for the progress bar.
338 setMaxProgressDelegate(19);
[1050]339
[1051]340 // smh--not used: System.Configuration.ConfigurationManager.GetSection("appSettings");
341
[1050]342 setStatusDelegate("Connecting to VISTA");
[1051]343
[1050]344 //Try to connect using supplied values for Server and Port
345 //Why am I doing this? The library BMX net uses prompts for access and verify code
346 //whether you can connect or not. Not good. So I test first whether
347 //we can connect at all by doing a simple connection and disconnect.
[1051]348 //TODO: Make this more robust by sending a TCPConnect message and seeing if you get a response
[1050]349 if (m_Server != "" && m_Port != 0)
350 {
351 System.Net.Sockets.TcpClient tcpClient = new System.Net.Sockets.TcpClient();
352 try
[794]353 {
[1050]354 tcpClient.Connect(m_Server, m_Port); // open it
355 tcpClient.Close(); // then close it
356 }
[1051]357 catch (System.Net.Sockets.SocketException)
[1050]358 {
[1051]359 MessageBox.Show("Cannot connect to VistA. Network Error");
360 return false;
[1050]361 }
362 }
363
[1062]364
365 bool bRetry = true;
366
[1051]367 // Do block is Log-in logic
368 do
[1050]369 {
370 // login crap
371 try
372 {
373 // Not my code
374 if (bReLogin == true)
[794]375 {
[1050]376 //Prompt for Access and Verify codes
377 _current.m_ConnectInfo.LoadConnectInfo("", "");
[794]378 }
[1050]379 // My code -- buts looks so ugly!
[1071]380 // Checks the passed parameters stored in the class variables
[1050]381 else
[794]382 {
[1050]383 if (m_Server != String.Empty && m_Port != 0 && m_AccessCode != String.Empty
384 && m_VerifyCode != String.Empty)
[794]385 {
[1050]386 m_ConnectInfo.LoadConnectInfo(m_Server, m_Port, m_AccessCode, m_VerifyCode);
[794]387 }
[1050]388 else if (m_Server != String.Empty && m_Port != 0)
389 m_ConnectInfo.LoadConnectInfo(m_Server, m_Port, "", "");
[794]390 else
[1050]391 m_ConnectInfo.LoadConnectInfo();
[794]392 }
[1050]393 bRetry = false;
394 }
395 catch (System.Net.Sockets.SocketException)
396 {
[1051]397 MessageBox.Show("Cannot connect to VistA. Network Error");
[1050]398 }
[1051]399 catch (BMXNetException ex)
[1050]400 {
401 if (MessageBox.Show("Unable to connect to VistA. " + ex.Message, "Clinical Scheduling", MessageBoxButtons.RetryCancel) == DialogResult.Retry)
[794]402 {
[1050]403 bRetry = true;
404 _current.m_ConnectInfo.ChangeServerInfo();
[794]405 }
[1050]406 else
[794]407 {
[1050]408 closeSplashDelegate();
409 bRetry = false;
[1051]410 return false; //tell main that it's a no go.
[794]411 }
[1050]412 }
413 }while (bRetry == true);
414
415 //Create global dataset
416 _current.m_dsGlobal = new DataSet("GlobalDataSet");
[614]417
[1050]418 //Version info
[1051]419 // Table #1
420 setProgressDelegate(1);
[1050]421 setStatusDelegate("Getting Version Info from Server...");
[614]422
[1050]423 DataTable ver = _dal.GetVersion("BSDX");
424 ver.TableName = "VersionInfo";
425 m_dsGlobal.Tables.Add(ver);
[1039]426
[1050]427 //How to extract the version numbers:
428 DataTable dtVersion = m_dsGlobal.Tables["VersionInfo"];
429 Debug.Assert(dtVersion.Rows.Count == 1);
430 DataRow rVersion = dtVersion.Rows[0];
431 string sMajor = rVersion["MAJOR_VERSION"].ToString();
432 string sMinor = rVersion["MINOR_VERSION"].ToString();
433 string sBuild = rVersion["BUILD"].ToString();
434 decimal fBuild = Convert.ToDecimal(sBuild);
[614]435
[1050]436 //Make sure that the server is running the same version the client is.
437 Version x = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
[1039]438
[1050]439 //if version numbers mismatch, don't continue.
440 //TODO: For future: Include in v. 1.5
441 /*
442 if (!(x.Major.ToString() == sMajor && x.Minor.ToString() + x.Build.ToString() == sMinor))
443 {
444 MessageBox.Show(
445 "Server runs version " + sMajor + "." + sMinor + "\r\n" +
446 "You are running " + x.ToString() + "\r\n\r\n" +
447 "Major, Minor and Build versions must match",
448 "Version Mismatch");
449 m_ds.Close();
450 return;
451 }
452 */
[1039]453
454
[1050]455 //Change encoding
[1051]456 // Call #2
457 setProgressDelegate(2);
[1050]458 setStatusDelegate("Setting encoding...");
459
460 if (m_Encoding == String.Empty)
461 {
462 string utf8_server_support = m_ConnectInfo.bmxNetLib.TransmitRPC("BMX UTF-8", "");
463 if (utf8_server_support == "1")
464 m_ConnectInfo.bmxNetLib.Encoder = System.Text.UTF8Encoding.UTF8;
465 }
[1051]466
467 //Set application context
468 // Call #3
469 setProgressDelegate(3);
[1050]470 setStatusDelegate("Setting Application Context to BSDXRPC...");
471 m_ConnectInfo.AppContext = "BSDXRPC";
[614]472
[1050]473 //Load global recordsets
474 string statusConst = "Loading VistA data tables...";
475 setStatusDelegate(statusConst);
[614]476
[1050]477 string sCommandText;
[614]478
[1051]479 //Schedule User Info
480 // Table #4
481 setProgressDelegate(4);
[1050]482 setStatusDelegate(statusConst + " Schedule User");
483 DataTable dtUser = _dal.GetUserInfo(m_ConnectInfo.DUZ);
484 dtUser.TableName = "SchedulingUser";
485 m_dsGlobal.Tables.Add(dtUser);
486 Debug.Assert(dtUser.Rows.Count == 1);
[614]487
[1050]488 // Only one row and one column named "MANAGER". Set local var m_bSchedManager to true if Manager.
489 DataRow rUser = dtUser.Rows[0];
490 Object oUser = rUser["MANAGER"];
491 string sUser = oUser.ToString();
492 m_bSchedManager = (sUser == "YES") ? true : false;
[614]493
[1051]494 //Get Access Types
495 // Table #5
496 setProgressDelegate(5);
[1050]497 setStatusDelegate(statusConst + " Access Types");
498 DataTable dtAccessTypes = _dal.GetAccessTypes();
499 dtAccessTypes.TableName = "AccessTypes";
500 m_dsGlobal.Tables.Add(dtAccessTypes);
[614]501
[1051]502 //Get Access Groups
503 // Table #6
504 setProgressDelegate(6);
[1050]505 setStatusDelegate(statusConst + " Access Groups");
506 LoadAccessGroupsTable();
[788]507
[1050]508 //Build Primary Key for AccessGroup table
509 DataTable dtGroups = m_dsGlobal.Tables["AccessGroup"];
510 DataColumn dcKey = dtGroups.Columns["ACCESS_GROUP"];
511 DataColumn[] dcKeys = new DataColumn[1];
512 dcKeys[0] = dcKey;
513 dtGroups.PrimaryKey = dcKeys;
514
[1062]515 //Get Access Group Types (Combines Access Types and Groups)
516 //Optimization Note: Can eliminate Access type and Access Group Table
517 // But they are heavily referenced throughout the code.
[1051]518 // Table #7
519 setProgressDelegate(7);
[1050]520 setStatusDelegate(statusConst + " Access Group Types");
521 LoadAccessGroupTypesTable();
522
523 //Build Primary Key for AccessGroupType table
524 DataTable dtAGTypes = m_dsGlobal.Tables["AccessGroupType"];
525 DataColumn dcGTKey = dtAGTypes.Columns["ACCESS_GROUP_TYPEID"];
526 DataColumn[] dcGTKeys = new DataColumn[1];
527 dcGTKeys[0] = dcGTKey;
528 dtAGTypes.PrimaryKey = dcGTKeys;
529
530 //Build Data Relationship between AccessGroupType and AccessTypes tables
531 DataRelation dr = new DataRelation("AccessGroupType", //Relation Name
532 m_dsGlobal.Tables["AccessGroup"].Columns["BMXIEN"], //Parent
533 m_dsGlobal.Tables["AccessGroupType"].Columns["ACCESS_GROUP_ID"]); //Child
534 m_dsGlobal.Relations.Add(dr);
535
[1051]536 //ResourceGroup Table (Resource Groups by User)
537 // Table #8
[1062]538 // What shows up on the tree. The groups the user has access to.
[1051]539 setProgressDelegate(8);
[1050]540 setStatusDelegate(statusConst + " Resource Groups By User");
541 LoadResourceGroupTable();
542
[1051]543 //Resources by user
544 // Table #9
[1062]545 // Individual Resources
[1051]546 setProgressDelegate(9);
[1050]547 setStatusDelegate(statusConst + " Resources By User");
548 LoadBSDXResourcesTable();
549
550 //Build Primary Key for Resources table
551 DataColumn[] dc = new DataColumn[1];
552 dc[0] = m_dsGlobal.Tables["Resources"].Columns["RESOURCEID"];
553 m_dsGlobal.Tables["Resources"].PrimaryKey = dc;
554
[1051]555 //GroupResources table
556 // Table #10
[1062]557 // Resource Groups and Indivdual Resources together
[1051]558 setProgressDelegate(10);
[1050]559 setStatusDelegate(statusConst + " Group Resources");
560 LoadGroupResourcesTable();
561
562 //Build Primary Key for ResourceGroup table
563 dc = new DataColumn[1];
564 dc[0] = m_dsGlobal.Tables["ResourceGroup"].Columns["RESOURCE_GROUP"];
565 m_dsGlobal.Tables["ResourceGroup"].PrimaryKey = dc;
566
567 //Build Data Relationships between ResourceGroup and GroupResources tables
568 dr = new DataRelation("GroupResource", //Relation Name
569 m_dsGlobal.Tables["ResourceGroup"].Columns["RESOURCE_GROUP"], //Parent
570 m_dsGlobal.Tables["GroupResources"].Columns["RESOURCE_GROUP"]); //Child
571 CGSchedLib.OutputArray(m_dsGlobal.Tables["GroupResources"], "GroupResources");
572 m_dsGlobal.Relations.Add(dr);
573
[1051]574 //HospitalLocation table
575 //Table #11
576 setProgressDelegate(11);
[1050]577 setStatusDelegate(statusConst + " Clinics");
578 //cmd.CommandText = "SELECT BMXIEN 'HOSPITAL_LOCATION_ID', NAME 'HOSPITAL_LOCATION', DEFAULT_PROVIDER, STOP_CODE_NUMBER, INACTIVATE_DATE, REACTIVATE_DATE FROM HOSPITAL_LOCATION";
579 sCommandText = "BSDX HOSPITAL LOCATION";
580 ConnectInfo.RPMSDataTable(sCommandText, "HospitalLocation", m_dsGlobal);
581 Debug.Write("LoadGlobalRecordsets -- HospitalLocation loaded\n");
582
583 //Build Primary Key for HospitalLocation table
584 dc = new DataColumn[1];
585 DataTable dtTemp = m_dsGlobal.Tables["HospitalLocation"];
586 dc[0] = dtTemp.Columns["HOSPITAL_LOCATION_ID"];
587 m_dsGlobal.Tables["HospitalLocation"].PrimaryKey = dc;
588
589 //Build Data Relationships between Resources and HospitalLocation tables
590 dr = new DataRelation("HospitalLocationResource", //Relation Name
591 m_dsGlobal.Tables["HospitalLocation"].Columns["HOSPITAL_LOCATION_ID"], //Parent
592 m_dsGlobal.Tables["Resources"].Columns["HOSPITAL_LOCATION_ID"], false); //Child
593 m_dsGlobal.Relations.Add(dr);
594
[1051]595 //Build ScheduleUser table
596 //Table #12
597 setProgressDelegate(12);
[1050]598 setStatusDelegate(statusConst + " Schedule User");
599 this.LoadScheduleUserTable();
600
601 //Build Primary Key for ScheduleUser table
602 dc = new DataColumn[1];
603 dtTemp = m_dsGlobal.Tables["ScheduleUser"];
604 dc[0] = dtTemp.Columns["USERID"];
605 m_dsGlobal.Tables["ScheduleUser"].PrimaryKey = dc;
606
[1051]607 //Build ResourceUser table
608 //Table #13
[1062]609 //Acess to Resources by [this] User
[1051]610 setProgressDelegate(13);
[1050]611 setStatusDelegate(statusConst + " Resource User");
612 this.LoadResourceUserTable();
613
614 //Build Primary Key for ResourceUser table
615 dc = new DataColumn[1];
616 dtTemp = m_dsGlobal.Tables["ResourceUser"];
617 dc[0] = dtTemp.Columns["RESOURCEUSER_ID"];
618 m_dsGlobal.Tables["ResourceUser"].PrimaryKey = dc;
619
620 //Create relation between BSDX Resource and BSDX Resource User tables
621 dr = new DataRelation("ResourceUser", //Relation Name
622 m_dsGlobal.Tables["Resources"].Columns["RESOURCEID"], //Parent
623 m_dsGlobal.Tables["ResourceUser"].Columns["RESOURCEID"]); //Child
624 m_dsGlobal.Relations.Add(dr);
625
[1051]626 //Build active provider table
627 //Table #14
[1062]628 //TODO: Lazy load the provider table; no need to load in advance.
[1051]629 setProgressDelegate(14);
[1050]630 setStatusDelegate(statusConst + " Providers");
631 sCommandText = "SELECT BMXIEN, NAME FROM NEW_PERSON WHERE INACTIVE_DATE = '' AND BMXIEN > 1";
632 ConnectInfo.RPMSDataTable(sCommandText, "Provider", m_dsGlobal);
633 Debug.Write("LoadGlobalRecordsets -- Provider loaded\n");
634
[1062]635 //Build the HOLIDAY table
[1051]636 //Table #15
637 setProgressDelegate(15);
[1050]638 setStatusDelegate(statusConst + " Holiday");
639 sCommandText = "SELECT NAME, DATE FROM HOLIDAY WHERE DATE > '" + DateTime.Today.ToShortDateString() + "'";
640 ConnectInfo.RPMSDataTable(sCommandText, "HOLIDAY", m_dsGlobal);
641 Debug.Write("LoadingGlobalRecordsets -- Holidays loaded\n");
642
643
644 //Save the xml schema
645 //m_dsGlobal.WriteXmlSchema(@"..\..\csSchema20060526.xsd");
646 //----------------------------------------------
647
[1051]648 setStatusDelegate("Setting Receive Timeout");
[1050]649 _current.m_ConnectInfo.ReceiveTimeout = 30000; //30-second timeout
650
[788]651#if DEBUG
[1050]652 _current.m_ConnectInfo.ReceiveTimeout = 600000; //longer timeout for debugging
653#endif
[1051]654 // Event Subsriptions
655 setStatusDelegate("Subscribing to Server Events");
[1062]656 //Table #16
657 setProgressDelegate(16);
658 _current.m_ConnectInfo.SubscribeEvent("BSDX SCHEDULE");
659 //Table #17
[1051]660 setProgressDelegate(17);
[1062]661 _current.m_ConnectInfo.SubscribeEvent("BSDX CALL WORKSTATIONS");
[1051]662 //Table #18
663 setProgressDelegate(18);
[1062]664 _current.m_ConnectInfo.SubscribeEvent("BSDX ADMIN MESSAGE");
[1051]665 //Table #19
666 setProgressDelegate(19);
667 _current.m_ConnectInfo.SubscribeEvent("BSDX ADMIN SHUTDOWN");
[614]668
[1050]669 _current.m_ConnectInfo.EventPollingInterval = 5000; //in milliseconds
670 _current.m_ConnectInfo.EventPollingEnabled = true;
671 _current.m_ConnectInfo.AutoFire = 12; //AutoFire every 12*5 seconds
[614]672
[1050]673 //Close Splash Screen
674 closeSplashDelegate();
[1051]675
676 return true;
[1050]677
[614]678 }
679
680
[1050]681
[614]682 public void LoadAccessGroupsTable()
683 {
684 string sCommandText = "SELECT * FROM BSDX_ACCESS_GROUP";
685 ConnectInfo.RPMSDataTable(sCommandText, "AccessGroup", m_dsGlobal);
686 Debug.Write("LoadGlobalRecordsets -- AccessGroups loaded\n");
687 }
688
689 public void LoadAccessGroupTypesTable()
690 {
691 string sCommandText = "BSDX GET ACCESS GROUP TYPES";
692 ConnectInfo.RPMSDataTable(sCommandText, "AccessGroupType", m_dsGlobal);
693 Debug.Write("LoadGlobalRecordsets -- AccessGroupTypes loaded\n");
694 }
695
696 public void LoadBSDXResourcesTable()
697 {
698 string sCommandText = "BSDX RESOURCES^" + m_ConnectInfo.DUZ;
699 ConnectInfo.RPMSDataTable(sCommandText, "Resources", m_dsGlobal);
700 Debug.Write("LoadGlobalRecordsets -- Resources loaded\n");
701 }
702
703 public void LoadResourceGroupTable()
704 {
705 //ResourceGroup Table (Resource Groups by User)
706 //Table "ResourceGroup" contains all resource group names
707 //to which user has access
708 //Fields are: RESOURCE_GROUPID, RESOURCE_GROUP
709 string sCommandText = "BSDX RESOURCE GROUPS BY USER^" + m_ConnectInfo.DUZ;
710 ConnectInfo.RPMSDataTable(sCommandText, "ResourceGroup", m_dsGlobal);
711 Debug.Write("LoadGlobalRecordsets -- ResourceGroup loaded\n");
712 }
713
714 public void LoadGroupResourcesTable()
715 {
716 //Table "GroupResources" contains all active GROUP/RESOURCE combinations
717 //to which user has access based on entries in BSDX RESOURCE USER file
718 //If user has BSDXZMGR or XUPROGMODE keys, then ALL Group/Resource combinstions
719 //are returned.
720 //Fields are: RESOURCE_GROUPID, RESOURCE_GROUP, RESOURCE_GROUP_ITEMID, RESOURCE_NAME, RESOURCE_ID
721 string sCommandText = "BSDX GROUP RESOURCE^" + m_ConnectInfo.DUZ;
722 ConnectInfo.RPMSDataTable(sCommandText, "GroupResources", m_dsGlobal);
723 Debug.Write("LoadGlobalRecordsets -- GroupResources loaded\n");
724 }
725
726 public void LoadScheduleUserTable()
727 {
728 //Table "ScheduleUser" contains an entry for each user in File 200 (NEW PERSON)
729 //who possesses the BSDXZMENU security key.
730 string sCommandText = "BSDX SCHEDULE USER";
731 ConnectInfo.RPMSDataTable(sCommandText, "ScheduleUser", m_dsGlobal);
732 Debug.Write("LoadGlobalRecordsets -- ScheduleUser loaded\n");
733 }
734
735 public void LoadResourceUserTable()
736 {
737 //Table "ResourceUser" duplicates the BSDX RESOURCE USER File.
738 //NOTE: Column names are RESOURCEUSER_ID, RESOURCEID,
739 // OVERBOOK, MODIFY_SCHEDULE, USERID, USERID1
740 //string sCommandText = "SELECT BMXIEN RESOURCEUSER_ID, INTERNAL[RESOURCENAME] RESOURCEID, OVERBOOK, MODIFY_SCHEDULE, USERNAME USERID, INTERNAL[USERNAME] FROM BSDX_RESOURCE_USER";
741 LoadResourceUserTable(false);
742 }
743
744 public void LoadResourceUserTable(bool bAllUsers)
745 {
[1050]746 string sCommandText = @"SELECT BMXIEN RESOURCEUSER_ID, RESOURCENAME, INTERNAL[RESOURCENAME] RESOURCEID, OVERBOOK, MODIFY_SCHEDULE, MODIFY_APPOINTMENTS, USERNAME, INTERNAL[USERNAME] USERID FROM BSDX_RESOURCE_USER"; // WHERE INTERNAL[INSTITUTION]=" + m_ConnectInfo.DUZ2;
747
748 if (!bAllUsers)
749 {
750 sCommandText += String.Format(" WHERE INTERNAL[USERNAME] = {0}", m_ConnectInfo.DUZ);
751 }
752
[614]753 ConnectInfo.RPMSDataTable(sCommandText, "ResourceUser", m_dsGlobal);
754 Debug.Write("LoadGlobalRecordsets -- ResourceUser loaded\n");
755 }
756
757
758 public void RegisterDocumentView(CGDocument doc, CGView view)
759 {
760 //Store the view in the list of views
761 this.Views.Add(view, doc);
762
763 //Hook into the view's 'closed' event
764 view.Closed += new EventHandler(ViewClosed);
765
766 //Hook into the view's mnuRPMSServer.Click event
767 view.mnuRPMSServer.Click += new EventHandler(mnuRPMSServer_Click);
768
769 //Hook into the view's mnuRPMSLogin.Click event
770 view.mnuRPMSLogin.Click += new EventHandler(mnuRPMSLogin_Click);
771
772 }
773
774 public void RegisterAVDocumentView(CGAVDocument doc, CGAVView view)
775 {
776 //Store the view in the list of views
777 this.AvailabilityViews.Add(view, doc);
778
779 //Hook into the view's 'closed' event
780 view.Closed += new EventHandler(AVViewClosed);
781 }
782
783 public CGAVView GetAVViewByResource(ArrayList sResourceArray)
784 {
785 if (sResourceArray == null)
786 return null;
787
788 bool bEqual = true;
789 foreach (CGAVView v in m_AVViews.Keys)
790 {
791 CGAVDocument d = v.Document;
792
793 bEqual = false;
794 if (d.Resources.Count == sResourceArray.Count)
795 {
796 bEqual = true;
797 for (int j = 0; j < sResourceArray.Count; j++)
798 {
799 if (sResourceArray.Contains(d.Resources[j]) == false)
800 {
801 bEqual = false;
802 break;
803 }
804 if (d.Resources.Contains(sResourceArray[j]) == false)
805 {
806 bEqual = false;
807 break;
808 }
809 }
810 if (bEqual == true)
811 return v;
812 }
813 }
814 return null;
815 }
816 /// <summary>
817 /// Return the first view having a resource array matching sResourceArray
818 /// </summary>
819 /// <param name="sResourceArray"></param>
820 /// <returns></returns>
821 public CGView GetViewByResource(ArrayList sResourceArray)
822 {
823 if (sResourceArray == null)
824 return null;
825
826 bool bEqual = true;
827 foreach (CGView v in _views.Keys)
828 {
829 CGDocument d = v.Document;
830
831 bEqual = false;
832 if (d.Resources.Count == sResourceArray.Count)
833 {
834 bEqual = true;
835 for (int j = 0; j < sResourceArray.Count; j++)
836 {
837 if (sResourceArray.Contains(d.Resources[j]) == false)
838 {
839 bEqual = false;
840 break;
841 }
842 if (d.Resources.Contains(sResourceArray[j]) == false)
843 {
844 bEqual = false;
845 break;
846 }
847 }
848 if (bEqual == true)
849 return v;
850 }
851 }
852 return null;
853 }
854
[1050]855 /// <summary>
856 /// Removes view and Handles Disconnection from Database if no views are left.
857 /// </summary>
858 /// <param name="sender"></param>
859 /// <param name="e"></param>
[614]860 private void ViewClosed(object sender, EventArgs e)
861 {
862 //Remove the sender from our document list
863 Views.Remove(sender);
864
865 //If no documents left, then close RPMS connection & exit the application
866 if ((Views.Count == 0)&&(this.AvailabilityViews.Count == 0)&&(m_bExitOK == true))
867 {
868 m_ConnectInfo.EventPollingEnabled = false;
869 m_ConnectInfo.UnSubscribeEvent("BSDX SCHEDULE");
870 m_ConnectInfo.CloseConnection();
871 Application.Exit();
872 }
873 }
874
875 private void AVViewClosed(object sender, EventArgs e)
876 {
877 //Remove the sender from our document list
878 this.AvailabilityViews.Remove(sender);
879
880 //If no documents left, then close RPMS connection & exit the application
881 if ((Views.Count == 0)&&(this.AvailabilityViews.Count == 0)&&(m_bExitOK == true))
882 {
883 m_ConnectInfo.bmxNetLib.CloseConnection();
884 Application.Exit();
885 }
886 }
887
888 private void KeepAlive()
889 {
890 foreach (CGView v in _views.Keys)
891 {
892 CGDocument d = v.Document;
893 DateTime dNow = DateTime.Now;
894 DateTime dLast = d.LastRefreshed;
895 TimeSpan tsDiff = dNow - dLast;
896 if (tsDiff.Seconds > 180)
897 {
898 for (int j = 0; j < d.Resources.Count; j++)
899 {
900 v.RaiseRPMSEvent("SCHEDULE-" + d.Resources[j].ToString(), "");
901 }
902
903 break;
904 }
905 }
906 }
907
908 /// <summary>
909 /// Propogate availability updates to all sRresource's doc/views
910 /// </summary>
911 public void UpdateViews(string sResource, string sOldResource)
912 {
913 if (sResource == null)
914 return;
915 foreach (CGView v in _views.Keys)
916 {
917 CGDocument d = v.Document;
918 for (int j = 0; j < d.Resources.Count; j++)
919 {
920 if ((sResource == "") || (sResource == ((string) d.Resources[j])) || (sOldResource == ((string) d.Resources[j])))
921 {
922 d.RefreshDocument();
923 break;
924 }
925 }
926 v.UpdateTree();
927 }
928 }
929
930 /// <summary>
931 /// Propogate availability updates to all doc/views
932 /// </summary>
933 public void UpdateViews()
934 {
935 UpdateViews("","");
936 foreach (CGView v in _views.Keys)
937 {
938 v.UpdateTree();
939 }
940 }
941
942 /// <summary>
943 /// Calls each view associated with document Doc and closes it.
944 /// </summary>
945 public void CloseAllViews(CGDocument doc)
946 {
947 //iterate through all views and call update.
948 Hashtable h = CGDocumentManager.Current.Views;
949
950 CGDocument d;
951 int nTempCount = h.Count;
952 do
953 {
954 nTempCount = h.Count;
955 foreach (CGView v in h.Keys)
956 {
957 d = (CGDocument) h[v];
958 if (d == doc)
959 {
960 v.Close();
961 break;
962 }
963 }
964 } while ((h.Count > 0) && (nTempCount != h.Count));
965 }
966
967 /// <summary>
968 /// Calls each view associated with Availability Doc and closes it.
969 /// </summary>
970 public void CloseAllViews(CGAVDocument doc)
971 {
972 //iterate through all views and call update.
973 Hashtable h = CGDocumentManager.Current.AvailabilityViews;
974
975 CGAVDocument d;
976 int nTempCount = h.Count;
977 do
978 {
979 nTempCount = h.Count;
980 foreach (CGAVView v in h.Keys)
981 {
982 d = (CGAVDocument) h[v];
983 if (d == doc)
984 {
985 v.Close();
986 break;
987 }
988 }
989 } while ((h.Count > 0) && (nTempCount != h.Count));
990
991
992 }
993
[1071]994 /// <summary>
995 /// Accomplishes Changing the Server to which you connect
996 /// </summary>
997 /// <remarks>
998 /// Parameter relog-in for InitializeApp forces initialize app to use
999 /// 1. The server the user just picked and then BMX saved off to User Preferences
1000 /// 2. A new access and verify code pair
1001 /// </remarks>
1002 /// <param name="sender">unused</param>
1003 /// <param name="e">unused</param>
[614]1004 private void mnuRPMSServer_Click(object sender, EventArgs e)
1005 {
1006 //Warn that changing servers will close all schedules
[627]1007 if (MessageBox.Show("Are you sure you want to close all schedules and connect to a different VistA server?", "Clinical Scheduling", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) != DialogResult.OK)
[614]1008 return;
1009
1010 //Reconnect to RPMS and recreate all global recordsets
1011 try
1012 {
[1071]1013 // Close All, but tell the Close All method not to call Applicaiton.Exit since we still plan to continue.
1014 // Close All does not call Application.Exit, but CGView_Close handler does
[614]1015 m_bExitOK = false;
[1071]1016 CloseAll();
1017 m_bExitOK = true;
1018
1019 //Used in Do loop
1020 bool bRetry = true;
1021
1022 // Do Loop to deal with changing the server and the vagaries of user choices.
[614]1023 do
1024 {
1025 try
1026 {
[1071]1027 //ChangeServerInfo does not re-login the user
1028 //It only changes the saved server information in the %APPDATA% folder
1029 //so it can be re-used when BMX tries to log in again.
1030 //Access and Verify code are prompted for in InitializeApp
1031 m_ConnectInfo.ChangeServerInfo();
[614]1032 bRetry = false;
1033 }
1034 catch (Exception ex)
1035 {
1036 if (ex.Message == "User cancelled.")
1037 {
1038 bRetry = false;
[1071]1039 Application.Exit();
[614]1040 return;
1041 }
[627]1042 if (MessageBox.Show("Unable to connect to VistA. " + ex.Message , "Clinical Scheduling", MessageBoxButtons.RetryCancel) == DialogResult.Retry)
[614]1043 {
1044 bRetry = true;
1045 }
1046 else
1047 {
1048 bRetry = false;
[1071]1049 Application.Exit();
[614]1050 return;
1051 }
1052 }
1053 } while (bRetry == true);
1054
[1071]1055 //Parameter for initialize app tells it that this is a re-login and forces a new access and verify code.
1056 bool isEverythingOkay = this.InitializeApp(true);
[614]1057
[1071]1058 //if an error occurred, break out. This time we need to call Application.Exit since it's already running.
1059 if (!isEverythingOkay)
1060 {
1061 Application.Exit();
1062 return;
1063 }
[614]1064
[1071]1065 //Otherwise, everything is okay. So open document and view, then show and activate view.
1066 CGDocument doc = new CGDocument();
1067 doc.DocManager = _current;
[614]1068
[1071]1069 CGView view = new CGView();
[1073]1070 view.InitializeDocView(doc, _current, doc.StartDate, _current.WindowText);
[1071]1071
1072 view.Show();
1073 view.Activate();
1074
1075 //Application.Run need not be called b/c it is already running.
[614]1076 }
1077 catch (Exception ex)
1078 {
1079 throw ex;
1080 }
1081
1082 }
1083
[1071]1084 /// <summary>
1085 /// Accomplishes Re-login into RPMS/VISTA. Now all logic is in this event handler.
1086 /// </summary>
1087 /// <param name="sender">not used</param>
1088 /// <param name="e">not used</param>
[614]1089 private void mnuRPMSLogin_Click(object sender, EventArgs e)
1090 {
1091 //Warn that changing login will close all schedules
[627]1092 if (MessageBox.Show("Are you sure you want to close all schedules and login to VistA?", "Clinical Scheduling", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) != DialogResult.OK)
[614]1093 return;
1094
1095 //Reconnect to RPMS and recreate all global recordsets
1096 try
1097 {
[1071]1098 // Close All, but tell the Close All method not to call Applicaiton.Exit since we still plan to continue.
1099 // Close All does not call Application.Exit, but CGView_Close handler does
[614]1100 m_bExitOK = false;
1101 CloseAll();
1102 m_bExitOK = true;
[1071]1103
1104 //Parameter for initialize app tells it that this is a re-login and forces a new access and verify code.
1105 bool isEverythingOkay = this.InitializeApp(true);
1106
1107 //if an error occurred, break out. This time we need to call Application.Exit since it's already running.
1108 if (!isEverythingOkay)
1109 {
1110 Application.Exit();
1111 return;
1112 }
1113
1114 //Otherwise, everything is okay. So open document and view, then show and activate view.
1115 CGDocument doc = new CGDocument();
1116 doc.DocManager = _current;
1117
1118 CGView view = new CGView();
[1073]1119 view.InitializeDocView(doc, _current, doc.StartDate, _current.WindowText);
[1071]1120
1121 view.Show();
1122 view.Activate();
1123
1124 //Application.Run need not be called b/c it is already running.
[614]1125 }
1126 catch (Exception ex)
1127 {
1128 throw ex;
1129 }
1130
1131 }
1132
1133 delegate void CloseAllDelegate(string sMsg);
1134
1135 private void CloseAll(string sMsg)
1136 {
1137 if (sMsg == "")
1138 {
1139 sMsg = "Scheduling System Shutting Down Immediately for Maintenance.";
1140 }
1141
[620]1142 MessageBox.Show(sMsg, "Clinical Scheduling Administrator -- System Shutdown Notification", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
[614]1143
1144 CloseAll();
1145 }
1146
1147 private void CloseAll()
1148 {
1149 //Close all documents, views and connections
1150 Hashtable h = CGDocumentManager.Current.Views;
1151 int nTempCount = h.Count;
1152 do
1153 {
1154 nTempCount = h.Count;
1155 foreach (CGView v in h.Keys)
1156 {
1157 v.Close();
1158 break;
1159 }
1160 } while ((h.Count > 0) && (nTempCount != h.Count));
1161
1162 h = CGDocumentManager.Current.AvailabilityViews;
1163 nTempCount = h.Count;
1164 do
1165 {
1166 nTempCount = h.Count;
1167 foreach (CGAVView v in h.Keys)
1168 {
1169 v.Close();
1170 break;
1171 }
1172 } while ((h.Count > 0) && (nTempCount != h.Count));
1173
1174 }
1175
[1011]1176 public delegate DataTable RPMSDataTableDelegate(string CommandString, string TableName);
[614]1177
1178 public DataTable RPMSDataTable(string sSQL, string sTableName)
1179 {
1180 //Retrieves a recordset from RPMS
1181 string sErrorMessage = "";
[843]1182 DataTable dtOut;
1183
[614]1184 try
1185 {
[1011]1186 //System.IntPtr pHandle = this.Handle;
[614]1187 RPMSDataTableDelegate rdtd = new RPMSDataTableDelegate(ConnectInfo.RPMSDataTable);
[1050]1188 //dtOut = (DataTable) this.Invoke(rdtd, new object[] {sSQL, sTableName});
1189 dtOut = rdtd.Invoke(sSQL, sTableName);
[614]1190 }
[843]1191
[614]1192 catch (Exception ex)
1193 {
1194 sErrorMessage = "CGDocumentManager.RPMSDataTable error: " + ex.Message;
1195 throw ex;
1196 }
[843]1197
1198 return dtOut;
1199
[614]1200 }
1201
1202 public void ChangeDivision(System.Windows.Forms.Form frmCaller)
1203 {
1204 this.ConnectInfo.ChangeDivision(frmCaller);
1205 foreach (CGView v in _views.Keys)
1206 {
1207 v.InitializeDocView(v.Document.DocName);
1208 v.Document.RefreshDocument();
1209 }
1210 }
1211
1212 public void ViewRefresh()
1213 {
1214 foreach (CGView v in _views.Keys)
1215 {
1216 try
1217 {
1218 v.Document.RefreshDocument();
1219 }
1220 catch (Exception ex)
1221 {
1222 Debug.Write("CGDocumentManager.ViewRefresh Exception: " + ex.Message + "\n");
1223 }
1224 finally
1225 {
1226 }
1227 }
1228 Debug.Write("DocManager refreshed all views.\n");
1229 }
1230
1231 #endregion Methods & Events
1232
1233 }
1234}
Note: See TracBrowser for help on using the repository browser.