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

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

LoadingSplash: New Form that does... pretty much nothing. Just shows a splash when updating data from server.
CGView:

CGDocumentManager:

CGDocument:

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