source: cprs/branches/GUI-config/CPRS-Lib/ORNet.~pas@ 476

Last change on this file since 476 was 476, checked in by Kevin Toppenberg, 16 years ago

New WorldVistA Config Utility

File size: 16.2 KB
Line 
1unit ORNet;
2
3interface
4
5uses SysUtils, Windows, Classes, Forms, Controls, ORFn, TRPCB, RPCConf1, Dialogs; //, SharedRPCBroker;
6
7procedure SetBrokerServer(const AName: string; APort: Integer; WantDebug: Boolean);
8function AuthorizedOption(const OptionName: string): Boolean;
9function ConnectToServer(const OptionName: string): Boolean;
10function MRef(glvn: string): string;
11procedure CallV(const RPCName: string; const AParam: array of const);
12function sCallV(const RPCName: string; const AParam: array of const): string;
13procedure tCallV(ReturnData: TStrings; const RPCName: string; const AParam: array of const);
14function UpdateContext(NewContext: string): boolean;
15function IsBaseContext: boolean;
16procedure CallBrokerInContext;
17procedure CallBroker;
18function RetainedRPCCount: Integer;
19procedure SetRetainedRPCMax(Value: Integer);
20function GetRPCMax: integer;
21procedure LoadRPCData(Dest: TStrings; ID: Integer);
22function DottedIPStr: string;
23procedure CallRPCWhenIdle(CallProc: TORIdleCallProc; Msg: String);
24procedure EnsureBroker;
25
26(*
27function pCallV(const RPCName: string; const AParam: array of const): PChar;
28procedure wCallV(AControl: TControl; const RPCName: string; const AParam: array of const);
29procedure WrapWP(Buf: pChar);
30*)
31
32var
33 //RPCBrokerV: TSharedRPCBroker; // an RPC Broker object - used by all calls
34 RPCBrokerV: TRPCBroker;
35 RPCLastCall: string;
36
37implementation
38
39uses Winsock;
40
41const
42 // *** these are constants from RPCBErr.pas, will broker document them????
43 XWB_M_REJECT = 20000 + 2; // M error
44 XWB_BadSignOn = 20000 + 4; // SignOn 'Error' (happens when cancel pressed)
45
46var
47 uCallList: TList;
48 uMaxCalls: Integer;
49 uShowRPCs: Boolean;
50 uBaseContext: string = '';
51 uCurrentContext: string = '';
52
53{ private procedures and functions ---------------------------------------------------------- }
54
55procedure EnsureBroker;
56{ ensures that a broker object has been created - creates & initializes it if necessary }
57begin
58 if RPCBrokerV = nil then
59 begin
60 //RPCBrokerV := TSharedRPCBroker.Create(Application);
61 RPCBrokerV := TRPCBroker.Create(Application);
62 with RPCBrokerV do
63 begin
64 KernelLogIn := True;
65 Login.Mode := lmAppHandle;
66 ClearParameters := True;
67 ClearResults := True;
68 DebugMode := False;
69 end;
70 end;
71end;
72
73procedure SetList(AStringList: TStrings; ParamIndex: Integer);
74{ places TStrings into RPCBrokerV.Mult[n], where n is a 1-based (not 0-based) index }
75var
76 i: Integer;
77begin
78 with RPCBrokerV.Param[ParamIndex] do
79 begin
80 PType := list;
81 with AStringList do for i := 0 to Count - 1 do Mult[IntToStr(i+1)] := Strings[i];
82 end;
83end;
84
85procedure SetParams(const RPCName: string; const AParam: array of const);
86{ takes the params (array of const) passed to xCallV and sets them into RPCBrokerV.Param[i] }
87const
88 BoolChar: array[boolean] of char = ('0', '1');
89var
90 i: integer;
91 TmpExt: Extended;
92begin
93 RPCLastCall := RPCName + ' (SetParam begin)';
94 if Length(RPCName) = 0 then raise Exception.Create('No RPC Name');
95 EnsureBroker;
96 with RPCBrokerV do
97 begin
98 ClearParameters := True;
99 RemoteProcedure := RPCName;
100 for i := 0 to High(AParam) do with AParam[i] do
101 begin
102 Param[i].PType := literal;
103 case VType of
104 vtInteger: Param[i].Value := IntToStr(VInteger);
105 vtBoolean: Param[i].Value := BoolChar[VBoolean];
106 vtChar: Param[i].Value := VChar;
107 //vtExtended: Param[i].Value := FloatToStr(VExtended^);
108 vtExtended: begin
109 TmpExt := VExtended^;
110 if(abs(TmpExt) < 0.0000000000001) then TmpExt := 0;
111 Param[i].Value := FloatToStr(TmpExt);
112 end;
113 vtString: with Param[i] do
114 begin
115 Value := VString^;
116 if (Length(Value) > 0) and (Value[1] = #1) then
117 begin
118 Value := Copy(Value, 2, Length(Value));
119 PType := reference;
120 end;
121 end;
122 vtPChar: Param[i].Value := StrPas(VPChar);
123 vtPointer: if VPointer = nil
124 then ClearParameters := True {Param[i].PType := null}
125 else raise Exception.Create('Pointer type must be nil.');
126 vtObject: if VObject is TStrings then SetList(TStrings(VObject), i);
127 vtAnsiString: with Param[i] do
128 begin
129 Value := string(VAnsiString);
130 if (Length(Value) > 0) and (Value[1] = #1) then
131 begin
132 Value := Copy(Value, 2, Length(Value));
133 PType := reference;
134 end;
135 end;
136 vtInt64: Param[i].Value := IntToStr(VInt64^);
137 else raise Exception.Create('Unable to pass parameter type to Broker.');
138 end; {case}
139 end; {for}
140 end; {with}
141 RPCLastCall := RPCName + ' (SetParam end)';
142end;
143
144{ public procedures and functions ----------------------------------------------------------- }
145
146function UpdateContext(NewContext: string): boolean;
147begin
148 if NewContext = uCurrentContext then
149 Result := TRUE
150 else
151 begin
152 Result := RPCBrokerV.CreateContext(NewContext);
153 if Result then
154 uCurrentContext := NewContext
155 else
156 if (NewContext <> uBaseContext) and RPCBrokerV.CreateContext(uBaseContext) then
157 uCurrentContext := uBaseContext
158 else
159 uCurrentContext := '';
160 end;
161end;
162
163function IsBaseContext: boolean;
164begin
165 Result := ((uCurrentContext = uBaseContext) or (uCurrentContext = ''));
166end;
167
168procedure CallBrokerInContext;
169var
170 AStringList: TStringList;
171 i, j: Integer;
172 x, y: string;
173begin
174 RPCLastCall := RPCBrokerV.RemoteProcedure + ' (CallBroker begin)';
175 if uShowRPCs then StatusText(RPCBrokerV.RemoteProcedure);
176 with RPCBrokerV do if not Connected then // Happens if broker connection is lost.
177 begin
178 ClearResults := True;
179 Exit;
180 end;
181 if uCallList.Count = uMaxCalls then
182 begin
183 AStringList := uCallList.Items[0];
184 AStringList.Free;
185 uCallList.Delete(0);
186 end;
187 AStringList := TStringList.Create;
188 AStringList.Add(RPCBrokerV.RemoteProcedure);
189 if uCurrentContext <> uBaseContext then
190 AStringList.Add('Context: ' + uCurrentContext);
191 AStringList.Add(' ');
192 AStringList.Add('Params ------------------------------------------------------------------');
193 with RPCBrokerV do for i := 0 to Param.Count - 1 do
194 begin
195 case Param[i].PType of
196 //global: x := 'global';
197 list: x := 'list';
198 literal: x := 'literal';
199 //null: x := 'null';
200 reference: x := 'reference';
201 undefined: x := 'undefined';
202 //wordproc: x := 'wordproc';
203 end;
204 AStringList.Add(x + #9 + Param[i].Value);
205 if Param[i].PType = list then
206 begin
207 for j := 0 to Param[i].Mult.Count - 1 do
208 begin
209 x := Param[i].Mult.Subscript(j);
210 y := Param[i].Mult[x];
211 AStringList.Add(#9 + '(' + x + ')=' + y);
212 end;
213 end;
214 end; {with...for}
215 //RPCBrokerV.Call;
216 try
217 RPCBrokerV.Call;
218 except
219 // The broker erroneously sets connected to false if there is any error (including an
220 // error on the M side). It should only set connection to false if there is no connection.
221 on E:EBrokerError do
222 begin
223 if E.Code = XWB_M_REJECT then
224 begin
225 x := 'An error occurred on the server.' + CRLF + CRLF + E.Action;
226 Application.MessageBox(PChar(x), 'Server Error', MB_OK);
227 end
228 else raise;
229 (*
230 case E.Code of
231 XWB_M_REJECT: begin
232 x := 'An error occurred on the server.' + CRLF + CRLF + E.Action;
233 Application.MessageBox(PChar(x), 'Server Error', MB_OK);
234 end;
235 else begin
236 x := 'An error occurred with the network connection.' + CRLF +
237 'Action was: ' + E.Action + CRLF + 'Code was: ' + E.Mnemonic +
238 CRLF + CRLF + 'Application cannot continue.';
239 Application.MessageBox(PChar(x), 'Network Error', MB_OK);
240 end;
241 end;
242 *)
243 // make optional later...
244 if not RPCBrokerV.Connected then Application.Terminate;
245 end;
246 end;
247 AStringList.Add(' ');
248 AStringList.Add('Results -----------------------------------------------------------------');
249 AStringList.AddStrings(RPCBrokerV.Results);
250 uCallList.Add(AStringList);
251 if uShowRPCs then StatusText('');
252 RPCLastCall := RPCBrokerV.RemoteProcedure + ' (completed)';
253end;
254
255procedure CallBroker;
256begin
257 UpdateContext(uBaseContext);
258 CallBrokerInContext;
259end;
260
261procedure SetBrokerServer(const AName: string; APort: Integer; WantDebug: Boolean);
262{ makes the initial connection to a server }
263begin
264 EnsureBroker;
265 with RPCBrokerV do
266 begin
267 Server := AName;
268 if APort > 0 then ListenerPort := APort;
269 DebugMode := WantDebug;
270 Connected := True;
271 end;
272end;
273
274function AuthorizedOption(const OptionName: string): Boolean;
275{ checks to see if the user is authorized to use this application }
276begin
277 EnsureBroker;
278 Result := RPCBrokerV.CreateContext(OptionName);
279 if Result then
280 begin
281 if (uBaseContext = '') then
282 uBaseContext := OptionName;
283 uCurrentContext := OptionName;
284 end;
285end;
286
287function ConnectToServer(const OptionName: string): Boolean;
288{ establish initial connection to server using optional command line parameters and check that
289 this application (option) is allowed for this user }
290var
291 WantDebug: Boolean;
292 AServer, APort, x: string;
293 i, ModalResult: Integer;
294begin
295 Result := False;
296 WantDebug := False;
297 AServer := '';
298 APort := '';
299 for i := 1 to ParamCount do // params may be: S[ERVER]=hostname P[ORT]=port DEBUG
300 begin
301 if UpperCase(ParamStr(i)) = 'DEBUG' then WantDebug := True;
302 if UpperCase(ParamStr(i)) = 'SHOWRPCS' then uShowRPCs := True;
303 x := UpperCase(Piece(ParamStr(i), '=', 1));
304 if (x = 'S') or (x = 'SERVER') then AServer := Piece(ParamStr(i), '=', 2);
305 if (x = 'P') or (x = 'PORT') then APort := Piece(ParamStr(i), '=', 2);
306 end;
307 if (AServer = '') or (APort = '') then
308 begin
309 ModalResult := GetServerInfo(AServer, APort);
310 if ModalResult = mrCancel then Exit;
311 end;
312 // use try..except to work around errors in the Broker SignOn screen
313 try
314 SetBrokerServer(AServer, StrToIntDef(APort, 9200), WantDebug);
315 Result := AuthorizedOption(OptionName);
316 if Result then Result := RPCBrokerV.Connected;
317 RPCBrokerV.RPCTimeLimit := 300;
318 except
319 on E:EBrokerError do
320 begin
321 if E.Code <> XWB_BadSignOn then InfoBox(E.Message, 'Error', MB_OK or MB_ICONERROR);
322 Result := False;
323 end;
324 end;
325end;
326
327function MRef(glvn: string): string;
328{ prepends ASCII 1 to string, allows SetParams to interpret as an M reference }
329begin
330 Result := #1 + glvn;
331end;
332
333procedure CallV(const RPCName: string; const AParam: array of const);
334{ calls the broker leaving results in results property which must be read by caller }
335var
336 SavedCursor: TCursor;
337begin
338 SavedCursor := Screen.Cursor;
339 Screen.Cursor := crHourGlass;
340 SetParams(RPCName, AParam);
341 CallBroker; //RPCBrokerV.Call;
342 Screen.Cursor := SavedCursor;
343end;
344
345function sCallV(const RPCName: string; const AParam: array of const): string;
346{ calls the broker and returns a scalar value. }
347var
348 SavedCursor: TCursor;
349begin
350 SavedCursor := Screen.Cursor;
351 Screen.Cursor := crHourGlass;
352 SetParams(RPCName, AParam);
353 CallBroker; //RPCBrokerV.Call;
354 if RPCBrokerV.Results.Count > 0 then Result := RPCBrokerV.Results[0] else Result := '';
355 Screen.Cursor := SavedCursor;
356end;
357
358procedure tCallV(ReturnData: TStrings; const RPCName: string; const AParam: array of const);
359{ calls the broker and returns TStrings data }
360var
361 SavedCursor: TCursor;
362begin
363 if ReturnData = nil then raise Exception.Create('TString not created');
364 SavedCursor := Screen.Cursor;
365 Screen.Cursor := crHourGlass;
366 SetParams(RPCName, AParam);
367 CallBroker; //RPCBrokerV.Call;
368 ReturnData.Assign(RPCBrokerV.Results);
369 Screen.Cursor := SavedCursor;
370end;
371
372(* uncomment if these are needed -
373
374function pCallV(const RPCName: string; const AParam: array of const): PChar;
375{ Calls the Broker. Result is a PChar containing raw Broker data. }
376{ -- Caller must dispose the string that is returned -- }
377var
378 SavedCursor: TCursor;
379begin
380 SavedCursor := Screen.Cursor;
381 Screen.Cursor := crHourGlass;
382 SetParams(RPCName, AParam);
383 RPCBrokerV.Call;
384 pCallV := StrNew(RPCBrokerV.Results.GetText);
385 Screen.Cursor := SavedCursor;
386end;
387
388procedure wCallV(AControl: TControl; const RPCName: string; const AParam: array of const);
389{ Calls the Broker. Places data into control (wrapped). }
390var
391 BufPtr: PChar;
392begin
393 BufPtr := pCallV(RPCName, AParam);
394 WrapWP(BufPtr);
395 AControl.SetTextBuf(BufPtr);
396 StrDispose(BufPtr);
397end;
398
399procedure WrapWP(Buf: pChar);
400{ Iterates through Buf and wraps text in the same way that FM wraps text. }
401var
402 PSub: PChar;
403begin
404 PSub := StrScan(Buf, #13);
405 while PSub <> nil do
406 begin
407 if Ord(PSub[2]) > 32 then
408 begin
409 StrMove(PSub, PSub + SizeOf(Char), StrLen(PSub));
410 PSub[0] := #32;
411 end
412 else repeat Inc(PSub, SizeOf(Char)) until (Ord(PSub[0]) > 32) or (PSub = StrEnd(PSub));
413 PSub := StrScan(PSub, #13);
414 end;
415end;
416
417*)
418
419function RetainedRPCCount: Integer;
420begin
421 Result := uCallList.Count;
422end;
423
424procedure SetRetainedRPCMax(Value: Integer);
425begin
426 if Value > 0 then uMaxCalls := Value;
427end;
428
429function GetRPCMax: integer;
430begin
431 Result := uMaxCalls;
432end;
433
434procedure LoadRPCData(Dest: TStrings; ID: Integer);
435begin
436 if (ID > -1) and (ID < uCallList.Count) then Dest.Assign(TStringList(uCallList.Items[ID]));
437end;
438
439function DottedIPStr: string;
440{ return the IP address of the local machine as a string in dotted form: nnn.nnn.nnn.nnn }
441const
442 WINSOCK1_1 = $0101; // minimum required version of WinSock
443 SUCCESS = 0; // value returned by WinSock functions if no error
444var
445 //WSAData: TWSAData; // structure to hold startup information
446 HostEnt: PHostEnt; // pointer to Host Info structure (see WinSock 1.1, page 60)
447 IPAddr: PInAddr; // pointer to IP address in network order (4 bytes)
448 LocalName: array[0..255] of Char; // buffer for the name of the client machine
449begin
450 Result := 'No IP Address';
451 // ensure the Winsock DLL has been loaded (should be if there is a broker connection)
452 //if WSAStartup(WINSOCK1_1, WSAData) <> SUCCESS then Exit;
453 //try
454 // get the name of the client machine
455 if gethostname(LocalName, SizeOf(LocalName) - 1) <> SUCCESS then Exit;
456 // get information about the client machine (contained in a record of type THostEnt)
457 HostEnt := gethostbyname(LocalName);
458 if HostEnt = nil then Exit;
459 // get a pointer to the four bytes that contain the IP address
460 // Dereference HostEnt to get the THostEnt record. In turn, dereference the h_addr_list
461 // field to get a pointer to the IP address. The pointer to the IP address is type PChar,
462 // so it needs to be typecast as PInAddr in order to make the call to inet_ntoa.
463 IPAddr := PInAddr(HostEnt^.h_addr_list^);
464 // Dereference IPAddr (which is a PChar typecast as PInAddr) to get the 4 bytes that need
465 // to be passed to inet_ntoa. A string with the IP address in dotted format is returned.
466 Result := inet_ntoa(IPAddr^);
467 //finally
468 // causes the reference counter in Winsock (set by WSAStartup, above) to be decremented
469 //WSACleanup;
470 //end;
471end;
472
473procedure RPCIdleCallDone(Msg: string);
474begin
475 RPCBrokerV.ClearResults := True;
476end;
477
478procedure CallRPCWhenIdle(CallProc: TORIdleCallProc; Msg: String);
479begin
480 CallWhenIdleNotifyWhenDone(CallProc, RPCIdleCallDone, Msg);
481end;
482
483initialization
484 RPCBrokerV := nil;
485 RPCLastCall := 'No RPCs called';
486 uCallList := TList.Create;
487 uMaxCalls := 10;
488 uShowRPCs := False;
489
490finalization
491 while uCallList.Count > 0 do
492 begin
493 TStringList(uCallList.Items[0]).Free;
494 uCallList.Delete(0);
495 end;
496 uCallList.Free;
497
498end.
Note: See TracBrowser for help on using the repository browser.