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

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

Added (maybe) better uncaught Exception handling. We'll see if it works at all.
Release exes, again.

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