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

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

CalendarGrid: Add MouseEnter event: puts focus on calendar grid. Used to "steal" focus from other controls so that lose focus events would fire.
CGDocumentManager: Added application-wide error handler on Application error exception; Added load timer to test how long it takes to load the GUI.
CGDocument: Just documentation updates.

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