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

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

CGDocument:

CGDocumentManager:

CGView: (Changes to support printing of Appointment Slip)

  • Changed name and text of context menu to say "print appointment slip"
  • Logic to Print Appointment Slip

CustomPrinting.cs renamed to Printing.cs; old Printing.Cs removed.
DAppointPage:

  • New checkbox to request printing of Appointment Slip
  • Sex of patient now pulled and included in form.

DPatientLetter:

  • Redirected to use new Printing framework.

Patient:

Printing:

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