unit uScanner;

interface

uses
  Generics.Collections;


type
  TBarcode = class
  private
    fTimestamp : TDateTime;
    fCode : string;
  public
    constructor Create(aTimestamp : TDateTime; aCode : string);
    property TimeStamp : TDateTime read fTimestamp;
    property Code : string read fCode;
  end;

   T4Bytes = packed record
   case Integer of
    0: (Bytes: array[0..3] of Byte);
    1: (Total: Cardinal);
  end;

  TScanner = class
  private
    fPort : integer;
    fReady : boolean;
    fHasData: boolean;
    function TimestampToDateTime(aTimestamp : Ansistring) : TDateTime;
  public
    constructor Create(aPort : integer);
    destructor Destroy; override;
    function getBarcodes : TList<TBarcode>;
    procedure clearBarcodes;
    property IsReady : boolean read fReady;
    property HasData : boolean read fHasData;
    procedure setDateTime(aDateTime : TDateTime);
    function getDateTime : TDateTime;
  end;


implementation

uses
  uCSP2, Dialogs, SysUtils, Classes;

{ TBarcode }

constructor TBarcode.Create(aTimestamp: TDateTime; aCode: string);
begin
  fTimestamp := aTimestamp;
  fCode := aCode;
end;

{ TScanner }

procedure TScanner.clearBarcodes;
begin
  csp2Init(fPort);
  csp2ClearData;
end;

constructor TScanner.Create(aPort: integer);
begin
  fPort := aPort -1;
  fReady := csp2Init(fPort) = STATUS_OK;
  sleep(150);
  if fReady then
    fHasData := csp2DataAvailable = DATA_AVAILABLE
  else
    fHasData := false;
end;

destructor TScanner.Destroy;
begin
  csp2Restore;
  inherited;
end;

function TScanner.getBarcodes: TList<TBarcode>;
var
  bc : TBarcode;
  i, count, pl : integer;
  buffer : array[0..63] of AnsiChar;
  dt : TDateTime;
  s : string;
begin
  result := TList<TBarcode>.Create;
  begin
    count := csp2ReadData;
    for i := 0 to count -1 do
    begin
      FillChar(buffer,64,#0);
      pl := csp2GetPacket(@buffer,i,63);

      // The Last 4 Bytes hold the timestamp
      dt := TimestampToDateTime(copy(buffer,pl-3,4));

      s := copy(buffer,3,pl-6);
      bc := TBarcode.Create(dt,s);
      result.Add(bc);
    end;
  end;
end;

function TScanner.getDateTime: TDateTime;
var
  buf : array[0..5] of AnsiChar;
  y,m,d,h,n,s : word;
begin
  csp2GetTime(@buf);
  y := Ord(buf[5]) + 2000;
  m := Ord(buf[4]);
  d := Ord(buf[3]);
  h := Ord(buf[2]);
  n := Ord(buf[1]);
  s := Ord(buf[0]);

  result := EncodeDate(y,m,d) + EncodeTime(h,n,s,0);
end;

procedure TScanner.setDateTime(aDateTime: TDateTime);
var
  st : Ansistring;
  y,m,d,h,n,s,ms : word;
begin
  DecodeDate(aDateTime,y,m,d);
  DecodeTime(aDateTime,h,n,s,ms);

  st := Chr(s)+Chr(n)+Chr(h)+Chr(d)+Chr(m)+Chr(y-2000);

  csp2SetTime(PAnsiChar(st));
end;

function TScanner.TimestampToDateTime(aTimestamp: Ansistring): TDateTime;
var
  i1,i2,i3,i4,i5,i6 : integer;
  qb : T4Bytes;
  y,m,d,h,n,s : word;
begin
  // CharToByte
  qb.Bytes[3] := Ord(aTimestamp[1]);
  qb.Bytes[2] := Ord(aTimestamp[2]);
  qb.Bytes[1] := Ord(aTimestamp[3]);
  qb.Bytes[0] := Ord(aTimestamp[4]);

  { 00000000000000000000000000000000 = 32 Bit
    |----| = seconds
          |----| = minute
                |---|  = hour
                     |---| = day
                          |--| = month
                              |----| = year
  }
  i1 := qb.Total and $fc000000; // 6 Bit
  i2 := qb.Total and $03f00000; // 6 Bit
  i3 := qb.Total and $000f8000; // 5 Bit
  i4 := qb.Total and $00007c00; // 5 Bit
  i5 := qb.Total and $000003c0; // 4 Bit
  i6 := qb.Total and $0000003f; // 6 Bit

  // Shift right to align the bits
  s := i1 shr 26;
  n := i2 shr 20;
  h := i3 shr 15;
  d := i4 shr 10;
  m := i5 shr 6;
  y := i6;

  result := EncodeDate(y+2000,m,d) + EncodeTime(h,n,s,0);
end;

end.

