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

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

CGAVDocument: Don't Update Grid. Leave that to CGAVView.
CGAVView: When updating grid, make a copy of appointments, and use the copy, so there won't be clashes when updating appointments in the background.
CGDocument: Minor Changes in AppointmentNoShow; New method AppointmentUndoCheckIn
CGDocumentManager: Resequenced load order b/c UserPreferences now talks to DB.
DAL: New Methods to save autoprint preferences; new delegate to enable some async stuff
DCheckIn & DAppointPage: _myCodeIsFiringIstheCheckBoxChangedEvent added to protect against firing event when setting checkbox in the code.
DPatientLetter: No changes.
UserPreferences: Now gets and saves user preferences from DB.

File size: 46.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#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 21 steps to load the application. That's max for the progress bar.
357 setMaxProgressDelegate(21);
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
475
476 //User Interface Culture (m_CultureName is set from the command line flag /culture)
477 try { Thread.CurrentThread.CurrentUICulture = new CultureInfo(m_CultureName); } // if "", invariant culture
478 catch (CultureNotFoundException) { Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; }
479
480 //Create global dataset
481 _current.m_dsGlobal = new DataSet("GlobalDataSet");
482
483 //Version info
484 // Table #1
485 setProgressDelegate(1);
486 setStatusDelegate("Getting Version Info from Server...");
487
488 DataTable ver = _dal.GetVersion("BSDX");
489 ver.TableName = "VersionInfo";
490 m_dsGlobal.Tables.Add(ver);
491
492 //How to extract the version numbers:
493 DataTable dtVersion = m_dsGlobal.Tables["VersionInfo"];
494 Debug.Assert(dtVersion.Rows.Count == 1);
495 DataRow rVersion = dtVersion.Rows[0];
496 string sMajor = rVersion["MAJOR_VERSION"].ToString();
497 string sMinor = rVersion["MINOR_VERSION"].ToString();
498 string sBuild = rVersion["BUILD"].ToString();
499 decimal fBuild = Convert.ToDecimal(sBuild);
500
501 //Make sure that the server is running the same version the client is.
502 Version x = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
503
504 //if version numbers mismatch, don't continue.
505 //TODO: For future: Include in v. 1.5
506 /*
507 if (!(x.Major.ToString() == sMajor && x.Minor.ToString() + x.Build.ToString() == sMinor))
508 {
509 MessageBox.Show(
510 "Server runs version " + sMajor + "." + sMinor + "\r\n" +
511 "You are running " + x.ToString() + "\r\n\r\n" +
512 "Major, Minor and Build versions must match",
513 "Version Mismatch");
514 m_ds.Close();
515 return;
516 }
517 */
518
519
520 //Change encoding
521 // Call #2
522 setProgressDelegate(2);
523 setStatusDelegate("Setting encoding...");
524
525 if (m_Encoding == String.Empty)
526 {
527 string utf8_server_support = m_ConnectInfo.bmxNetLib.TransmitRPC("BMX UTF-8", "");
528 if (utf8_server_support == "1")
529 m_ConnectInfo.bmxNetLib.Encoder = System.Text.UTF8Encoding.UTF8;
530 }
531
532 //Set application context
533 // Call #3
534 setProgressDelegate(3);
535 setStatusDelegate("Setting Application Context to BSDXRPC...");
536 m_ConnectInfo.AppContext = "BSDXRPC";
537
538 //User Preferences Object
539 setProgressDelegate(4); //next number is 6 b/c two calls
540 setStatusDelegate("Getting User Preferences from the Server...");
541
542 _current.UserPreferences = new UserPreferences(); // Does the calling to do that...
543
544 //Load global recordsets
545 string statusConst = "Loading VistA data tables...";
546 setStatusDelegate(statusConst);
547
548 string sCommandText;
549
550 //Schedule User Info
551 // Table #4
552 setProgressDelegate(6);
553 setStatusDelegate(statusConst + " Schedule User");
554 DataTable dtUser = _dal.GetUserInfo(m_ConnectInfo.DUZ);
555 dtUser.TableName = "SchedulingUser";
556 m_dsGlobal.Tables.Add(dtUser);
557 Debug.Assert(dtUser.Rows.Count == 1);
558
559 // Only one row and one column named "MANAGER". Set local var m_bSchedManager to true if Manager.
560 DataRow rUser = dtUser.Rows[0];
561 Object oUser = rUser["MANAGER"];
562 string sUser = oUser.ToString();
563 m_bSchedManager = (sUser == "YES") ? true : false;
564
565 //Get Access Types
566 // Table #5
567 setProgressDelegate(7);
568 setStatusDelegate(statusConst + " Access Types");
569 DataTable dtAccessTypes = _dal.GetAccessTypes();
570 dtAccessTypes.TableName = "AccessTypes";
571 m_dsGlobal.Tables.Add(dtAccessTypes);
572
573 //Get Access Groups
574 // Table #6
575 setProgressDelegate(8);
576 setStatusDelegate(statusConst + " Access Groups");
577 LoadAccessGroupsTable();
578
579 //Build Primary Key for AccessGroup table
580 DataTable dtGroups = m_dsGlobal.Tables["AccessGroup"];
581 DataColumn dcKey = dtGroups.Columns["ACCESS_GROUP"];
582 DataColumn[] dcKeys = new DataColumn[1];
583 dcKeys[0] = dcKey;
584 dtGroups.PrimaryKey = dcKeys;
585
586 //Get Access Group Types (Combines Access Types and Groups)
587 //Optimization Note: Can eliminate Access type and Access Group Table
588 // But they are heavily referenced throughout the code.
589 // Table #7
590 setProgressDelegate(9);
591 setStatusDelegate(statusConst + " Access Group Types");
592 LoadAccessGroupTypesTable();
593
594 //Build Primary Key for AccessGroupType table
595 DataTable dtAGTypes = m_dsGlobal.Tables["AccessGroupType"];
596 DataColumn dcGTKey = dtAGTypes.Columns["ACCESS_GROUP_TYPEID"];
597 DataColumn[] dcGTKeys = new DataColumn[1];
598 dcGTKeys[0] = dcGTKey;
599 dtAGTypes.PrimaryKey = dcGTKeys;
600
601 //Build Data Relationship between AccessGroupType and AccessTypes tables
602 DataRelation dr = new DataRelation("AccessGroupType", //Relation Name
603 m_dsGlobal.Tables["AccessGroup"].Columns["BMXIEN"], //Parent
604 m_dsGlobal.Tables["AccessGroupType"].Columns["ACCESS_GROUP_ID"]); //Child
605 m_dsGlobal.Relations.Add(dr);
606
607 //ResourceGroup Table (Resource Groups by User)
608 // Table #8
609 // What shows up on the tree. The groups the user has access to.
610 setProgressDelegate(10);
611 setStatusDelegate(statusConst + " Resource Groups By User");
612 LoadResourceGroupTable();
613
614 //Resources by user
615 // Table #9
616 // Individual Resources
617 setProgressDelegate(11);
618 setStatusDelegate(statusConst + " Resources By User");
619 LoadBSDXResourcesTable();
620
621 //Build Primary Key for Resources table
622 DataColumn[] dc = new DataColumn[1];
623 dc[0] = m_dsGlobal.Tables["Resources"].Columns["RESOURCEID"];
624 m_dsGlobal.Tables["Resources"].PrimaryKey = dc;
625
626 //GroupResources table
627 // Table #10
628 // Resource Groups and Indivdual Resources together
629 setProgressDelegate(12);
630 setStatusDelegate(statusConst + " Group Resources");
631 LoadGroupResourcesTable();
632
633 //Build Primary Key for ResourceGroup table
634 dc = new DataColumn[1];
635 dc[0] = m_dsGlobal.Tables["ResourceGroup"].Columns["RESOURCE_GROUP"];
636 m_dsGlobal.Tables["ResourceGroup"].PrimaryKey = dc;
637
638 //Build Data Relationships between ResourceGroup and GroupResources tables
639 dr = new DataRelation("GroupResource", //Relation Name
640 m_dsGlobal.Tables["ResourceGroup"].Columns["RESOURCE_GROUP"], //Parent
641 m_dsGlobal.Tables["GroupResources"].Columns["RESOURCE_GROUP"]); //Child
642
643 m_dsGlobal.Relations.Add(dr);
644
645 //HospitalLocation table
646 //Table #11
647 setProgressDelegate(13);
648 setStatusDelegate(statusConst + " Clinics");
649 //cmd.CommandText = "SELECT BMXIEN 'HOSPITAL_LOCATION_ID', NAME 'HOSPITAL_LOCATION', DEFAULT_PROVIDER, STOP_CODE_NUMBER, INACTIVATE_DATE, REACTIVATE_DATE FROM HOSPITAL_LOCATION";
650 sCommandText = "BSDX HOSPITAL LOCATION";
651 ConnectInfo.RPMSDataTable(sCommandText, "HospitalLocation", m_dsGlobal);
652 Debug.Write("LoadGlobalRecordsets -- HospitalLocation loaded\n");
653
654 //Build Primary Key for HospitalLocation table
655 dc = new DataColumn[1];
656 DataTable dtTemp = m_dsGlobal.Tables["HospitalLocation"];
657 dc[0] = dtTemp.Columns["HOSPITAL_LOCATION_ID"];
658 m_dsGlobal.Tables["HospitalLocation"].PrimaryKey = dc;
659
660 //Build Data Relationships between Resources and HospitalLocation tables
661 dr = new DataRelation("HospitalLocationResource", //Relation Name
662 m_dsGlobal.Tables["HospitalLocation"].Columns["HOSPITAL_LOCATION_ID"], //Parent
663 m_dsGlobal.Tables["Resources"].Columns["HOSPITAL_LOCATION_ID"], false); //Child
664 m_dsGlobal.Relations.Add(dr);
665
666 //Build ScheduleUser table
667 //Table #12
668 setProgressDelegate(14);
669 setStatusDelegate(statusConst + " Schedule User");
670 this.LoadScheduleUserTable();
671
672 //Build Primary Key for ScheduleUser table
673 dc = new DataColumn[1];
674 dtTemp = m_dsGlobal.Tables["ScheduleUser"];
675 dc[0] = dtTemp.Columns["USERID"];
676 m_dsGlobal.Tables["ScheduleUser"].PrimaryKey = dc;
677
678 //Build ResourceUser table
679 //Table #13
680 //Acess to Resources by [this] User
681 setProgressDelegate(15);
682 setStatusDelegate(statusConst + " Resource User");
683 this.LoadResourceUserTable();
684
685 //Build Primary Key for ResourceUser table
686 dc = new DataColumn[1];
687 dtTemp = m_dsGlobal.Tables["ResourceUser"];
688 dc[0] = dtTemp.Columns["RESOURCEUSER_ID"];
689 m_dsGlobal.Tables["ResourceUser"].PrimaryKey = dc;
690
691 //Create relation between BSDX Resource and BSDX Resource User tables
692 dr = new DataRelation("ResourceUser", //Relation Name
693 m_dsGlobal.Tables["Resources"].Columns["RESOURCEID"], //Parent
694 m_dsGlobal.Tables["ResourceUser"].Columns["RESOURCEID"]); //Child
695 m_dsGlobal.Relations.Add(dr);
696
697 //Build active provider table
698 //Table #14
699 //TODO: Lazy load the provider table; no need to load in advance.
700 setProgressDelegate(16);
701 setStatusDelegate(statusConst + " Providers");
702 sCommandText = "SELECT BMXIEN, NAME FROM NEW_PERSON WHERE INACTIVE_DATE = '' AND BMXIEN > 1";
703 ConnectInfo.RPMSDataTable(sCommandText, "Provider", m_dsGlobal);
704 Debug.Write("LoadGlobalRecordsets -- Provider loaded\n");
705
706 //Build the HOLIDAY table
707 //Table #15
708 setProgressDelegate(17);
709 setStatusDelegate(statusConst + " Holiday");
710 sCommandText = "SELECT NAME, DATE FROM HOLIDAY WHERE INTERNAL[DATE] > '" + FMDateTime.Create(DateTime.Today).DateOnly.FMDateString + "'";
711 ConnectInfo.RPMSDataTable(sCommandText, "HOLIDAY", m_dsGlobal);
712 Debug.Write("LoadingGlobalRecordsets -- Holidays loaded\n");
713
714
715 //Save the xml schema
716 //m_dsGlobal.WriteXmlSchema(@"..\..\csSchema20060526.xsd");
717 //----------------------------------------------
718
719 setStatusDelegate("Setting Receive Timeout");
720 _current.m_ConnectInfo.ReceiveTimeout = 30000; //30-second timeout
721
722#if DEBUG
723 _current.m_ConnectInfo.ReceiveTimeout = 600000; //longer timeout for debugging
724#endif
725 // Event Subsriptions
726 setStatusDelegate("Subscribing to Server Events");
727 //Table #16
728 setProgressDelegate(18);
729 _current.m_ConnectInfo.SubscribeEvent("BSDX SCHEDULE");
730 //Table #17
731 setProgressDelegate(19);
732 _current.m_ConnectInfo.SubscribeEvent("BSDX CALL WORKSTATIONS");
733 //Table #18
734 setProgressDelegate(20);
735 _current.m_ConnectInfo.SubscribeEvent("BSDX ADMIN MESSAGE");
736 //Table #19
737 setProgressDelegate(21);
738 _current.m_ConnectInfo.SubscribeEvent("BSDX ADMIN SHUTDOWN");
739
740 _current.m_ConnectInfo.EventPollingInterval = 5000; //in milliseconds
741 _current.m_ConnectInfo.EventPollingEnabled = true;
742 _current.m_ConnectInfo.AutoFire = 12; //AutoFire every 12*5 seconds
743
744 //Close Splash Screen
745 closeSplashDelegate();
746
747 return true;
748
749 }
750
751
752
753 public void LoadAccessGroupsTable()
754 {
755 string sCommandText = "SELECT * FROM BSDX_ACCESS_GROUP";
756 ConnectInfo.RPMSDataTable(sCommandText, "AccessGroup", m_dsGlobal);
757 Debug.Write("LoadGlobalRecordsets -- AccessGroups loaded\n");
758 }
759
760 public void LoadAccessGroupTypesTable()
761 {
762 string sCommandText = "BSDX GET ACCESS GROUP TYPES";
763 ConnectInfo.RPMSDataTable(sCommandText, "AccessGroupType", m_dsGlobal);
764 Debug.Write("LoadGlobalRecordsets -- AccessGroupTypes loaded\n");
765 }
766
767 public void LoadBSDXResourcesTable()
768 {
769 string sCommandText = "BSDX RESOURCES^" + m_ConnectInfo.DUZ;
770 ConnectInfo.RPMSDataTable(sCommandText, "Resources", m_dsGlobal);
771 Debug.Write("LoadGlobalRecordsets -- Resources loaded\n");
772 }
773
774 public void LoadResourceGroupTable()
775 {
776 //ResourceGroup Table (Resource Groups by User)
777 //Table "ResourceGroup" contains all resource group names
778 //to which user has access
779 //Fields are: RESOURCE_GROUPID, RESOURCE_GROUP
780 string sCommandText = "BSDX RESOURCE GROUPS BY USER^" + m_ConnectInfo.DUZ;
781 ConnectInfo.RPMSDataTable(sCommandText, "ResourceGroup", m_dsGlobal);
782 Debug.Write("LoadGlobalRecordsets -- ResourceGroup loaded\n");
783 }
784
785 public void LoadGroupResourcesTable()
786 {
787 //Table "GroupResources" contains all active GROUP/RESOURCE combinations
788 //to which user has access based on entries in BSDX RESOURCE USER file
789 //If user has BSDXZMGR or XUPROGMODE keys, then ALL Group/Resource combinstions
790 //are returned.
791 //Fields are: RESOURCE_GROUPID, RESOURCE_GROUP, RESOURCE_GROUP_ITEMID, RESOURCE_NAME, RESOURCE_ID
792 string sCommandText = "BSDX GROUP RESOURCE^" + m_ConnectInfo.DUZ;
793 ConnectInfo.RPMSDataTable(sCommandText, "GroupResources", m_dsGlobal);
794 Debug.Write("LoadGlobalRecordsets -- GroupResources loaded\n");
795 }
796
797 public void LoadScheduleUserTable()
798 {
799 //Table "ScheduleUser" contains an entry for each user in File 200 (NEW PERSON)
800 //who possesses the BSDXZMENU security key.
801 string sCommandText = "BSDX SCHEDULE USER";
802 ConnectInfo.RPMSDataTable(sCommandText, "ScheduleUser", m_dsGlobal);
803 Debug.Write("LoadGlobalRecordsets -- ScheduleUser loaded\n");
804 }
805
806 public void LoadResourceUserTable()
807 {
808 //Table "ResourceUser" duplicates the BSDX RESOURCE USER File.
809 //NOTE: Column names are RESOURCEUSER_ID, RESOURCEID,
810 // OVERBOOK, MODIFY_SCHEDULE, USERID, USERID1
811 //string sCommandText = "SELECT BMXIEN RESOURCEUSER_ID, INTERNAL[RESOURCENAME] RESOURCEID, OVERBOOK, MODIFY_SCHEDULE, USERNAME USERID, INTERNAL[USERNAME] FROM BSDX_RESOURCE_USER";
812 LoadResourceUserTable(false);
813 }
814
815 public void LoadResourceUserTable(bool bAllUsers)
816 {
817 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;
818
819 if (!bAllUsers)
820 {
821 sCommandText += String.Format(" WHERE INTERNAL[USERNAME] = {0}", m_ConnectInfo.DUZ);
822 }
823
824 ConnectInfo.RPMSDataTable(sCommandText, "ResourceUser", m_dsGlobal);
825 Debug.Write("LoadGlobalRecordsets -- ResourceUser loaded\n");
826 }
827
828
829 public void RegisterDocumentView(CGDocument doc, CGView view)
830 {
831 //Store the view in the list of views
832 this.Views.Add(view, doc);
833
834 //Hook into the view's 'closed' event
835 view.Closed += new EventHandler(ViewClosed);
836
837 //Hook into the view's mnuRPMSServer.Click event
838 view.mnuRPMSServer.Click += new EventHandler(mnuRPMSServer_Click);
839
840 //Hook into the view's mnuRPMSLogin.Click event
841 view.mnuRPMSLogin.Click += new EventHandler(mnuRPMSLogin_Click);
842
843 }
844
845 public void RegisterAVDocumentView(CGAVDocument doc, CGAVView view)
846 {
847 //Store the view in the list of views
848 this.AvailabilityViews.Add(view, doc);
849
850 //Hook into the view's 'closed' event
851 view.Closed += new EventHandler(AVViewClosed);
852 }
853
854 public CGAVView GetAVViewByResource(ArrayList sResourceArray)
855 {
856 if (sResourceArray == null)
857 return null;
858
859 bool bEqual = true;
860 foreach (CGAVView v in m_AVViews.Keys)
861 {
862 CGAVDocument d = v.Document;
863
864 bEqual = false;
865 if (d.Resources.Count == sResourceArray.Count)
866 {
867 bEqual = true;
868 for (int j = 0; j < sResourceArray.Count; j++)
869 {
870 if (sResourceArray.Contains(d.Resources[j]) == false)
871 {
872 bEqual = false;
873 break;
874 }
875 if (d.Resources.Contains(sResourceArray[j]) == false)
876 {
877 bEqual = false;
878 break;
879 }
880 }
881 if (bEqual == true)
882 return v;
883 }
884 }
885 return null;
886 }
887 /// <summary>
888 /// Return the first view having a resource array matching sResourceArray
889 /// </summary>
890 /// <param name="sResourceArray"></param>
891 /// <returns></returns>
892 public CGView GetViewByResource(ArrayList sResourceArray)
893 {
894 if (sResourceArray == null)
895 return null;
896
897 bool bEqual = true;
898 foreach (CGView v in _views.Keys)
899 {
900 CGDocument d = v.Document;
901
902 bEqual = false;
903 if (d.Resources.Count == sResourceArray.Count)
904 {
905 bEqual = true;
906 for (int j = 0; j < sResourceArray.Count; j++)
907 {
908 if (sResourceArray.Contains(d.Resources[j]) == false)
909 {
910 bEqual = false;
911 break;
912 }
913 if (d.Resources.Contains(sResourceArray[j]) == false)
914 {
915 bEqual = false;
916 break;
917 }
918 }
919 if (bEqual == true)
920 return v;
921 }
922 }
923 return null;
924 }
925
926 /// <summary>
927 /// Removes view and Handles Disconnection from Database if no views are left.
928 /// </summary>
929 /// <param name="sender"></param>
930 /// <param name="e"></param>
931 private void ViewClosed(object sender, EventArgs e)
932 {
933 //Remove the sender from our document list
934 Views.Remove(sender);
935
936 //If no documents left, then close RPMS connection & exit the application
937 if ((Views.Count == 0)&&(this.AvailabilityViews.Count == 0)&&(m_bExitOK == true))
938 {
939 m_ConnectInfo.EventPollingEnabled = false;
940 m_ConnectInfo.UnSubscribeEvent("BSDX SCHEDULE");
941 m_ConnectInfo.CloseConnection();
942 Application.Exit();
943 }
944 }
945
946 private void AVViewClosed(object sender, EventArgs e)
947 {
948 //Remove the sender from our document list
949 this.AvailabilityViews.Remove(sender);
950
951 //If no documents left, then close RPMS connection & exit the application
952 if ((Views.Count == 0)&&(this.AvailabilityViews.Count == 0)&&(m_bExitOK == true))
953 {
954 m_ConnectInfo.bmxNetLib.CloseConnection();
955 Application.Exit();
956 }
957 }
958
959 /// <summary>
960 /// Not used
961 /// </summary>
962 private void KeepAlive()
963 {
964 foreach (CGView v in _views.Keys)
965 {
966 CGDocument d = v.Document;
967 DateTime dNow = DateTime.Now;
968 DateTime dLast = d.LastRefreshed;
969 TimeSpan tsDiff = dNow - dLast;
970 if (tsDiff.Seconds > 180)
971 {
972 for (int j = 0; j < d.Resources.Count; j++)
973 {
974 v.RaiseRPMSEvent("SCHEDULE-" + d.Resources[j].ToString(), "");
975 }
976
977 break;
978 }
979 }
980 }
981
982 /// <summary>
983 /// Propogate availability updates to all sRresource's doc/views
984 /// </summary>
985 public void UpdateViews(string sResource, string sOldResource)
986 {
987 if (sResource == null)
988 return;
989 foreach (CGView v in _views.Keys)
990 {
991 CGDocument d = v.Document;
992 for (int j = 0; j < d.Resources.Count; j++)
993 {
994 if ((sResource == "") || (sResource == ((string) d.Resources[j])) || (sOldResource == ((string) d.Resources[j])))
995 {
996 d.RefreshDocument();
997 break;
998 }
999 }
1000 v.UpdateTree();
1001 }
1002 }
1003
1004 /// <summary>
1005 /// Propogate availability updates to all doc/views
1006 /// </summary>
1007 public void UpdateViews()
1008 {
1009 UpdateViews("","");
1010 foreach (CGView v in _views.Keys)
1011 {
1012 v.UpdateTree();
1013 }
1014 }
1015
1016 /// <summary>
1017 /// Calls each view associated with document Doc and closes it.
1018 /// </summary>
1019 public void CloseAllViews(CGDocument doc)
1020 {
1021 //iterate through all views and call update.
1022 Hashtable h = CGDocumentManager.Current.Views;
1023
1024 CGDocument d;
1025 int nTempCount = h.Count;
1026 do
1027 {
1028 nTempCount = h.Count;
1029 foreach (CGView v in h.Keys)
1030 {
1031 d = (CGDocument) h[v];
1032 if (d == doc)
1033 {
1034 v.Close();
1035 break;
1036 }
1037 }
1038 } while ((h.Count > 0) && (nTempCount != h.Count));
1039 }
1040
1041 /// <summary>
1042 /// Calls each view associated with Availability Doc and closes it.
1043 /// </summary>
1044 public void CloseAllViews(CGAVDocument doc)
1045 {
1046 //iterate through all views and call update.
1047 Hashtable h = CGDocumentManager.Current.AvailabilityViews;
1048
1049 CGAVDocument d;
1050 int nTempCount = h.Count;
1051 do
1052 {
1053 nTempCount = h.Count;
1054 foreach (CGAVView v in h.Keys)
1055 {
1056 d = (CGAVDocument) h[v];
1057 if (d == doc)
1058 {
1059 v.Close();
1060 break;
1061 }
1062 }
1063 } while ((h.Count > 0) && (nTempCount != h.Count));
1064
1065
1066 }
1067
1068 /// <summary>
1069 /// Accomplishes Changing the Server to which you connect
1070 /// </summary>
1071 /// <remarks>
1072 /// Parameter relog-in for InitializeApp forces initialize app to use
1073 /// 1. The server the user just picked and then BMX saved off to User Preferences
1074 /// 2. A new access and verify code pair
1075 /// </remarks>
1076 /// <param name="sender">unused</param>
1077 /// <param name="e">unused</param>
1078 private void mnuRPMSServer_Click(object sender, EventArgs e)
1079 {
1080 //Warn that changing servers will close all schedules
1081 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)
1082 return;
1083
1084 //Reconnect to RPMS and recreate all global recordsets
1085 try
1086 {
1087 // Close All, but tell the Close All method not to call Applicaiton.Exit since we still plan to continue.
1088 // Close All does not call Application.Exit, but CGView_Close handler does
1089 m_bExitOK = false;
1090 CloseAll();
1091 m_bExitOK = true;
1092
1093 //Used in Do loop
1094 bool bRetry = true;
1095
1096 // Do Loop to deal with changing the server and the vagaries of user choices.
1097 do
1098 {
1099 try
1100 {
1101 //ChangeServerInfo does not re-login the user
1102 //It only changes the saved server information in the %APPDATA% folder
1103 //so it can be re-used when BMX tries to log in again.
1104 //Access and Verify code are prompted for in InitializeApp
1105 m_ConnectInfo.ChangeServerInfo();
1106 bRetry = false;
1107 }
1108 catch (Exception ex)
1109 {
1110 if (ex.Message == "User cancelled.")
1111 {
1112 bRetry = false;
1113 Application.Exit();
1114 return;
1115 }
1116 if (MessageBox.Show("Unable to connect to VistA. " + ex.Message , "Clinical Scheduling", MessageBoxButtons.RetryCancel) == DialogResult.Retry)
1117 {
1118 bRetry = true;
1119 }
1120 else
1121 {
1122 bRetry = false;
1123 Application.Exit();
1124 return;
1125 }
1126 }
1127 } while (bRetry == true);
1128
1129 //Parameter for initialize app tells it that this is a re-login and forces a new access and verify code.
1130 bool isEverythingOkay = this.InitializeApp(true);
1131
1132 //if an error occurred, break out. This time we need to call Application.Exit since it's already running.
1133 if (!isEverythingOkay)
1134 {
1135 Application.Exit();
1136 return;
1137 }
1138
1139 //Otherwise, everything is okay. So open document and view, then show and activate view.
1140 CGDocument doc = new CGDocument();
1141 doc.DocManager = _current;
1142
1143 CGView view = new CGView();
1144 view.InitializeDocView(doc, _current, doc.StartDate, _current.WindowText);
1145
1146 view.Show();
1147 view.Activate();
1148
1149 //Application.Run need not be called b/c it is already running.
1150 }
1151 catch (Exception ex)
1152 {
1153 throw ex;
1154 }
1155
1156 }
1157
1158 /// <summary>
1159 /// Accomplishes Re-login into RPMS/VISTA. Now all logic is in this event handler.
1160 /// </summary>
1161 /// <param name="sender">not used</param>
1162 /// <param name="e">not used</param>
1163 private void mnuRPMSLogin_Click(object sender, EventArgs e)
1164 {
1165 //Warn that changing login will close all schedules
1166 if (MessageBox.Show("Are you sure you want to close all schedules and login to VistA?", "Clinical Scheduling", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) != DialogResult.OK)
1167 return;
1168
1169 //Reconnect to RPMS and recreate all global recordsets
1170 try
1171 {
1172 // Close All, but tell the Close All method not to call Applicaiton.Exit since we still plan to continue.
1173 // Close All does not call Application.Exit, but CGView_Close handler does
1174 m_bExitOK = false;
1175 CloseAll();
1176 m_bExitOK = true;
1177
1178 //Parameter for initialize app tells it that this is a re-login and forces a new access and verify code.
1179 bool isEverythingOkay = this.InitializeApp(true);
1180
1181 //if an error occurred, break out. This time we need to call Application.Exit since it's already running.
1182 if (!isEverythingOkay)
1183 {
1184 Application.Exit();
1185 return;
1186 }
1187
1188 //Otherwise, everything is okay. So open document and view, then show and activate view.
1189 CGDocument doc = new CGDocument();
1190 doc.DocManager = _current;
1191
1192 CGView view = new CGView();
1193 view.InitializeDocView(doc, _current, doc.StartDate, _current.WindowText);
1194
1195 view.Show();
1196 view.Activate();
1197
1198 //Application.Run need not be called b/c it is already running.
1199 }
1200 catch (Exception ex)
1201 {
1202 throw ex;
1203 }
1204
1205 }
1206
1207 delegate void CloseAllDelegate(string sMsg);
1208
1209 private void CloseAll(string sMsg)
1210 {
1211 if (sMsg == "")
1212 {
1213 sMsg = "Scheduling System Shutting Down Immediately for Maintenance.";
1214 }
1215
1216 MessageBox.Show(sMsg, "Clinical Scheduling Administrator -- System Shutdown Notification", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
1217
1218 CloseAll();
1219 }
1220
1221 private void CloseAll()
1222 {
1223 //Close all documents, views and connections
1224 Hashtable h = CGDocumentManager.Current.Views;
1225 int nTempCount = h.Count;
1226 do
1227 {
1228 nTempCount = h.Count;
1229 foreach (CGView v in h.Keys)
1230 {
1231 v.Close();
1232 break;
1233 }
1234 } while ((h.Count > 0) && (nTempCount != h.Count));
1235
1236 h = CGDocumentManager.Current.AvailabilityViews;
1237 nTempCount = h.Count;
1238 do
1239 {
1240 nTempCount = h.Count;
1241 foreach (CGAVView v in h.Keys)
1242 {
1243 v.Close();
1244 break;
1245 }
1246 } while ((h.Count > 0) && (nTempCount != h.Count));
1247
1248 }
1249
1250 public delegate DataTable RPMSDataTableDelegate(string CommandString, string TableName);
1251
1252 public DataTable RPMSDataTable(string sSQL, string sTableName)
1253 {
1254 //Retrieves a recordset from RPMS
1255 string sErrorMessage = "";
1256 DataTable dtOut;
1257
1258 try
1259 {
1260 //System.IntPtr pHandle = this.Handle;
1261 RPMSDataTableDelegate rdtd = new RPMSDataTableDelegate(ConnectInfo.RPMSDataTable);
1262 //dtOut = (DataTable) this.Invoke(rdtd, new object[] {sSQL, sTableName});
1263 dtOut = rdtd.Invoke(sSQL, sTableName);
1264 }
1265
1266 catch (Exception ex)
1267 {
1268 sErrorMessage = "CGDocumentManager.RPMSDataTable error: " + ex.Message;
1269 throw ex;
1270 }
1271
1272 return dtOut;
1273
1274 }
1275
1276 public void ChangeDivision(System.Windows.Forms.Form frmCaller)
1277 {
1278 this.ConnectInfo.ChangeDivision(frmCaller);
1279 foreach (CGView v in _views.Keys)
1280 {
1281 v.InitializeDocView(v.Document.DocName);
1282 v.Document.RefreshDocument();
1283 }
1284 }
1285
1286 public void ViewRefresh()
1287 {
1288 foreach (CGView v in _views.Keys)
1289 {
1290 try
1291 {
1292 v.Document.RefreshDocument();
1293 }
1294 catch (Exception ex)
1295 {
1296 Debug.Write("CGDocumentManager.ViewRefresh Exception: " + ex.Message + "\n");
1297 }
1298 finally
1299 {
1300 }
1301 }
1302 Debug.Write("DocManager refreshed all views.\n");
1303 }
1304
1305 #endregion Methods & Events
1306
1307 }
1308}
Note: See TracBrowser for help on using the repository browser.