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

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

calendarGrid: Removed MouseEnter event and added it to CGView which now has to handle it properly.
CGAppointments: now supports cloning.
CGDocument: Added RefreshDocumentAsync, which retrives data from the server w/o asking the grid to refersh.
CGDocumentManager: CGView.InitializeDocView does not take appointments as argument; callers are changed.
CGView: Many Changes:

  • Opening a Schedule nows calls up a splash and a wait cursor while loading.
  • Events coming from the server are now handled asynchronously (not on UI thread). This results in much better responsiveness.
  • Appointments, Availabilities, Resources are all set in the CalendarGrid by UpdateArrays; as a centralized point of drawing the grid.
  • A mistaken "feature"--stealing focus from each others windows, was removed--CalendarGrid.Focus event only fired now if the form is the Active Form. This is accomplished using GetActiveWindow() from user32.dll (a Win32 API).

LoadingSplash: Opacity removed; form resized.

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