| 1 | unit CommProtocol1;
|
|---|
| 2 |
|
|---|
| 3 | interface
|
|---|
| 4 |
|
|---|
| 5 | uses
|
|---|
| 6 | Classes, SysUtils, VarBlockSerializer, syncobjs, CommPin, Threading,
|
|---|
| 7 | DebugLog, StreamHelper, StopWatch, SpecializedList, Common, DateUtils;
|
|---|
| 8 |
|
|---|
| 9 | type
|
|---|
| 10 | ECommResponseCodeError = class(Exception);
|
|---|
| 11 | ECommTimeout = class(Exception);
|
|---|
| 12 | ECommError = class(Exception);
|
|---|
| 13 | ENotActive = class(Exception);
|
|---|
| 14 |
|
|---|
| 15 | TResponseError = (rcNone, rcCommandNotSupported, rcSequenceOutOfRange,
|
|---|
| 16 | rcEWrongParameters, rcVarIntDecode, rcDropped);
|
|---|
| 17 | TMessageType = (mtNone, mtRequest, mtResponse);
|
|---|
| 18 |
|
|---|
| 19 | TCommProtocol = class;
|
|---|
| 20 |
|
|---|
| 21 | { TDeviceProtocolSession }
|
|---|
| 22 |
|
|---|
| 23 | TDeviceProtocolSession = class
|
|---|
| 24 | private
|
|---|
| 25 | RepeatCounter: integer;
|
|---|
| 26 | ReceiveEvent: TSimpleEvent;
|
|---|
| 27 | Request: TStreamHelper;
|
|---|
| 28 | ResponseParameters: TVarBlockIndexed;
|
|---|
| 29 | TransmitTime: TDateTime;
|
|---|
| 30 | public
|
|---|
| 31 | Lock: TCriticalSection;
|
|---|
| 32 | Enabled: Boolean;
|
|---|
| 33 | SequenceNumber: Integer;
|
|---|
| 34 | ResponseCode: Integer;
|
|---|
| 35 | CommandError: Integer;
|
|---|
| 36 | RaiseError: Boolean;
|
|---|
| 37 | Timeouted: Boolean;
|
|---|
| 38 | CommandIndex: TListInteger;
|
|---|
| 39 | Latency: TDateTime;
|
|---|
| 40 | constructor Create;
|
|---|
| 41 | destructor Destroy; override;
|
|---|
| 42 | end;
|
|---|
| 43 |
|
|---|
| 44 | { TDeviceProtocolSessionList }
|
|---|
| 45 |
|
|---|
| 46 | TDeviceProtocolSessionList = class(TListObject)
|
|---|
| 47 | private
|
|---|
| 48 | function GetSequenceNumber: Integer;
|
|---|
| 49 | public
|
|---|
| 50 | SequenceNumber: integer;
|
|---|
| 51 | MaxSequenceNumber: integer;
|
|---|
| 52 | MaxSessionCount: integer;
|
|---|
| 53 | Parent: TCommProtocol;
|
|---|
| 54 | Lock: TCriticalSection;
|
|---|
| 55 | procedure Add(Session: TDeviceProtocolSession);
|
|---|
| 56 | function GetBySequence(Sequence: integer): TDeviceProtocolSession;
|
|---|
| 57 | procedure Remove(Session: TDeviceProtocolSession);
|
|---|
| 58 | constructor Create;
|
|---|
| 59 | destructor Destroy; override;
|
|---|
| 60 | procedure Assign(Source: TListObject);
|
|---|
| 61 | end;
|
|---|
| 62 |
|
|---|
| 63 | TAfterRequest = procedure(Command: TListInteger; Parameters: TVarBlockIndexed;
|
|---|
| 64 | Result: TVarBlockIndexed; var ResponseError: TResponseError;
|
|---|
| 65 | var CommandError: integer) of object;
|
|---|
| 66 |
|
|---|
| 67 | { TRetransmitCheckThread }
|
|---|
| 68 |
|
|---|
| 69 | TRetransmitCheckThread = class(TTermThread)
|
|---|
| 70 | public
|
|---|
| 71 | Parent: TCommProtocol;
|
|---|
| 72 | CheckPeriod: Integer;
|
|---|
| 73 | procedure Execute; override;
|
|---|
| 74 | end;
|
|---|
| 75 |
|
|---|
| 76 | { TRemoteBuffer }
|
|---|
| 77 |
|
|---|
| 78 | TRemoteBuffer = class
|
|---|
| 79 | Lock: TCriticalSection;
|
|---|
| 80 | Size: Integer;
|
|---|
| 81 | Used: Integer;
|
|---|
| 82 | procedure Allocate(AValue: Integer);
|
|---|
| 83 | procedure Release(AValue: Integer);
|
|---|
| 84 | procedure Assign(Source: TRemoteBuffer);
|
|---|
| 85 | constructor Create;
|
|---|
| 86 | destructor Destroy; override;
|
|---|
| 87 | end;
|
|---|
| 88 |
|
|---|
| 89 | { TCommProtocol }
|
|---|
| 90 |
|
|---|
| 91 | TCommProtocol = class
|
|---|
| 92 | private
|
|---|
| 93 | FActive: Boolean;
|
|---|
| 94 | FOnAfterRequest: TAfterRequest;
|
|---|
| 95 | FOnCommand: TAfterRequest;
|
|---|
| 96 | FOnDebugLog: TDebugLogAddEvent;
|
|---|
| 97 | OnAfterRequest: TAfterRequest;
|
|---|
| 98 | RetransmitThread: TRetransmitCheckThread;
|
|---|
| 99 | procedure HandleRequest(Stream: TStream);
|
|---|
| 100 | procedure SetActive(const AValue: Boolean);
|
|---|
| 101 | public
|
|---|
| 102 | RetransmitTimeout: TDateTime;
|
|---|
| 103 | RetransmitRepeatCount: integer;
|
|---|
| 104 | RetransmitTotalCount: integer;
|
|---|
| 105 | RemoteBuffer: TRemoteBuffer;
|
|---|
| 106 | WrongSequenceCount: integer;
|
|---|
| 107 | Sessions: TDeviceProtocolSessionList;
|
|---|
| 108 | Pin: TCommPin;
|
|---|
| 109 | LastCommandResponseTime: TDateTime;
|
|---|
| 110 | LastLatency: TDateTime;
|
|---|
| 111 | Lock: TCriticalSection;
|
|---|
| 112 | procedure DataReceive(Sender: TCommPin; Stream: TStream); virtual;
|
|---|
| 113 | procedure SendCommand(Command: array of integer;
|
|---|
| 114 | ResponseParameters: TVarBlockIndexed = nil;
|
|---|
| 115 | RequestParameters: TVarBlockIndexed = nil; ARaiseError: boolean = True);
|
|---|
| 116 | constructor Create; virtual;
|
|---|
| 117 | destructor Destroy; override;
|
|---|
| 118 | procedure Assign(Source: TCommProtocol); virtual;
|
|---|
| 119 | property OnAfterRequest: TAfterRequest read FOnAfterRequest write FOnAfterRequest;
|
|---|
| 120 | property OnCommand: TAfterRequest read FOnCommand write FOnCommand;
|
|---|
| 121 | property OnDebugLog: TDebugLogAddEvent read FOnDebugLog write FOnDebugLog;
|
|---|
| 122 | property Active: Boolean read FActive write SetActive;
|
|---|
| 123 | end;
|
|---|
| 124 |
|
|---|
| 125 | resourcestring
|
|---|
| 126 | SRemoteBufferInconsistency = 'Remote buffer inconsistency';
|
|---|
| 127 | SResponseError = 'Command %0:s response error %1:s';
|
|---|
| 128 | SResponseTimeout = 'Response timeout';
|
|---|
| 129 | SWrongSequenceCount = 'Receive wrong sequence number %d';
|
|---|
| 130 | SDeviceProtocol = 'Device protocol';
|
|---|
| 131 | SProtocolDecodeError = 'Data decode error';
|
|---|
| 132 | SProtocolNotActive = 'Device protocol not active';
|
|---|
| 133 |
|
|---|
| 134 |
|
|---|
| 135 | implementation
|
|---|
| 136 |
|
|---|
| 137 | { TRemoteBuffer }
|
|---|
| 138 |
|
|---|
| 139 | procedure TRemoteBuffer.Allocate(AValue: Integer);
|
|---|
| 140 | begin
|
|---|
| 141 | try
|
|---|
| 142 | Lock.Acquire;
|
|---|
| 143 |
|
|---|
| 144 | // Wait for free remote buffer
|
|---|
| 145 | while (Used + AValue) > Size do
|
|---|
| 146 | try
|
|---|
| 147 | Lock.Release;
|
|---|
| 148 | Sleep(1);
|
|---|
| 149 | finally
|
|---|
| 150 | Lock.Acquire;
|
|---|
| 151 | end;
|
|---|
| 152 | Used := Used + AValue;
|
|---|
| 153 | finally
|
|---|
| 154 | Lock.Release;
|
|---|
| 155 | end;
|
|---|
| 156 | end;
|
|---|
| 157 |
|
|---|
| 158 | procedure TRemoteBuffer.Release(AValue: Integer);
|
|---|
| 159 | begin
|
|---|
| 160 | try
|
|---|
| 161 | Lock.Acquire;
|
|---|
| 162 | Used := Used - AValue;
|
|---|
| 163 | if Used < 0 then
|
|---|
| 164 | raise Exception.Create(SRemoteBufferInconsistency);
|
|---|
| 165 | finally
|
|---|
| 166 | Lock.Release;
|
|---|
| 167 | end;
|
|---|
| 168 | end;
|
|---|
| 169 |
|
|---|
| 170 | procedure TRemoteBuffer.Assign(Source: TRemoteBuffer);
|
|---|
| 171 | begin
|
|---|
| 172 | Used := Source.Used;
|
|---|
| 173 | Size := Source.Size;
|
|---|
| 174 | end;
|
|---|
| 175 |
|
|---|
| 176 | constructor TRemoteBuffer.Create;
|
|---|
| 177 | begin
|
|---|
| 178 | Lock := TCriticalSection.Create;
|
|---|
| 179 | Size := 127;
|
|---|
| 180 | Used := 0;
|
|---|
| 181 | end;
|
|---|
| 182 |
|
|---|
| 183 | destructor TRemoteBuffer.Destroy;
|
|---|
| 184 | begin
|
|---|
| 185 | Lock.Free;
|
|---|
| 186 | inherited Destroy;
|
|---|
| 187 | end;
|
|---|
| 188 |
|
|---|
| 189 |
|
|---|
| 190 | procedure TCommProtocol.DataReceive(Sender: TCommPin; Stream: TStream);
|
|---|
| 191 | var
|
|---|
| 192 | ResponseSequenceNumber: Integer;
|
|---|
| 193 | Session: TDeviceProtocolSession;
|
|---|
| 194 | MessageType: Integer;
|
|---|
| 195 | Request: TVarBlockIndexed;
|
|---|
| 196 | TempStream: TMemoryStream;
|
|---|
| 197 | begin
|
|---|
| 198 | try
|
|---|
| 199 | TempStream := TMemoryStream.Create;
|
|---|
| 200 | Request := TVarBlockIndexed.Create;
|
|---|
| 201 | Request.Enclose := False;
|
|---|
| 202 | with Request do
|
|---|
| 203 | try
|
|---|
| 204 | Stream.Position := 0;
|
|---|
| 205 | ReadFromStream(Stream);
|
|---|
| 206 | if TestIndex(0) then
|
|---|
| 207 | MessageType := ReadVarUInt(0);
|
|---|
| 208 | if MessageType = Integer(mtResponse) then begin
|
|---|
| 209 | if TestIndex(1) then begin
|
|---|
| 210 | ResponseSequenceNumber := ReadVarUInt(1);
|
|---|
| 211 | try
|
|---|
| 212 | Sessions.Lock.Acquire;
|
|---|
| 213 | Session := Sessions.GetBySequence(ResponseSequenceNumber);
|
|---|
| 214 | if Assigned(Session) then begin
|
|---|
| 215 | with Session do try
|
|---|
| 216 | Session.Lock.Acquire;
|
|---|
| 217 | if TestIndex(2) and Assigned(ResponseParameters) then begin
|
|---|
| 218 | ReadVarStream(2, TempStream);
|
|---|
| 219 | ResponseParameters.Enclose := False;
|
|---|
| 220 | ResponseParameters.ReadFromStream(TempStream);
|
|---|
| 221 | end;
|
|---|
| 222 | if TestIndex(3) then ResponseCode := ReadVarUInt(3)
|
|---|
| 223 | else ResponseCode := 0;
|
|---|
| 224 | if TestIndex(4) then CommandError := ReadVarUInt(4)
|
|---|
| 225 | else CommandError := 0;
|
|---|
| 226 | Latency := Now - TransmitTime;
|
|---|
| 227 | Enabled := False;
|
|---|
| 228 | ReceiveEvent.SetEvent;
|
|---|
| 229 | finally
|
|---|
| 230 | Session.Lock.Release;
|
|---|
| 231 | end;
|
|---|
| 232 | end else begin
|
|---|
| 233 | Inc(WrongSequenceCount);
|
|---|
| 234 | if Assigned(FOnDebugLog) then
|
|---|
| 235 | FOnDebugLog(SDeviceProtocol, Format(SWrongSequenceCount, [ResponseSequenceNumber]));
|
|---|
| 236 | end;
|
|---|
| 237 | finally
|
|---|
| 238 | Sessions.Lock.Release;
|
|---|
| 239 | end;
|
|---|
| 240 | end;
|
|---|
| 241 | end else
|
|---|
| 242 | if MessageType = Integer(mtRequest) then HandleRequest(Stream);
|
|---|
| 243 | except
|
|---|
| 244 | on EReadError do begin
|
|---|
| 245 | if Assigned(FOnDebugLog) then
|
|---|
| 246 | FOnDebugLog(SDeviceProtocol, SProtocolDecodeError);
|
|---|
| 247 | end;
|
|---|
| 248 | end;
|
|---|
| 249 | finally
|
|---|
| 250 | TempStream.Free;
|
|---|
| 251 | Request.Free;
|
|---|
| 252 | end;
|
|---|
| 253 | end;
|
|---|
| 254 |
|
|---|
| 255 | procedure TCommProtocol.HandleRequest(Stream: TStream);
|
|---|
| 256 | var
|
|---|
| 257 | ResponseCode: TResponseError;
|
|---|
| 258 | Response: TVarBlockIndexed;
|
|---|
| 259 | ResponseData: TVarBlockIndexed;
|
|---|
| 260 | RequestData: TVarBlockIndexed;
|
|---|
| 261 | CommandIndex: TListInteger;
|
|---|
| 262 | CommandStream: TVarBlockIndexed;
|
|---|
| 263 | SequenceNumber: integer;
|
|---|
| 264 | CommandError: integer;
|
|---|
| 265 | MessageType: integer;
|
|---|
| 266 | Command: TVarBlockIndexed;
|
|---|
| 267 | TempStream: TMemoryStream;
|
|---|
| 268 | begin
|
|---|
| 269 | try
|
|---|
| 270 | TempStream := TMemoryStream.Create;
|
|---|
| 271 | Command := TVarBlockIndexed.Create;
|
|---|
| 272 | CommandIndex := TListInteger.Create;
|
|---|
| 273 | Response := TVarBlockIndexed.Create;
|
|---|
| 274 | ResponseData := TVarBlockIndexed.Create;
|
|---|
| 275 | CommandStream := TVarBlockIndexed.Create;
|
|---|
| 276 | RequestData := TVarBlockIndexed.Create;
|
|---|
| 277 | ResponseCode := rcNone;
|
|---|
| 278 | Command.Enclose := False;
|
|---|
| 279 | Command.ReadFromStream(Stream);
|
|---|
| 280 | with Command do begin
|
|---|
| 281 | if TestIndex(0) then
|
|---|
| 282 | MessageType := ReadVarUInt(0);
|
|---|
| 283 | if TestIndex(1) then
|
|---|
| 284 | SequenceNumber := ReadVarUInt(1);
|
|---|
| 285 | if TestIndex(2) then
|
|---|
| 286 | ReadVarUIntArray(2, CommandIndex);
|
|---|
| 287 | if TestIndex(3) then
|
|---|
| 288 | ReadVarIndexedBlock(3, RequestData);
|
|---|
| 289 | if Assigned(FOnCommand) then
|
|---|
| 290 | FOnCommand(CommandIndex, RequestData, ResponseData, ResponseCode, CommandError)
|
|---|
| 291 | else
|
|---|
| 292 | ResponseCode := rcCommandNotSupported;
|
|---|
| 293 | end;
|
|---|
| 294 | with Response do begin
|
|---|
| 295 | Enclose := False;
|
|---|
| 296 | WriteVarUInt(0, Integer(mtResponse));
|
|---|
| 297 | WriteVarUInt(1, Integer(SequenceNumber));
|
|---|
| 298 | if ResponseData.Items.Count > 0 then
|
|---|
| 299 | WriteVarIndexedBlock(2, ResponseData);
|
|---|
| 300 | WriteVarUInt(3, Integer(ResponseCode));
|
|---|
| 301 | WriteVarUInt(4, Integer(CommandError));
|
|---|
| 302 | WriteToStream(TempStream);
|
|---|
| 303 | if ResponseCode <> rcDropped then
|
|---|
| 304 | Pin.Send(TempStream);
|
|---|
| 305 | end;
|
|---|
| 306 |
|
|---|
| 307 | if Assigned(FOnAfterRequest) then
|
|---|
| 308 | FOnAfterRequest(CommandIndex, RequestData, ResponseData,
|
|---|
| 309 | ResponseCode, CommandError);
|
|---|
| 310 | finally
|
|---|
| 311 | TempStream.Free;
|
|---|
| 312 | RequestData.Free;
|
|---|
| 313 | CommandStream.Free;
|
|---|
| 314 | ResponseData.Free;
|
|---|
| 315 | Response.Free;
|
|---|
| 316 | Command.Free;
|
|---|
| 317 | CommandIndex.Free;
|
|---|
| 318 | end;
|
|---|
| 319 | end;
|
|---|
| 320 |
|
|---|
| 321 | procedure TCommProtocol.SetActive(const AValue: Boolean);
|
|---|
| 322 | begin
|
|---|
| 323 | if FActive = AValue then Exit;
|
|---|
| 324 | FActive := AValue;
|
|---|
| 325 | if AValue then begin
|
|---|
| 326 | RetransmitThread := TRetransmitCheckThread.Create(True);
|
|---|
| 327 | with RetransmitThread do begin
|
|---|
| 328 | CheckPeriod := 100; // ms
|
|---|
| 329 | Parent := Self;
|
|---|
| 330 | FreeOnTerminate := False;
|
|---|
| 331 | Name := 'CommProtocol';
|
|---|
| 332 | Start;
|
|---|
| 333 | end;
|
|---|
| 334 | end else begin
|
|---|
| 335 | // Wait for empty session list
|
|---|
| 336 | try
|
|---|
| 337 | Sessions.Lock.Acquire;
|
|---|
| 338 | while Sessions.Count > 0 do
|
|---|
| 339 | try
|
|---|
| 340 | Sessions.Lock.Release;
|
|---|
| 341 | Sleep(1);
|
|---|
| 342 | finally
|
|---|
| 343 | Sessions.Lock.Acquire;
|
|---|
| 344 | end;
|
|---|
| 345 | finally
|
|---|
| 346 | Sessions.Lock.Release;
|
|---|
| 347 | end;
|
|---|
| 348 | FreeAndNil(RetransmitThread);
|
|---|
| 349 | end;
|
|---|
| 350 | end;
|
|---|
| 351 |
|
|---|
| 352 | procedure TCommProtocol.SendCommand(Command: array of integer;
|
|---|
| 353 | ResponseParameters: TVarBlockIndexed = nil; RequestParameters: TVarBlockIndexed = nil;
|
|---|
| 354 | ARaiseError: boolean = True);
|
|---|
| 355 | var
|
|---|
| 356 | Session: TDeviceProtocolSession;
|
|---|
| 357 | NewRequest: TVarBlockIndexed;
|
|---|
| 358 | begin
|
|---|
| 359 | if FActive then begin
|
|---|
| 360 | try
|
|---|
| 361 | //Lock.Acquire;
|
|---|
| 362 | Session := TDeviceProtocolSession.Create;
|
|---|
| 363 | Sessions.Add(Session);
|
|---|
| 364 | NewRequest := TVarBlockIndexed.Create;
|
|---|
| 365 |
|
|---|
| 366 | Session.ResponseParameters := ResponseParameters;
|
|---|
| 367 | with Session do begin
|
|---|
| 368 | try
|
|---|
| 369 | Lock.Acquire;
|
|---|
| 370 | CommandIndex.Clear;
|
|---|
| 371 | CommandIndex.AddArray(Command);
|
|---|
| 372 | with NewRequest do begin
|
|---|
| 373 | Enclose := False;
|
|---|
| 374 | WriteVarUInt(0, Integer(mtRequest));
|
|---|
| 375 | WriteVarUInt(1, SequenceNumber);
|
|---|
| 376 | WriteVarUIntArray(2, CommandIndex);
|
|---|
| 377 | if Assigned(RequestParameters) then
|
|---|
| 378 | WriteVarIndexedBlock(3, RequestParameters);
|
|---|
| 379 | end;
|
|---|
| 380 | RaiseError := ARaiseError;
|
|---|
| 381 | NewRequest.WriteToStream(Request);
|
|---|
| 382 |
|
|---|
| 383 | RemoteBuffer.Allocate(Request.Size);
|
|---|
| 384 |
|
|---|
| 385 | //StopWatch.Start;
|
|---|
| 386 | TransmitTime := Now;
|
|---|
| 387 | Pin.Send(Request);
|
|---|
| 388 | Enabled := True;
|
|---|
| 389 | finally
|
|---|
| 390 | Lock.Release;
|
|---|
| 391 | end;
|
|---|
| 392 | try
|
|---|
| 393 | while ReceiveEvent.WaitFor(1) = wrTimeout do begin
|
|---|
| 394 | if Timeouted then
|
|---|
| 395 | raise ECommTimeout.Create(SResponseTimeout);
|
|---|
| 396 | end;
|
|---|
| 397 | if ResponseCode <> Integer(rcNone) then begin
|
|---|
| 398 | if Assigned(FOnDebugLog) then
|
|---|
| 399 | FOnDebugLog(SDeviceProtocol, Format(SResponseError, [CommandIndex.Implode('.', IntToStr), IntToStr(ResponseCode)]));
|
|---|
| 400 | raise ECommResponseCodeError.Create(Format(SResponseError, [CommandIndex.Implode('.', IntToStr), IntToStr(ResponseCode)]));
|
|---|
| 401 | end;
|
|---|
| 402 | LastCommandResponseTime := Now;
|
|---|
| 403 | LastLatency := Latency;
|
|---|
| 404 | finally
|
|---|
| 405 | RemoteBuffer.Release(Session.Request.Size);
|
|---|
| 406 | Sessions.Remove(Session);
|
|---|
| 407 | end;
|
|---|
| 408 | end;
|
|---|
| 409 | finally
|
|---|
| 410 | NewRequest.Free;
|
|---|
| 411 | //Lock.Free;
|
|---|
| 412 | end;
|
|---|
| 413 | end else raise ENotActive.Create(SProtocolNotActive);
|
|---|
| 414 | end;
|
|---|
| 415 |
|
|---|
| 416 | constructor TCommProtocol.Create;
|
|---|
| 417 | begin
|
|---|
| 418 | RemoteBuffer := TRemoteBuffer.Create;
|
|---|
| 419 | Lock := TCriticalSection.Create;
|
|---|
| 420 | Pin := TCommPin.Create;
|
|---|
| 421 | Pin.OnReceive := DataReceive;
|
|---|
| 422 | Sessions := TDeviceProtocolSessionList.Create;
|
|---|
| 423 | Sessions.Parent := Self;
|
|---|
| 424 | RetransmitTimeout := 3 * OneSecond;
|
|---|
| 425 | RetransmitRepeatCount := 3;
|
|---|
| 426 | RetransmitTotalCount := 0;
|
|---|
| 427 | end;
|
|---|
| 428 |
|
|---|
| 429 | destructor TCommProtocol.Destroy;
|
|---|
| 430 | begin
|
|---|
| 431 | Active := False;
|
|---|
| 432 | Sessions.Free;
|
|---|
| 433 | Pin.Free;
|
|---|
| 434 | Lock.Free;
|
|---|
| 435 | RemoteBuffer.Free;
|
|---|
| 436 | inherited;
|
|---|
| 437 | end;
|
|---|
| 438 |
|
|---|
| 439 | procedure TCommProtocol.Assign(Source: TCommProtocol);
|
|---|
| 440 | begin
|
|---|
| 441 | LastCommandResponseTime := Source.LastCommandResponseTime;
|
|---|
| 442 | LastLatency := Source.LastLatency;
|
|---|
| 443 | RemoteBuffer.Assign(Source.RemoteBuffer);
|
|---|
| 444 | WrongSequenceCount := Source.WrongSequenceCount;
|
|---|
| 445 | RetransmitTimeout := Source.RetransmitTimeout;
|
|---|
| 446 | RetransmitRepeatCount := Source.RetransmitRepeatCount;
|
|---|
| 447 | RetransmitTotalCount := Source.RetransmitTotalCount;
|
|---|
| 448 | Pin.Connect(Source.Pin.RemotePin);
|
|---|
| 449 | OnCommand := Source.OnCommand;
|
|---|
| 450 | OnAfterRequest := Source.OnAfterRequest;
|
|---|
| 451 | OnDebugLog := Source.OnDebugLog;
|
|---|
| 452 | Sessions.Assign(Source.Sessions);
|
|---|
| 453 | Active := Source.Active;
|
|---|
| 454 | end;
|
|---|
| 455 |
|
|---|
| 456 | { TDeviceProtocolSession }
|
|---|
| 457 |
|
|---|
| 458 | constructor TDeviceProtocolSession.Create;
|
|---|
| 459 | begin
|
|---|
| 460 | ResponseCode := 0;
|
|---|
| 461 | Lock := TCriticalSection.Create;
|
|---|
| 462 | ReceiveEvent := TSimpleEvent.Create;
|
|---|
| 463 | //ReceiveEvent.ManualReset := True;
|
|---|
| 464 | Request := TStreamHelper.Create;
|
|---|
| 465 | ResponseParameters := nil;
|
|---|
| 466 | CommandIndex := TListInteger.Create;
|
|---|
| 467 | Latency := 0;
|
|---|
| 468 | TransmitTime := 0;
|
|---|
| 469 | end;
|
|---|
| 470 |
|
|---|
| 471 | destructor TDeviceProtocolSession.Destroy;
|
|---|
| 472 | begin
|
|---|
| 473 | CommandIndex.Free;
|
|---|
| 474 | Request.Free;
|
|---|
| 475 | ReceiveEvent.Free;
|
|---|
| 476 | Lock.Free;
|
|---|
| 477 | inherited Destroy;
|
|---|
| 478 | end;
|
|---|
| 479 |
|
|---|
| 480 | { TDeviceProtocolSessionList }
|
|---|
| 481 |
|
|---|
| 482 | procedure TDeviceProtocolSessionList.Add(Session: TDeviceProtocolSession);
|
|---|
| 483 | begin
|
|---|
| 484 | // Block if no free session available
|
|---|
| 485 | try
|
|---|
| 486 | Lock.Acquire;
|
|---|
| 487 | Session.SequenceNumber := GetSequenceNumber;
|
|---|
| 488 | while Count >= MaxSessionCount do
|
|---|
| 489 | begin
|
|---|
| 490 | try
|
|---|
| 491 | Lock.Release;
|
|---|
| 492 | Sleep(1);
|
|---|
| 493 | finally
|
|---|
| 494 | Lock.Acquire;
|
|---|
| 495 | end;
|
|---|
| 496 | end;
|
|---|
| 497 | inherited Add(Session);
|
|---|
| 498 | finally
|
|---|
| 499 | Lock.Release;
|
|---|
| 500 | end;
|
|---|
| 501 | end;
|
|---|
| 502 |
|
|---|
| 503 | function TDeviceProtocolSessionList.GetBySequence(Sequence: integer):
|
|---|
| 504 | TDeviceProtocolSession;
|
|---|
| 505 | var
|
|---|
| 506 | I: integer;
|
|---|
| 507 | begin
|
|---|
| 508 | I := 0;
|
|---|
| 509 | while (I < Count) and (TDeviceProtocolSession(Items[I]).SequenceNumber <> Sequence) do
|
|---|
| 510 | Inc(I);
|
|---|
| 511 | if I < Count then
|
|---|
| 512 | Result := TDeviceProtocolSession(Items[I])
|
|---|
| 513 | else
|
|---|
| 514 | Result := nil;
|
|---|
| 515 | end;
|
|---|
| 516 |
|
|---|
| 517 | procedure TDeviceProtocolSessionList.Remove(Session: TDeviceProtocolSession);
|
|---|
| 518 | begin
|
|---|
| 519 | try
|
|---|
| 520 | Lock.Acquire;
|
|---|
| 521 | inherited Remove(TObject(Session));
|
|---|
| 522 | finally
|
|---|
| 523 | Lock.Release;
|
|---|
| 524 | end;
|
|---|
| 525 | end;
|
|---|
| 526 |
|
|---|
| 527 | constructor TDeviceProtocolSessionList.Create;
|
|---|
| 528 | begin
|
|---|
| 529 | inherited Create;
|
|---|
| 530 | Lock := TCriticalSection.Create;
|
|---|
| 531 | MaxSessionCount := 100;
|
|---|
| 532 | MaxSequenceNumber := 127;
|
|---|
| 533 | end;
|
|---|
| 534 |
|
|---|
| 535 | destructor TDeviceProtocolSessionList.Destroy;
|
|---|
| 536 | begin
|
|---|
| 537 | // Free session list before freeing Lock
|
|---|
| 538 | // instead of freeing in inherited Destroy in TListObject
|
|---|
| 539 | try
|
|---|
| 540 | // Lock.Acquire;
|
|---|
| 541 | Clear;
|
|---|
| 542 | finally
|
|---|
| 543 | // Lock.Release;
|
|---|
| 544 | end;
|
|---|
| 545 |
|
|---|
| 546 | Lock.Free;
|
|---|
| 547 | inherited;
|
|---|
| 548 | end;
|
|---|
| 549 |
|
|---|
| 550 | procedure TDeviceProtocolSessionList.Assign(Source: TListObject);
|
|---|
| 551 | begin
|
|---|
| 552 | MaxSequenceNumber := TDeviceProtocolSessionList(Source).MaxSequenceNumber;
|
|---|
| 553 | MaxSessionCount := TDeviceProtocolSessionList(Source).MaxSessionCount;
|
|---|
| 554 | SequenceNumber := TDeviceProtocolSessionList(Source).SequenceNumber;
|
|---|
| 555 |
|
|---|
| 556 | inherited;
|
|---|
| 557 | end;
|
|---|
| 558 |
|
|---|
| 559 | function TDeviceProtocolSessionList.GetSequenceNumber: Integer;
|
|---|
| 560 | begin
|
|---|
| 561 | try
|
|---|
| 562 | //Lock.Acquire;
|
|---|
| 563 | Inc(SequenceNumber);
|
|---|
| 564 | if SequenceNumber > MaxSequenceNumber then
|
|---|
| 565 | SequenceNumber := 0;
|
|---|
| 566 | Result := SequenceNumber;
|
|---|
| 567 | finally
|
|---|
| 568 | //Lock.Release;
|
|---|
| 569 | end;
|
|---|
| 570 | end;
|
|---|
| 571 |
|
|---|
| 572 | { TRetransmitCheckThread }
|
|---|
| 573 |
|
|---|
| 574 | procedure TRetransmitCheckThread.Execute;
|
|---|
| 575 | var
|
|---|
| 576 | I: Integer;
|
|---|
| 577 | C: Integer;
|
|---|
| 578 | Session: TDeviceProtocolSession;
|
|---|
| 579 | begin
|
|---|
| 580 | with Parent do
|
|---|
| 581 | repeat
|
|---|
| 582 | try
|
|---|
| 583 | Parent.Sessions.Lock.Acquire;
|
|---|
| 584 | I := 0;
|
|---|
| 585 | while I < Sessions.Count do begin
|
|---|
| 586 | Session := TDeviceProtocolSession(Sessions[I]);
|
|---|
| 587 | with Session do
|
|---|
| 588 | if Enabled then begin
|
|---|
| 589 | try
|
|---|
| 590 | Session.Lock.Acquire;
|
|---|
| 591 | if (TransmitTime > 0) and (Now > (TransmitTime + RetransmitTimeout)) then begin
|
|---|
| 592 | if RepeatCounter < RetransmitRepeatCount then begin
|
|---|
| 593 | Pin.Send(Request);
|
|---|
| 594 | TransmitTime := Now;
|
|---|
| 595 | Inc(RepeatCounter);
|
|---|
| 596 | Inc(RetransmitTotalCount);
|
|---|
| 597 | end else
|
|---|
| 598 | Timeouted := True;
|
|---|
| 599 | end;
|
|---|
| 600 | finally
|
|---|
| 601 | Session.Lock.Release;
|
|---|
| 602 | end;
|
|---|
| 603 | end;
|
|---|
| 604 | Inc(I);
|
|---|
| 605 | end;
|
|---|
| 606 | finally
|
|---|
| 607 | Parent.Sessions.Lock.Release;
|
|---|
| 608 | end;
|
|---|
| 609 |
|
|---|
| 610 | if not Terminated then
|
|---|
| 611 | Sleep(CheckPeriod);
|
|---|
| 612 | until Terminated;
|
|---|
| 613 | end;
|
|---|
| 614 |
|
|---|
| 615 | end.
|
|---|
| 616 |
|
|---|