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

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

CGDocumentManager: Version Check Added.
Release versions, again

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