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

Last change on this file since 1112 was 1112, checked in by Sam Habiel, 13 years ago

CGDocument: Minor Change: Add appointment ID to Appointment
CGDocumentManager: added parameter /culture to set Thread.CurrentThread.CurrentUICulture
CGView: Minor changes in CheckInAppointment: Printing of Routing Slip now always happens.

Routing slip now get parameter for patient order.

Patient: i18n of UserFriendlyAge
Printing: Extensive changes. Support for i18n; new routing slip and appt slip. i18n support only for appt slip.

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