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

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

CGAppointment: Added Provider as a Member of Class. (auto property)
CGDocument: No changes
CGDocumentManager: Added UserPreferences as a member of a Class (private and property)
CGView: Changes to support printing of Routing Slip
DAppointPage: Changes to support UserPreferences member for auto printing the routing slips
DCheckIn: Extensive changes in load code (now uses LINQ instead of ADO.net). Changes to support UserPreferences member for auto printing the routing slips.
Patient: Documentation for UserFriendlyAge.
Provider: New class to represent Provider
UserPreferences: New class to represent User preferences. Does not interact with DB right now.

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