unit Dalltran;{interactive transfer
  of data for selected doors
  and woorkup of the data}
{$I DEFINE}
interface
uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  StdCtrls, Forms, DBCtrls, DB, DBGrids, Grids, ExtCtrls,
  DBTables,

  main, showmsg, progbox, datedlg,{timer}
  TVType, mytype, modutype, compareu, {msgu}
  chrntype, dalltype, dallutl, dallrdu,
  globals, ttblu, binhex, country, confu, conftype,
  logtype,  dateu, fileu, showinfo, mylib, stru, numlist
  ;


function FindUserByTouchIDStr(ATouchIDStr:TTouchIDStr; StayIfFound:boolean):boolean;
function FindDoorByTouchIDStr(ATouchIDStr:TTouchIDStr; StayIfFound:boolean):boolean;

function IsAlreadyAssigned(ATouchIDStr:TTouchIDStr):boolean;
{calls the above, pops up info that it is already assigned}

procedure TransferData(tt:TTransferType);{chrntype}

const
  Transfering:boolean=false;

function WorkupFile(const AFileName:string; DTbl:TTable):boolean;

function GetDoorIDFromConfigs(const DCfg:TChConfig; const UCfg:TChConfig;
  var ADoorID:TDoorID):boolean;

function GetInfoAboutTouch(var TblTouchID:TMediumID; var DCfg:TChConfig;
  var UCfg:TChConfig):boolean;

implementation

procedure IncludeNonTransferedSynchro(S:word);
begin
  NonTransferedSynchros := ' ' + trim(NonTransferedSynchros) + ' ';
  AddNumberToList(S, NonTransferedSynchros);
  ConfigReadWriteValue(rwWriteFlush, EnvSectionName, 'NonTransferedSynchros',
    @NonTransferedSynchros, ptString);
  NonTransferedSynchros := ' ' + trim(NonTransferedSynchros) + ' ';
end;

procedure ExcludeNonTransferedSynchro(S:word);
begin
  NonTransferedSynchros := ' ' + trim(NonTransferedSynchros) + ' ';
  DeleteNumberFromList(S, NonTransferedSynchros);
  ConfigReadWriteValue(rwWriteFlush, EnvSectionName, 'NonTransferedSynchros',
    @NonTransferedSynchros, ptString);
  NonTransferedSynchros := ' ' + trim(NonTransferedSynchros) + ' ';
end;

function IsNonTransferedSynchro(S:word):boolean;
begin
  IsNonTransferedSynchro := NumberIsInList(S, NonTransferedSynchros);
end;

function SynchroToDoorID(ASynchro:TSynchro):integer;
begin
  SynchroToDoorID := ASynchro div 2;
end;

function GetDoorIDFromConfigs(const DCfg:TChConfig; const UCfg:TChConfig;
  var ADoorID:TDoorID):boolean;
begin
  GetDoorIDFromConfigs := false;
  if UCfg.Synchro = InitSynchro then
    ADoorID := SynchroToDoorID(DCfg.Synchro)
  else
    ADoorID := SynchroToDoorID(UCfg.Synchro);
  GetDoorIDFromConfigs := true;
end;

var
  temptbl:TChTable;

function GetInfoAboutTouch(var TblTouchID:TMediumID; var DCfg:TChConfig;
  var UCfg:TChConfig):boolean;
var
  outtime:longint;
  ReadTouchID:TMediumID;
  downoffs,upoffs:word;
begin
  downoffs := (DownConfigOffset div 32) * 32;
  upoffs := (UpConfigOffset div 32) * 32;
  GetInfoAboutTouch := false;
  {InfoFormShow('Vložte čip do čtečky');}
  outtime := GetTickCount + 1000;{globals .TouchTransferTimeout;}
  repeat
    if GetTickCount > outtime then
    begin
      {InfoFormHide;}
      ShowMessage('Timeout - přenosové medium nebylo přiloženo dostatečně dlouho.', smError, 0);
      exit;
    end;
  until GetTouchID(globals .TouchPortNr, ReadTouchID);
  {InfoFormHide;}

  if not IsMemTouchID(ReadTouchID, true) then begin
    exit;
  end;

  if compareu.CompareRec(TblTouchID, ReadTouchID, TouchIDLen) <> 0 then
  begin
    ShowMessage('Čip byl změněn, vložte původní čip nebo opakujte dotek.',smError,0);
    exit;
  end;

  if not TouchBlockToBuf(TouchPortNr, downoffs, CHBlockSize, temptbl.Data[downoffs]) then
  begin
    {$IFDEF DEBUG}
    ShowMessage('TouchBlockToBuff downoffs failed.',smError,0);
    {$ENDIF}
    exit;
  end;
  if not TouchBlockToBuf(TouchPortNr, upoffs, CHBlockSize, temptbl.Data[upoffs]) then
  begin
    {$IFDEF DEBUG}
    ShowMessage('TouchBlockToBuff upoffs failed.',smError,0);
    {$ENDIF}
    exit;
  end;
  UCfg := temptbl.UpConfig;
  DCfg := temptbl.DownConfig;
  GetInfoAboutTouch := true;
end;


function FindUserByTouchIDStr(ATouchIDStr:TTouchIDStr; StayIfFound:boolean):boolean;
var indn:string;
  b:TBookmark;
  r:boolean;

begin
  indn := MainForm.UsersTable.IndexName;
  b := MainForm.UsersTable.GetBookMark;
  MainForm.UsersTable.DisableControls;
  MainForm.UsersTable.IndexName := inMediumID;

  r := MainForm.UsersTable.FindKey([ATouchIDStr]);

  MainForm.UsersTable.IndexName := indn;
  if not StayIfFound then
    MainForm.UsersTable.GotoBookmark(b);
  MainForm.UsersTable.FreeBookmark(b);

  MainForm.UsersTable.EnableControls;
  FindUserByTouchIDStr := r;
end;

function FindDoorByTouchIDStr(ATouchIDStr:TTouchIDStr; StayIfFound:boolean):boolean;
var indn:string;
  b:TBookmark;
  r:boolean;
begin
  indn := MainForm.DoorsTable.IndexName;
  b := MainForm.DoorsTable.GetBookMark;
  MainForm.DoorsTable.IndexName := inMediumID;

  r := MainForm.DoorsTable.FindKey([ATouchIDStr]);

  MainForm.DoorsTable.IndexName := indn;
  if not StayIfFound then
    MainForm.DoorsTable.GotoBookmark(b);
  MainForm.DoorsTable.FreeBookmark(b);

  FindDoorByTouchIDStr := r;
end;

function IsAlreadyAssigned(ATouchIDStr:TTouchIDStr):boolean;
var r:boolean;
begin
  r := FindUserByTouchIDStr(ATouchIDStr, false);
  if r then begin
    ShowMessage('Medium je již přiřazeno', smError, 0);
  end;
  IsAlreadyAssigned := r;
end;

{
function GetDoorT2FName(ADoorID:TDoorID):string;
var s:string;
begin
  str(ADoorID, s);
  while length(s) < 5 do s := '0' + s;
  GetDoorT2FName := 'D' + s + '.T2F';
end;

function GetDoorF2TName(ADoorID:TDoorID):string;
var s:string;
begin
  str(ADoorID, s);
  while length(s) < 5 do s := '0' + s;
  GetDoorF2TName := 'D' + s + '.F2T';
end;
}

function DBToTouchTable(UTbl:TTable; DTbl:TTable; var ATbl:TCHTable):boolean;
var
  prio:TPriority;{dalltype, chrntype}

  uid:TUserID;
  did:TDoorID;
  gid:TGroupID;

{  uids:TUserIDStr;}
  dids:TDoorIDStr;
{  gids:TGroupIDStr;}

  dts, ts:TTouchIDStr;
  tr:TCHTouchRecord;
  tpos:word;
  tid:TMediumID;
  {ttblu}
  ttabl:PTouchTable;
  trec:TTouchRecord;{ttblu}
  canbe:boolean;
  usefil:boolean;
  code:integer;

  UGTbl, GDTbl:TTable;


  function BelongsToGroup:boolean;
  {var s:string;}
  var
    d,g:integer;
    u:longint;
  begin
    BelongsToGroup:= false;
    if UGTbl.FindKey([uid]) then begin
      repeat
        if UGTbl.EOF then
          break;
        u := UGTbl.FieldByName(fnUserID).AsInteger;
        if u <> uid then
          break;
        gid := UGTbl.FieldByName(fnGroupID).AsInteger;
        if GDTbl.FindKey([gid]) then begin
          repeat
            if GDTbl.EOF then
              break;
            g := GDTbl.FieldByName(fnGroupID).AsInteger;
            if g <> gid then
              break;
            d := GDTbl.FieldByName(fnDoorID).AsInteger;
            if (d = did) then begin
              BelongsToGroup := true;
              exit;
            end;
            GDTbl.Next;
          until false;
        end else begin
          {i.e. no doors assigned to this group}
        end;
        UGTbl.Next;
      until false;
    end;
  end;

  procedure FillTouchTable(var ATTbl:TCHTouchTable);
  var
    i:integer;
    tr:TCHTouchRecord;
    {b :byte;}
  begin
    {b := random(255) + 1;}
    fillchar(tr, sizeof(tr), $FF);
    {tr.Info := $FF;
    tr.TouchID[TouchIDLen - 1] := $FF;}
    {for i := 0 to TouchIDLen - 2 do
      tr.TouchID[i] := b;}
    for i := 0 to CHTouchTableSize - 1 do
      ATTbl[i] := tr;
  end;


label ex;

begin
  DBToTouchTable:= false;{ttable}
  ttabl := nil;
  if not TTInit(UTbl.RecordCount, GetExeDir, ttabl) then begin
    exit;
  end;{... sorted memory table of touch records inited}
  {change default behavior of the TouchTable:}
  canbe := true;
  TTSetProp(ttabl, tpCardIDCanBeZero, @canbe);
  usefil := false;
  TTSetProp(ttabl, tpUseFile, @usefil);

  UGTbl := MainForm.UserGroupsTable;
  GDTbl := MainForm.GroupDoorsTable;
  UGTbl.IndexName := inUserID;
  GDTbl.IndexName := inGroupID;

  prio := DTbl.FieldByName(fnPriority).AsInteger;
  dts := DTbl.FieldByName(fnMediumID).AsString;
  did := DTbl.FieldByName(fnDoorID).AsInteger;
  dids := IntToString(did, DoorIDLen);

  UTbl.IndexName := inMediumID;
  UTbl.First;
  FillTouchTable(ATbl.TouchTable);
  FillChar(ATbl.TimeTable, sizeof(ATbl.TimeTable), 0);
  tpos := 0;

  while not UTbl.EOF do begin
    uid := UTbl.FieldByName(fnUserID).AsInteger;
    ts := UTbl.FieldByName(fnMediumID).AsString;
    if (UTbl.FieldByName(fnPriority).AsInteger <= prio) or
      BelongsToGroup
      {v1.08}
      or (ts = dts)
      {/v1.08}
    then begin
      code := UTbl.FieldByName(fnCode).AsInteger;
        {.. code = tcNormal - user, or tcTranfer - transfer medium }
      if ts <> '' then begin
        if true{(code = tcNormal) or
           ((code = tcTransfer) and (ts = dts))}
        then begin
          SetTouchIDStr(ts, tid);{dallutl}
          if not TTAddRecord(ttabl, tid, UTbl.FieldByName(fnCode).AsInteger) then begin
            ShowMessage('DBToTouch: Tabulka medii je plna', smError,0);
            goto ex;
          end;
        end;
      end;
    end;
    UTbl.Next;
  end;

  tpos := 0;
  TTReset(ttabl);{ttblu}
  while TTReadTouchRecord(ttabl, trec) do begin
    if tpos = CHTouchTableSize then begin
      ShowMessage('Tabulka registrovaných čipů je plná.', smError, 0);
      break;
    end;
    move(trec.MediumID, tr.TouchID, sizeof(tr.TouchID));
    tr.Info := trec.CardID;
    ATbl.TouchTable[tpos] := tr;
    inc(tpos);
  end;
  DBToTouchTable:= true;
ex:
  TTDone(ttabl);
end;

type
  TLastTransferInfo = record
    Date:array[1..2] of TDateString;
    Time:array[1..2] of TTimeString;
    uploaded:integer; {were the data uploaded to touch (synchro changed?)
                       = 0 if not uploaded, 1 or 2 if yes according to synchro1 or 2}
    downloaded:integer; {as above but for time data download}
  end;


procedure TimeTableToDB(
  const ATouchTbl:TCHTable;
  const ATimeTbl:TCHTable;
  var LastTransfer:TLastTransferInfo;
  HTbl:TTable;
  DoorID:TDoorID
);
  {called after transfer is finished, if some time data were
   received, and converts them to database HTbl}
var
  lastps,           {last and cur position in timetable}
  curps:word;

  starttime,        {when the module started to register, time
                     of previous communication with access module}
  endtime,          {time of the last communication with
                     access module, i.e. when the time counter stopped}
  curtime,          {starttime + (curticks + time)* tickinsec }
  downtime:PDTO;    {time of download data from module}
  tmptime:PDTO;     {used just for debugging}

  curticks:longint; {time since module start, increased during reading data
                     if $FD encountered = tfCounterOverflow}
  tickinsec:real;   {how many seconds is one tick}
  allticks:longint; {accumulated in module from start to end}

  run:byte;         {how many times the main data reading cycle was done}
  tid:TMediumID;
  cnt:longint;      {made entries count}
  resetpos:integer; {<>-1  if during first run WarmReset mark found
                     or if time data overflowed (i.e. reset could be there)}

  DateFld:TField;
  TimeFld:TField;
  MediumIDFld:TField;
  DoorIDFld:TField;
  startTimeKnown:boolean;
  intervalInRealTimeSeconds:longint;
     { calculated from starttime and endtime if both known,
       used to make aproximation for systematic clock error }
  intervalInTickSeconds:longint;
     { calculated from ticks from module }
  FixTimeCoef:real;
     { calculated as average from FixTimeCoefMin and FixTimeCoefMax;
       those are calculated as:
         (intervalInRealTimeSeconds / intervalInTickSeconds) }

  syncOrd:integer; {synchro order = 1 or 2}
  allrecs:longint;
    { all number of records that will be added to history table,
      counted in run=1 }
  currec:longint;
    { currently added records count }
  pb:TProgressBox;

  procedure SetResetPos;
  begin
    resetpos := curps;
  end;

  procedure AppendHistoryRecord;
  var
    ts:TTouchIDStr;
    tid:TMediumID;
    trec :TCHTimeRecord;
    ticksToEnd:longint;
    ticksFromStart:longint;
    curSecs:longint;
  begin
    {dallutl chrntype}
    trec := ATimeTbl.TimeTable[curps];
    fillchar(tid, sizeof(tid), 0);
    move(ATouchTbl.TouchTable[trec.Index].TouchID, tid, sizeof(TTouchID7));
    if IsFFTouchID(tid) then
      exit;
    if startTimeKnown then
    begin
      if (resetpos = -1) then begin
        {(resetpos = -1) - no resets; aproximate for possible clock
        systematic error}
        DTAssign(curtime, starttime);
        ticksFromStart := curticks + trec.Time;
        curSecs := round(ticksFromStart * tickinsec * FixTimeCoef);
        DTDo(curtime, dtIncSecond, curSecs);
          {round(ticksFromStart * tickinsec));}

        {ticksToEnd := allticks - curticks - trec.Time;
        DTAssign(tmptime, endtime);
        DTDo(tmptime, dtIncSecond, - round(ticksToEnd * tickinsec));
        if DTGetStr(curtime, dtDateTimeString) <> DTGetStr(tmptime, dtDateTimeString) then
        begin
          ticksToEnd := ticksToEnd;
        end;
        }
      end else if (curps < resetpos) then begin
        DTAssign(curtime, starttime);
        ticksFromStart := curticks + trec.Time;
        DTDo(curtime, dtIncSecond, round(ticksFromStart * tickinsec * FixTimeCoef));
      end else begin
        ticksToEnd := allticks - curticks - trec.Time;
        DTAssign(curtime, endtime);
        DTDo(curtime, dtIncSecond, - round(ticksToEnd * tickinsec * FixTimeCoef));
      end;
    end else begin
      ticksToEnd := allticks - curticks - trec.Time;
      DTAssign(curtime, endtime);
      DTDo(curtime, dtIncSecond,
        - round(ticksToEnd * tickinsec));
    end;

    HTbl.Append;
    DateFld.AsString := DateStringToDateString(
      DTGetStr(curtime, dtDateString), dfRAW, dfEurope);
    TimeFld.AsString := copy(
      TimeStringToTimeString(
        DTGetStr(curtime, dtTimeString), dfRAW, dfEurope
      ), 1, 5
    );
    ts := GetTouchIDStr(tid);
    MediumIDFld.AsString := ts;
    DoorIDFld.AsInteger := DoorID;
    HTbl.Post;
  end;

  procedure CheckCorrectStartTime;
  var
    d:TDateString;t:TTimeString;
    c:real;msg:string;
  begin
    if resetpos < 0 then begin
      { no loss of electricity (no warmreset), i.e. time can be calculated:}
      repeat
        if (intervalInRealTimeSeconds < 0) or
           ((abs(intervalInRealTimeSeconds - intervalInTickSeconds) /
            intervalInRealTimeSeconds * 100) > AllowedTimeDifProc) then
        begin
          DTAssign(starttime, endtime);
          DTDo(starttime, dtIncSecond, - intervalInTickSeconds);
          intervalInRealTimeSeconds := intervalInTickSeconds;
          d := DTGetStr(starttime, dtDateString);
          t := copy(DTGetStr(starttime, dtTimeString), 1, 4);
          if not SelectDateTime('Počáteční čas mimo rozsah, ' +
            'zkontrolujte novou hodnotu:',d, t) then
          begin
            if ShowMessage('Ignorovat počáteční čas?', smNoYes, 0) = cmYes
            then begin
              startTimeKnown := false;
              break;
            end;
          end else begin
            LastTransfer.Date[LastTransfer.Downloaded] := d;
            LastTransfer.Time[LastTransfer.Downloaded] := t;

            if (d <> DTGetStr(starttime, dtDateString)) or
              (t <> DTGetStr(starttime, dtTimeString)) then
            begin
              {don't reset the starttime if not changed by user, because
               seconds diffrence in very short times can cause, that
               the difference won't fit in allowed difference interval}
              DTSetStr(starttime, dtDateTimeString, d + t);
            end;
            intervalInRealTimeSeconds := DTGetInterval(starttime, endtime, tuSecond);
          end;
        end else begin
          if UseFixTimeCoef then begin
            if intervalInRealTimeSeconds >= MinAutoFixPeriod then begin
              c := intervalInRealTimeSeconds / intervalInTickSeconds;
              if (c < FixTimeCoefMin) or (FixTimeCoefMin = 0) then{globals}
                FixTimeCoefMin := c;
              if (c > FixTimeCoefMax) or (FixTimeCoefMax = 0) then
                FixTimeCoefMax := c;
            end;
            if (FixTimeCoefMin <> 0) and (FixTimeCoefMax <> 0) then
              FixTimeCoef := (FixTimeCoefMin + FixTimeCoefMax) / 2;
          end;
          break;
        end;
      until false;
    end else begin
      { there was warmreset, we don't know for how long, let's hope it
        was just once }
        (*
       d := DTGetStr(starttime, dtDateString);
       t := copy(DTGetStr(starttime, dtTimeString), 1, 4);
       if not SelectDateTime('Byl detekován výpadek proudu, '
         'zkontrolujte počáteční čas pro výpočet časů příchodů:',d, t) then
       begin
         if ShowMessage('Ignorovat počáteční čas?', smNoYes, 0) = cmYes
         then begin
           startTimeKnown := false;
           break;
         end;
       end else begin
         LastTransfer.Date[LastTransfer.Downloaded] := d;
         LastTransfer.Time[LastTransfer.Downloaded] := t;

         if (d <> DTGetStr(starttime, dtDateString)) or
           (t <> DTGetStr(starttime, dtTimeString)) then
         begin
           {don't reset the starttime if not changed by user, because
            seconds diffrence in very short times can cause, that
            the difference won't fit in allowed difference interval}
           DTSetStr(starttime, dtDateTimeString, d + t);
         end;
         intervalInRealTimeSeconds := DTGetInterval(starttime, endtime, tuSecond);
       end;
        *)
    end;
  end;

label ex;
begin
  downtime := DTInit;
  starttime := DTInit;
  curtime := DTInit;
  endtime := DTInit;
  tmptime:= DTInit;
  pb := NoProgressBox;
  if (FixTimeCoefMin <> 0) and (FixTimeCoefMax <> 0) and UseFixTimeCoef then
    FixTimeCoef := (FixTimeCoefMin + FixTimeCoefMax) / 2
  else
    FixTimeCoef := 1;
  FillChar(tid, sizeof(tid), 0);

  DoorIDFld := HTbl.FieldByName(fnDoorID);
  MediumIDFld := HTbl.FieldByName(fnMediumID);
  DateFld := HTbl.FieldByName(fnDate);
  TimeFld := HTbl.FieldByName(fnTime);

  if (downtime = nil) or (starttime = nil) or (curtime = nil)
    or (endtime = nil)
  then
    goto ex;

  DTSetStr(starttime, dtDateTimeString, LastTransfer.Date[LastTransfer.DownLoaded]
    + LastTransfer.Time[LastTransfer.DownLoaded]);
  startTimeKnown := DTGet(starttime, dtIsValid) = dtOK;{dateu}
  if LastTransfer.DownLoaded = 1 then
    syncOrd := 2
  else
    syncOrd := 1;
  DTSetStr(endtime, dtDateTimeString, LastTransfer.Date[syncOrd] + LastTransfer.Time[syncOrd]);

  if (DTGet(endtime, dtIsValid) <> dtOK) or AlwaysAskForEndTime then begin
    repeat
      if not SelectDateTime('Zadejte čas přenosu časových údajů z modulu do čipu',
        LastTransfer.Date[syncOrd], LastTransfer.Time[syncOrd])
      then begin
         if ShowMessage('Ignorovat načtené časové údaje?', smNoYes, 0) = cmYes
         then
           goto ex{showmsg}
      end else begin
        DTSetStr(endtime, dtDateTimeString, LastTransfer.Date[syncOrd] + LastTransfer.Time[syncOrd]);
        if DTGet(endtime, dtIsValid) <> dtOK then
          continue;
        break;
      end;
    until false;
  end;

  if startTimeKnown then begin
    intervalInRealTimeSeconds := DTGetInterval(starttime, endtime, tuSecond);
  end;

  lastps := (ATimeTbl.UpConfig.NAP - CHTimeTableOffset) div sizeof(TCHTimeRecord);
  if (lastps < 0) or (lastps >= CHTimeTableSize) then begin
    ShowMessage('Adresa v tabulce časů v datech z modulu mimo rozsah',
     smError, 0);
    exit;
  end;

  tickinsec := 62.5 * (ATimeTbl.UpConfig.DivTimer - DivTimerFix) / 1000;
  {TimeOverFlowCount:word;{11fc TIME HI pocet preplneni interniho
   citace procesoru time}
  resetpos := -1;
  run := 0;{set 0 if should run the repeat ... until twice}
  allrecs := 0;
  currec := 0;

  repeat
    inc(run);
    if (run = 2) and (allrecs > 0) then begin
      ProgressBox(pb, pbShow, 'Načítají se časové údaje', 0, 0);
    end;
    curticks := 0;
    cnt := 0;
    if ATimeTbl.UpConfig.OverFlowCounter > 0 then begin
      curps := lastps;
      resetpos := 0;
    end else begin
      curps := 0;
      if curps = lastps then
        break;{no entries}
    end;

    while (curps <> lastps) or (cnt = 0) do begin
      if ATimeTbl.TimeTable[curps].Index = tfCounterOverflow then begin
        inc(curticks, 256);
      end else if ATimeTbl.TimeTable[curps].Index = tfNoData then begin
        curticks := curticks + ATimeTbl.TimeTable[curps].Time;
        break;
      end else if ATimeTbl.TimeTable[curps].Index =tfWarmReset then begin
        if (ATimeTbl.UpConfig.OverFlowCounter = 0) and
           (resetpos = -1) and (run = 2) then
        begin
          SetResetPos;
        end;
      end else begin
        if run = 2 then begin
          AppendHistoryRecord;
          inc(currec);
          if pb <> NoProgressBox then
            ProgressBox(pb, pbUpdate, '', currec, allrecs);
        end else
          inc(allrecs);
      end;
      inc(cnt);
      inc(curps);
      if curps = CHTimeTableSize then
        curps := 0;
    end;

    if run = 1 then begin
      allticks := curticks;
      intervalInTickSeconds := round(allticks * tickInSec);
      if startTimeKnown then begin
        {check correctness of starttime:}
        CheckCorrectStartTime;
      end;
    end;

  until run = 2;
ex:
  if pb <> NoProgressBox then
    ProgressBox(pb, pbHide, '', 0, 0);
  DTDone(tmptime);
  DTDone(downtime);
  DTDone(starttime);
  DTDone(curtime);
  DTDone(endtime);
end;

procedure UpdateTransferTimeInDB(DTbl:TTable;
  uploaded:integer;
  NewTransferDate:TDateString;
  NewTransferTime:TTimeString
  );
begin
  if (uploaded <> 0) then begin
    DTbl.Edit;
    if uploaded = 1 then begin
      DTbl.FieldByName(fnTransferDate1).AsString :=
        DateStringToDateString(NewTransferDate, dfRAW, dfEurope);
      DTbl.FieldByName(fnTransferTime1).AsString :=
        copy(TimeStringToTimeString(NewTransferTime, dfRAW, dfEurope), 1, 5);
    end else begin
      DTbl.FieldByName(fnTransferDate2).AsString :=
        DateStringToDateString(NewTransferDate, dfRAW, dfEurope);
      DTbl.FieldByName(fnTransferTime2).AsString :=
        copy(TimeStringToTimeString(NewTransferTime, dfRAW, dfEurope), 1, 5);
    end;
    DTbl.Post;
  end;
end;

function GetSynchroOrder(sync:word; DTbl:TTable):integer;
begin
  GetSynchroOrder := 2;
  if sync = DTbl.FieldByName(fnSynchro1).AsInteger then
    GetSynchroOrder := 1;
end;

procedure GetLastTransferTimesFromDB(var LastTransfer:TLastTransferInfo; DTbl:TTable);
begin
  LastTransfer.Date[1] := DateStringToDateString(DTbl.FieldByName(fnTransferDate1).AsString, dfEurope, dfRAW);
  LastTransfer.Time[1] := TimeStringToTimeString(
    DTbl.FieldByName(fnTransferTime1).AsString, dfEurope, dfRAW);

  LastTransfer.Date[2] := DateStringToDateString(DTbl.FieldByName(fnTransferDate2).AsString, dfEurope, dfRAW);
  LastTransfer.Time[2] := TimeStringToTimeString(
    DTbl.FieldByName(fnTransferTime2).AsString, dfEurope, dfRAW);
end;

procedure TransferData(tt:TTransferType);
var
  res:integer;            {result of the actions, if = 0 everything was OK}

  DTbl,                   {doors, users, history data files}
  UTbl,
  HTbl:TTable;

  DoorID:integer;         {id if current door that is transfering}

  {T2FOld, }
  T2FNew,                 { filled  with info FROM touch by transfer
                            (first only by configs before any other
                            transfer starts, then by time values) }
  F2TOld,                 { filled with copy of touchtable send
                            during last transfer to touch }
  F2TNew,                 { for creation of new table that
                            will be transfered to the touch }
  Mirror:PCHTable;        { Mirror is used to retrieve (before transfers)
                            or save (after transfers) content of configs
                            of touch after any change in Touch from PC }

  newDownSynchro,            {to be set to down config for next transfer,
                              identifies new TouchTable data to be sent
                              to DOOR}
  newUpSynchro:word;         {to be set to upconfig for next transfer,
                              i.e. the one the DOOR has the touchtable with
                              right now}
  synchro1, synchro2:word;   {copy of database values for this DoorID}

  oldUpCfg,
  oldDownCfg:TCHConfig;       {configs loaded now from TouchRAM before
                                   other transfers}
  {NonTransferedSynchro:word; {Set in TouchToFile, if NAP found to be =0 (otherwise = 0);
                              if non 0, then content of corresponding mirror file
                              was not really transfered -> the synchro will be included to
                              NonTransferedSynchros list (config var), and when the
                              transfer to touch will be invoked, the data will be retransmitted
                              to the medium }
  procTouchTable,
  procTimeTable:PCHTable;    {if both non nil after finishing transfers,
                              point to tables that should be used
                              to process door open times using those tables}

  LastTransfer:TLastTransferInfo;    { info about at what date,time
                                       and what synchro-id data transfered
                                       last time;}
  {for synchro1 and synchro2}
  NewTransferDate:TDateString;
  NewTransferTime:TTimeString;

  downoffs,upoffs:word;       {adjusted down modulo 32 upconfig and
                               downconfig area offsets}
  TblTouchID:TMediumID;        {from DTbl database}
  ReadTouchID:TMediumID;       {from reader}
  SwitchSynchros:boolean;     {just a clutch if something gone wrong,
                               force switching synchro values to touch}
  uploadmsg,downloadmsg:string;
    { optional  infos that will be shown upon successfule transfer;
      can be filled by corresponding procedures }
  mirrorRetrieved:boolean;    { set true if detected change in doorid
                                and new configs uploaded to transfer medium;
                                then later in FileToTouch the touchtable
                                will be also uploaded again }
  wasInit:boolean;            { was oldUpCongif.Synchro = InitSynchro?
                                in DoTouchToFile }


  function GetSyncOrder(sync:word):integer;
  begin
    GetSyncOrder := 2;
    if sync = Synchro1 then
      GetSyncOrder := 1;
  end;

  function GetOtherSynchro(sync:word):word;
  begin
    if (sync = InitSynchro) or (sync = $FFFF) then begin
      ShowMessage('GetOtherSynchro - invalid sync ' + inttostr(sync),
        smError, 0);
    end;
    if sync mod 2 = 0 then
      sync := sync + 1
    else
      sync := sync - 1;
    GetOtherSynchro := sync;
  end;


  function GetSynchroBackupName(Synchro:word; const Ext:ExtStr):string;
  var
    d:TDateString;
    fn:string;
    cnt:integer;
    sync:string[2];
  begin
    sync := ByteToHex(lo(Synchro));{dateu}
    d := DTGetStr(DTNow, dtDD) + DTGetStr(DTNow, dtHHMM); {logtype}
    cnt := 0;
    repeat
      fn := DataDir + d + sync + ext;
      if FExists(fn) then begin
        if d[6] < '9' then
          inc(d[6])
        else begin
          d[6] := '0';
          if d[5] < '9' then
            inc(d[5])
          else
            d[5] := '0';
        end;
        inc(cnt);
        if cnt > 100 then begin
          break;
        end;
      end else
        break;
    until false;
    GetSynchroBackupName := fn;
  end;

  function GetT2FName(Synchro:word):string;
  {var s:string[5];}
  begin
    {str(DoorID, s);
    while length(s) < 3 do s := '0' + s;}
    GetT2FName := DataDir + 'D' + WordToHex(Synchro) + '.T2F';
  end;

  function GetT2FBackupName(Synchro:word):string;
  begin
    GetT2FBackupName := GetSynchroBackupName(Synchro, '.T2F');
  end;


  function GetF2TName(Synchro:word):string;
  {var s:string[5];}
  begin
    {str(DoorID, s);
    while length(s) < 3 do s := '0' + s;}
    GetF2TName := DataDir + 'D' + WordToHex(Synchro) + '.F2T';
  end;

  function GetF2TBackupName(Synchro:word):string;
  begin
    GetF2TBackupName := GetSynchroBackupName(Synchro, '.F2T');
  end;


  function GetMirrorFileName(Synchro:word):string;{for saving/retrieving
    snapshots of touch config's, if one touch used fore more doors}
  {var s:string[5];}
  begin
    {str(DoorID, s);
    while length(s) < 3 do s := '0' + s;}
    GetMirrorFileName := DataDir + 'D' + WordToHex(Synchro) + '.MIR';
  end;

  procedure ClearTable(var ATable:TCHTable);{clear}
  begin
    FillChar(ATable.TouchTable, sizeof(ATable.TouchTable), $FF);
    FillChar(ATable.TimeTable, sizeof(ATable.TimeTable), $0);

    ATable.DownConfig.NAP := CHTimeTableOffset;{chrntype}
    ATable.DownConfig.TimeDoor := DefaultOpenDelay;
    ATable.DownConfig.DivTimer := DefaultTimeDivider + DivTimerFix;

    ATable.UpConfig.TimeDoor := DefaultOpenDelay;
    ATable.UpConfig.DivTimer := DefaultTimeDivider + DivTimerFix;
  end;

  function FillConfigFromDB:boolean;
  var cfg:TCHConfig;
  begin
    FillConfigFromDB := false;

    cfg.TimeDoor := round(DTbl.FieldByName(fnOpenDelay).AsInteger / 62.5);
    cfg.DivTimer := round(1000 * DTbl.FieldByName(fnTimeDivider).AsInteger / 62.5)
      + DivTimerFix;
    cfg.OverFlowCounter := 0;
    cfg.TimeOverFlowCount := 0;
    cfg.Reserved:= 0;
    cfg.NAP := 0;

    F2TNew^.UpConfig := cfg;
    F2TNew^.UpConfig.Synchro := newUpSynchro;

    F2TNew^.DownConfig := cfg;
    F2TNew^.DownConfig.NAP := CHTimeTableOffset;
    F2TNew^.DownConfig.Synchro := newDownSynchro;

    if ShouldTestErrorAddr then
      F2TNew^.DownConfig.NAP := TestErrorAddr;
    FillConfigFromDB := true;
  end;

  function CheckSynchroValues(reporterror:boolean):boolean;
  begin
    CheckSynchroValues:= false;
    if ((newDownSynchro <> Synchro1) and
        (newDownSynchro <> Synchro2)
       ) or
       ((newUpSynchro <> Synchro1) and
        (newUpSynchro <> Synchro2) and
        (newUpSynchro <> InitSynchro)
       )
    then begin
      res := -1;
      if reportError then begin
        ShowMessage('Nesprávné synchronizační hodnoty: ' +
         IntToStr(newDownSynchro) + ' ' + IntToStr(newUpSynchro) +
         '. Použijte inicializační přenos. '
         , smError, 0);
         {v1.08}
         res := -2;
         {/v1.08}
      end;
      exit;
    end;
    CheckSynchroValues := true;
  end;


  function GetInfoFromTouch:boolean;
  {check if the Touch is OK, read config values from touch}
  var
    outtime:longint;
  begin
    GetInfoFromTouch := false;

    SetTouchIDStr(DTbl.FieldByName(fnMediumID).AsString, TblTouchID);

    InfoFormShow('Vložte čip do čtečky');
    outtime := GetTickCount + globals .TouchTransferTimeout;
    repeat
      if GetTickCount > outtime then
      begin
        InfoFormHide;
        ShowMessage('Timeout - medium pro přenos nebylo vloženo', smError, 0);
        {v1.08} res := -2;{/v1.08
        res := -1;}
        exit;
      end{v1.08} else begin
        if InfoFormCanceled then begin
          InfoFormHide;
          ShowMessage('Přenos přerušen uživatelem', smError, 0);
          res := -2;
          exit;
        end else begin
          Application.ProcessMessages;
        end;
      end{/v1.08};
    until GetTouchID(globals .TouchPortNr, ReadTouchID);
    InfoFormHide;

    if not IsMemTouchID(ReadTouchID, true) then begin
      res := -1;
      exit;
    end;

    if compareu.CompareRec(TblTouchID, ReadTouchID, TouchIDLen) <> 0 then
    begin
      ShowMessage('Čip zadaný v záznamu pro tyto dveře je odlišný. Změňte záznam.',smError,0);
      {v1.08}
      res := -2;
      {/v1.08
      res := -1;}
      exit;
    end;

    if not TouchBlockToBuf(TouchPortNr, downoffs, CHBlockSize, T2FNew^.Data[downoffs]) then
    begin
      {$IFDEF DEBUG}
      ShowMessage('TouchBlockToBuff downoffs failed.',smError,0);
      {$ENDIF}
      res := -1;
      exit;
    end;
    if not TouchBlockToBuf(TouchPortNr, upoffs, CHBlockSize, T2FNew^.Data[upoffs]) then
    begin
      {$IFDEF DEBUG}
      ShowMessage('TouchBlockToBuff upoffs failed.',smError,0);
      {$ENDIF}
      res := -1;
      exit;
    end;
    oldUpCfg := T2FNew^.UpConfig;
    oldDownCfg := T2FNew^.DownConfig;
    GetInfoFromTouch := true;
  end;

  function RetrieveMirrorIfNeeded:boolean;
  var ok:boolean;
  begin
    RetrieveMirrorIfNeeded := false;
    {v1.08}
    ok := ((oldDownCfg.Synchro = Synchro1) and (oldUpCfg.Synchro = Synchro2)) or
          ((oldDownCfg.Synchro = Synchro2) and (oldUpCfg.Synchro = Synchro1)) or
          ((oldDownCfg.Synchro = Synchro1) and (oldUpCfg.Synchro = InitSynchro));
    {v1.08}
    if not ok
       {((oldDownCfg.Synchro <> Synchro1) or (oldDownCfg.Synchro <> Synchro2))
       and
       ((oldUpCfg.Synchro <>  Synchro1) or (oldUpCfg.Synchro <> Synchro2))
       and
       ((oldDownCfg.Synchro <> Synchro1) or (oldUpCfg.Synchro <> InitSynchro))}
    then begin
      if not FExists(GetMirrorFileName(Synchro1)) then begin
        ShowMessage('Ztraceny synchronizační hodnoty, kopie nenalezena. ' +
        'Proveďte inicializační přenos.', smError, 0);
        res  := -1;
        exit;
      end;
      res := ReadChTable(GetMirrorFileName(Synchro1), Mirror^);
      if res  <> 0 then begin
        ShowMessage('Nelze načíst kopii synchronizačních hodnot.' +
        'Proveďte inicializační přenos.', smError, 0);
        exit;
      end;
      oldDownCfg := Mirror^.DownConfig;
      oldUpCfg := Mirror^.UpConfig;
      mirrorRetrieved := true;
    end;
    RetrieveMirrorIfNeeded := true;
  end;

  procedure SaveMirror(var ATbl:TCHTable);
  var s:TSynchro;
  begin
    s := ATbl.UpConfig.Synchro;{chrntype}
    if s = InitSynchro then
      s := ATbl.DownConfig.Synchro;
    if (s mod 2) <> 0 then
      s := s - 1;
    {.. saving always under Synchro1 value, the even one}
    WriteChTable(GetMirrorFileName(s), ATbl);
  end;

  function LoadOldF2TFile(ASync:integer; ReportError:boolean):boolean;
  begin
    LoadOldF2TFile := false;
    res := ReadChTable(GetF2TName(ASync), F2TOld^);
    if res <> 0 then begin
      {file probably not found}
      if ReportError then begin
        ShowMessage('Nenalezen soubor s minulými daty do modulu: ' +
          GetF2TName(ASync) +
          '. Použijte inicializační přenos dat z modulu.'
          , smError, 0);
      end;
      exit;
    end;
    LoadOldF2TFile := true;
  end;

  function ClearTimeInfoInTouch(OnlyTimes:boolean):boolean;
  begin
    ClearTimeInfoInTouch := false;
    {1.08}
    if OnlyTimes then begin
      F2TNew^.TouchTable := F2TOld^.TouchTable;{chrntype}
    end;
    {/v1.08}
    F2TNew^.UpConfig := T2FNew^.UpConfig;
    {if ZeroNAP then
      F2TNew^.UpConfig.NAP := 0
    else}
    F2TNew^.UpConfig.NAP := CHTimeTableOffset;
    F2TNew^.UpConfig.TimeOverFlowCount := 0;
    F2TNew^.UpConfig.OverflowCounter := 0;
    F2TNew^.UpConfig.Reserved := 0;
    F2TNew^.DownConfig := T2FNew^.DownConfig;
    if not BufToTouchBlock(TouchPortNr, F2TNew^.Data[upoffs], upoffs, CHBlockSize) then begin
      res := -1;
      exit;
    end;
    ClearTimeInfoInTouch := true;
    SaveMirror(F2TNew^);
  end;

  function DoInitFileToTouch:boolean;
  begin
    DoInitFileToTouch := false;
    newDownSynchro := Synchro1;
    newUpSynchro := InitSynchro;

    FillConfigFromDB; {fill F2TNew^ tables's config parts using
                       newDownSynchro, newUpSynchro and Tbl values}
    res := WriteChTable(GetF2TName(newDownSynchro), F2TNew^);{dallutl}
    if res <> 0 then
      exit;
    res := FileToTouch(TouchPortNr, GetF2TName(newDownSynchro));
    if res <> 0 then begin
      EraseFile(GetF2TName(newDownSynchro));
      exit;
    end;
    SaveMirror(F2TNew^);
    EraseFile(GetF2TName(newUpSynchro));
    LastTransfer.Uploaded := GetSyncOrder(newDownSynchro);
    IncludeNonTransferedSynchro(Synchro1);
    IncludeNonTransferedSynchro(Synchro2);
    {i.e. we don't know yet,
     if transfer from touch to module was really done}
    DoInitFileToTouch := true;
  end;

  function DoInitTouchToFile:boolean;
  {zpracovani dat z door module (cely obsah pameti modulu prenesen
   do touchky); pak inicializace touch pro prvni prenos do modulu:}
  begin
    DoInitTouchToFile := false;
    if oldUpCfg.NAP = 0 then begin
      {no transfer made with DOOR module since last PC - Touch transfer}
      ShowMessage('Nebyl proveden přenos dat mezi modulem a přenosovým mediem',
        smError, 0);
      res := -1;
      exit;
      (*
      newDownSynchro := oldDownCfg.Synchro;
      newUpSynchro := oldUpCfg.Synchro;
      if not CheckSynchroValues then begin
       {some kind of nonsense data in module memory}
        exit;
      end;
      *)
    end else begin
      {transfer with DOOR module made since last PC - Touch transfer}
      if (oldUpCfg.Synchro = InitSynchro) then begin
        oldUpCfg.Synchro := Synchro2;
        oldUpCfg.NAP := CHTimeTableOffset;{i.e. ignore all time data}
      end;
      newDownSynchro := oldUpCfg.Synchro;
      newUpSynchro := oldDownCfg.Synchro;{=$FFFF, from door module}

      if true {((oldUpCfg.Synchro=Synchro1) or (oldUpCfg.Synchro = Synchro2)) and
         (oldDownCfg.Synchro = $FFFF)}
      {(CheckSynchroValues(false)} then
      begin
        {i.e.  work up only non nonsense synchro values, no no, do it always}
        if (oldUpCfg.NAP = CHTimeTableOffset)
        then begin
          {no data;  this was the only thing requested by user, so say him:}
          ShowMessage('Žádné časové údaje v přenosové paměti', smInfo,0);
        end else begin
          {download the touch and time data:}
          res := TouchToFile(TouchPortNr, GetT2FName(oldUpCfg.Synchro){T2FFileNew});
          if res <> 0 then begin
            exit;
          end;
          {load data from file to T2FNew:}
          res := ReadCHTable(GetT2FName(oldUpCfg.Synchro), T2FNew^);{dallutl}
          if res <> 0 then begin
            ShowMessage('Chyba při načítaní souboru s přenesenými daty: ' +
              GetT2FName(oldUpCfg.Synchro), smError, 0);
            exit;
          end;
          procTouchTable := T2FNew;
          procTimeTable := T2FNew;
          LastTransfer.Downloaded := GetSyncOrder(oldUpCfg.Synchro);

          {indicate that time data were accepted by pc}
          if not ClearTimeInfoInTouch(true) then
            exit;
        end;
      end;
    end;

    {init Touch with data (as DoInitFileToTouch):}
    newDownSynchro := Synchro1;
    newUpSynchro := InitSynchro;

    FillConfigFromDB;

    res := WriteChTable(GetF2TName(newDownSynchro), F2TNew^);
    if res <> 0 then
      exit;

    EraseFile(GetF2TName(newUpSynchro));

    res := FileToTouch(TouchPortNr, GetF2TName(newDownSynchro));
    if res <> 0 then
      exit;

    LastTransfer.Uploaded := GetSyncOrder(newDownSynchro);
    IncludeNonTransferedSynchro(Synchro1);
    IncludeNonTransferedSynchro(Synchro2);
    SaveMirror(F2TNew^);

    DoInitTouchToFile := true;
  end;


  function DoTouchToFile(OnlyThis:boolean):boolean;
  {read time info from Touch}
  begin
    DoTouchToFile := false;
    wasInit := (oldUpCfg.Synchro = InitSynchro);
    {reportNoTimeData := false;}
    {check if door open times should be downloaded and what
     are the synchros:}
    if oldUpCfg.NAP = 0 then begin
      {no transfer made with DOOR module since last PC - Touch transfer}
      IncludeNonTransferedSynchro(oldDownCfg.Synchro);
      if OnlyThis then begin
        ShowMessage('Nebyl proveden přenos dat mezi modulem a přenosovým mediem',
          smError, 0);
      end;
      res := -1;
      exit;
      (*
      newDownSynchro := oldDownCfg.Synchro;
      newUpSynchro := oldUpCfg.Synchro;
      if not CheckSynchroValues then begin
       {can be false if invalid synchro and not (tt=ttInitTouchToFile)}
        exit;
      end;
      *)
    end else begin
      {transfer with DOOR module made since last PC - Touch transfer}

      if (oldUpCfg.Synchro = InitSynchro) then begin
        oldUpCfg.Synchro := GetOtherSynchro(oldDownCfg.Synchro);{Synchro2;}
        oldUpCfg.NAP := CHTimeTableOffset;{i.e. ignore all time data}
        oldUpCfg.OverFlowCounter := 0;
      end;
      ExcludeNonTransferedSynchro(GetOtherSynchro(oldUpCfg.Synchro));

      if ((oldUpCfg.Synchro <> Synchro1) and
           (oldUpCfg.Synchro <> Synchro2)
          ) or
          ((oldDownCfg.Synchro <> Synchro1) and
           (oldDownCfg.Synchro <> Synchro2)
          )
      then begin
        {ShowMessage('Nesprávné synchronizační hodnoty: ' +
          IntToStr(oldDownCfg.Synchro) + ' ' +
          IntToStr(oldUpCfg.Synchro), smError, 0);
        exit;} {casy je mozne nacist i pro spatne synchro hodnoty,
          chyba se prip. ohlasi pri pokusu nacist stary soubor s daty
          do Touch (LoadOld..)}
      end;

      {newDownSynchro := oldUpCfg.Synchro;
      newUpSynchro := oldDownCfg.Synchro;

      if not CheckSynchroValues then
        exit;}

      if (oldUpCfg.NAP = CHTimeTableOffset) and
         (oldUpCfg.OverflowCounter = 0)
      then begin
        {v1.08}
        {load old content of transfer medium from file, so
         that F2TOld set to correct value (used in cleartimeinfointouch):}
        if not LoadOldF2TFile(oldUpCfg.Synchro, (not wasInit)) then begin
          if wasInit then begin
            if not LoadOldF2TFile(oldDownCfg.Synchro, true) then begin
              res := -1;
              exit;
            end;
          end else begin
            res := -1;
            exit;
          end;
        end;
        {/v1.08}
        {indicate that time data were accepted by pc}
        if not ClearTimeInfoInTouch(OnlyThis) then
          exit;
        {no data, this was the only thing requested by user, so say him:}
        if OnlyThis then
          ShowMessage('Žádné časové údaje v přenosové paměti', smError,0);
      end else begin
        {download the time data}
        {load old touchtable to memory:}
        if not LoadOldF2TFile(oldUpCfg.Synchro, (not wasInit)) then begin
          if wasInit then begin
            if not LoadOldF2TFile(oldDownCfg.Synchro, true) then begin
              res := -1;
              exit;
            end;
          end else begin
            res := -1;
            exit;
          end;
        end;
        {transfer time data from touch to PC memory:}
        if not TouchToTimeTable(TouchPortNr, T2FNew^) then
          exit;
        {write time data from PC memory to file:}
        res := WriteCHTable(GetT2FName(oldUpCfg.Synchro), T2FNew^);
        if res <> 0 then begin
          exit;
        end;
        {indicate, that time data should be worked up later:}
        procTouchTable := F2TOld;
        procTimeTable := T2FNew;
        LastTransfer.DownLoaded := GetSyncOrder(oldUpCfg.Synchro);
        {indicate that time data were accepted by pc}
        if not ClearTimeInfoInTouch(OnlyThis) then
          exit;

        if ShowDetailMsgs then begin
          downloadmsg := 'Kopie nových časových údajů je v souboru ' +
            GetT2FName(oldUpCfg.Synchro) + '. ';
        end;
        CopyFile(GetT2FName(oldUpCfg.Synchro), GetT2FBackupName(oldUpCfg.Synchro));
      end;
    end;
    DoTouchToFile := true;
  end;

  function DoFileToTouch:boolean;
  var
    sizetosend:word;{how much of bytes from TouchTable to send}
  begin
    DoFileToTouch := false;
    DoTouchToFile(false);{eventually get the time data (for any synchro values)}

    if not RetrieveMirrorIfNeeded then begin
      res := -1;
      exit;
    end;{retrieved oldDownCfg and oldUpCfg that corresponds to current SynchroX}

    if oldUpCfg.NAP = 0 then begin
      {no transfer made with DOOR module since last PC - Touch transfer,
       no need to transfer time data, keeps old synchro values}
      if not SwitchSynchros {usual way} then begin
        newDownSynchro := oldDownCfg.Synchro;
        newUpSynchro := oldUpCfg.Synchro;
      end else begin
        if oldUpCfg.Synchro = InitSynchro then begin
          ShowMessage('Nelze prohodit synchronizační hodnoty, medium '+
            ' je připraveno pro inicializační zápis do modulu.', smError, 0);
          newDownSynchro := oldDownCfg.Synchro;
          newUpSynchro := oldUpCfg.Synchro;
        end else begin
          newDownSynchro := oldUpCfg.Synchro;
          newUpSynchro := oldDownCfg.Synchro;
        end;
        {if newDownSynchro = InitSynchro then
          newDownSynchro := Synchro2;{DOWNSYNCHRO MUST NOT BE NEVER InitSynchro}
      end;
      {v1.08}
      {if newDownSynchro = InitSynchro then
        newDownSynchro := Synchro2;
      if newUpSynchro = InitSynchro then
        newUpSynchro :=  Synchro2;}
      {/v1.08}

      if not CheckSynchroValues(true) then begin
        exit;
      end;
    end else begin
      {transfer with DOOR module made since last PC - Touch transfer}
      if (oldUpCfg.Synchro = InitSynchro) then begin
        oldUpCfg.Synchro := Synchro2;
        oldUpCfg.NAP := CHTimeTableOffset;{i.e. ignore all time data}
      end;

      newDownSynchro := oldUpCfg.Synchro;
      newUpSynchro := oldDownCfg.Synchro;

      if not CheckSynchroValues(true) then
        exit;

      (* done in  DoTouchToFile(false):

      if oldUpCfg.NAP > CHTimeTableOffset then begin
        {load last touch table from file to PC mem:}
        if not LoadOldF2TFile(oldUpCfg.Synchro) then begin
          res := -1;
          exit;
        end;
        {download the time data from touch to PC mem}
        if not TouchToTimeTable(TouchPortNr, T2FNew^) then
          exit;
        res := WriteCHTable(GetT2FName(oldUpCfg.Synchro), T2FNew^);
        if res <> 0 then
          exit;
        procTouchTable := F2TOld;
        procTimeTable := T2FNew;
        LastTransfer.DownLoaded := GetSynchroOrder(oldUpCfg.Synchro);
        {indicate that time data were accepted by pc}
        if not ClearTimeInfoInTouch then
          exit;
      end;
      *)
    end;

    {v1.08}
    if not DBToTouchTable(UTbl, DTbl, F2TNew^) then begin
      res := -1;
      exit;
    end;
    {/v1.08}
    FillConfigFromDB;
    res := WriteChTable(GetF2TName(newDownSynchro), F2TNew^);
    if res <> 0 then
      exit;
    res := ReadCHTable(GetF2TName(newUpSynchro), F2TOld^);
    if (res = 0) and
       (not IsNonTransferedSynchro(newUpSynchro)) and
       (not TransferAlwaysAll) and
       (not mirrorRetrieved)
    then begin
      SizeToSend := GetDifTouchTableSize(F2TOld^, F2TNew^);
      {dallutl}
      if (SizeToSend mod CHBlockSize) <> 0 then begin
        SizeToSend := (SizeToSend div CHBlockSize) * CHBlockSize + CHBlockSize;
      end;
    end else begin
      SizeToSend := CHTouchTableSize * sizeof(TCHTouchRecord){chrntype};
      res := 0;
    end;

    if not TouchTableToTouch(TouchPortNr, SizeToSend, F2TNew^) then
    begin
      res := -1;
      exit;
    end;
    SaveMirror(F2TNew^);
    LastTransfer.Uploaded := GetSyncOrder(newDownSynchro);
    IncludeNonTransferedSynchro(newDownSynchro);{i.e. we don't know if
      it was transfered to module }
    if ShowDetailMsgs then begin
      uploadmsg := 'Kopie tabulky čipů přenesené do média je v souboru ' +
        GetF2TName(newDownSynchro) + '. Přeneseno ' +
          IntToStr(SizeToSend div sizeof(TCHTouchRecord)) + ' záznamů pro medium.';
    end;
    CopyFile(GetF2TName(newDownSynchro), GetF2TBackupName(newDownSynchro));
    DoFileToTouch := true;
  end;

  function DoTransfer:boolean;
  var d:TDoorID; s:TSynchro;
  begin
    DoTransfer:= false;
    if not GetInfoFromTouch then
      exit;
    {v1.08}
    s := T2FNew^.UpConfig.Synchro;
    if s = InitSynchro then
      s := T2FNew^.DownConfig.Synchro;
    d := SynchroToDoorID(s);
    if (d <> DoorID) then begin
      case tt of

        ttTouchToFile, ttFileToTouch, ttInitTouchToFile: begin
          if (T2FNew^.UpConfig.NAP = 0) then begin
            ShowMessage('Přenosové médium bylo připraveno pro přenos do dveří č. ' +
              IntToStr(d) + '. Proveďte nejprve tento přenos a následné "Načtení ' +
              'časových údajů ..." do PC pro tyto dveře.', smError, 0);
            exit;
          end;
          if ((T2FNew^.UpConfig.NAP > CHTimeTableOffset) or
              (T2FNew^.UpConfig.OverflowCounter <> 0)) and
             (T2FNew^.UpConfig.Synchro <> InitSynchro) then
          begin
            ShowMessage('Nebyly přeneseny časové údaje z přenosového média pro dveře č. '
              + IntToStr(d) + '. Proveďte nejdříve "Načtení časových údajů ..." pro tyto dveře.',
              smError, 0);
            exit;
          end;
        end;

        ttInitFileToTouch: begin
          if (T2FNew^.UpConfig.NAP = 0) then begin
            if ShowMessage('Přenosové médium bylo připraveno pro přenos do dveří č. ' +
                IntToStr(d) + '. Přepsat připravené údaje?', smNoYes, 0) <> cmYes then
              exit;
          end else begin
            if ((T2FNew^.UpConfig.NAP > CHTimeTableOffset) or
                (T2FNew^.UpConfig.OverflowCounter <> 0)) and
               (T2FNew^.UpConfig.Synchro <> InitSynchro) then
            begin
              if ShowMessage('Nebyly přeneseny časové údaje z přenosového média pro dveře č. '
                + IntToStr(d) + '. Ignorovat přenesené údaje?', smNoYes, 0) <> cmYes then
                exit;
            end;
          end;
        end;

      end;
    end;
    {/v1.08}
    if tt in [ttFileToTouch, ttInitFileToTouch] then begin
     TransferDateTimeForm.DateEdit.Text :=
       DateStringToDateString(NewTransferDate, dfRAW, dfEurope);

     TransferDateTimeForm.TimeEdit.Text :=
       TimeStringToTimeString(copy(NewTransferTime, 1, 4),
         dfRAW, dfEurope);

     TransferDateTimeForm.SwitchSynchrosCheckBox.Checked := false;
     while TransferDateTimeForm.ShowModal <> mrOK do begin
       if ShowMessage('Zrušit přenos?', smNoYes, 0)
         = cmYes then {showmsg}
         exit;
     end;
     SwitchSynchros := TransferDateTimeForm.SwitchSynchrosCheckBox.Checked;

     NewTransferDate := DateStringToDateString(TransferDateTimeForm.DateEdit.Text,
       dfEurope, dfRAW);
     NewTransferTime := TimeStringToTimeString(TransferDateTimeForm.TimeEdit.Text,
       dfEurope, dfRAW);
   end;

    case tt of
      ttInitFileToTouch: if not DoInitFileToTouch then exit;
      ttInitTouchToFile: if not DoInitTouchToFile then exit;
      ttTouchToFile : if not DoTouchToFile(true) then exit;
      ttFileToTouch : if not DoFileToTouch then exit;
    end;
    DoTransfer:= true;
  end;

  function InitTransferData:boolean;
  var dt:PDTO;
  begin
    InitTransferData := false;
    downoffs := (DownConfigOffset div 32) * 32;
    upoffs := (UpConfigOffset div 32) * 32;
    SwitchSynchros:= false;
    LastTransfer.uploaded := 0;
    LastTransfer.downloaded := 0;
    {NonTransferedSynchro:= 0;}
    F2TOld := nil;
    F2TNew := nil;
    {T2FOld := nil;}
    T2FNew := nil;
    Mirror := nil;
    {pointers to one of the above (after alloc):}
    procTouchTable := nil;
    procTimeTable := nil;

    {F2TFileOld := '';
    F2TFileNew := '';
    T2FFileOld := '';
    T2FFileNew := '';}

    New(F2TOld);
    New(F2TNew);
    {New(T2FOld);}
    New(T2FNew);
    New(Mirror);

    if (F2TOld = nil) or {(T2FOld = nil) or}
       (F2TNew = nil) or (T2FNew = nil) or (Mirror =  nil)
    then begin
      res := -1;
      exit;
    end;
    ClearTable(F2TOld^);
    ClearTable(F2TNew^);
    {ClearTable(T2FOld^);}
    ClearTable(T2FNew^);
    ClearTable(Mirror^);

    DTbl := MainForm.DoorsTable;
    UTbl := MainForm.UsersTable;
    HTbl := MainForm.HistoryTable;
    HTbl.IndexName := '';
    if DTbl.EOF and DTbl.BOF then begin
      res := -1;
      ShowMessage('Žádné záznamy o dveřích', smError, 0);
      exit;
    end;

    DoorID := DTbl.FieldByName(fnDoorID).AsInteger;
    Synchro1 := DTbl.FieldByName(fnSynchro1).AsInteger;
    Synchro2 := DTbl.FieldByName(fnSynchro2).AsInteger;

    GetLastTransferTimesFromDB(LastTransfer, DTbl);
{    LastTransfer.Date[1] := DateStringToDateString(DTbl.FieldByName(fnTransferDate1).AsString, dfEurope, dfRAW);
    LastTransfer.Time[1] := TimeStringToTimeString(
      DTbl.FieldByName(fnTransferTime1).AsString, dfEurope, dfRAW);

    LastTransfer.Date[2] := DateStringToDateString(DTbl.FieldByName(fnTransferDate2).AsString, dfEurope, dfRAW);
    LastTransfer.Time[2] := TimeStringToTimeString(
      DTbl.FieldByName(fnTransferTime2).AsString, dfEurope, dfRAW);
}
    if not DBToTouchTable(UTbl, DTbl, F2TNew^) then begin
      res := -1;
      exit;
    end;

    dt := DTInit;
    DTDo(dt, dtGetSystemTime, 0);
    DTDo(dt, {logtype}dtIncSecond, GoToModuleInterval);
      {GoToModuleInterval.. how long it usually takes to go to module and do the transfer}
    NewTransferTime := {country .TimeStringToTimeString(}
      DTGetStr(dt, dtTimeString);
      {dfRAW,
      dfEurope
    );{country}

    NewTransferDate := {DateStringToDateString(}DTGetStr(dt, dtDateString);
      {dfRAW, dfEurope);}

    DTDone(dt);

    InitTransferData := true;
  end;

  procedure DoneTransferData;
  begin
    if F2TOld <> nil then
      Dispose(F2TOld);
    if F2TNew <> nil then
      Dispose(F2TNew);
    {if T2FOld <> nil then
      Dispose(T2FOld);}
    if T2FNew <> nil then
      Dispose(T2FNew);
  end;

label ex;
begin{TransferData}
  Transfering := true;
  try
  res := 0;
  downloadmsg := '';
  uploadmsg := '';
  mirrorRetrieved:= false;
  if InitTransferData then begin
    if DoTransfer then begin
      res := 0;
      if (procTimeTable <> nil) and (procTouchTable <> nil) then begin
        TimeTableToDB(procTouchTable^, procTimeTable^, LastTransfer, HTbl,
          SynchroToDoorID(procTimeTable^.UpConfig.Synchro));
      end;
      UpdateTransferTimeInDB(DTbl, LastTransfer.Uploaded, NewTransferDate, NewTransferTime);
      ShowMessage('Přenos byl proveden. ' + downloadmsg + uploadmsg, smInfo, 0);
    end else begin
      {v1.08}
      if res <> -2 then
      {/v1.08}
        ShowMessage('Přenos byl přerušen.', smError, 0);
    end;
  end else begin
    {v1.08}
    if res <> -2 then
    {/v1.08}
      ShowMessage('Přenos nebyl proveden.', smInfo, 0);
  end;
  DoneTransferData;
  finally
    Transfering := false;
  end;
end;

function WorkupFile(const AFileName:string; DTbl:TTable):boolean;
var
  t:TChTable;
  LastTransfer:TLastTransferInfo;
  did:TDoorID;
  oldaskendtime:boolean;
begin
  WorkupFile := false;
  did := DTbl.FieldByName(fnDoorID).AsInteger;
  if ReadCHTable(AFileName, t) <> 0 then
    exit;
  GetLastTransferTimesFromDB(LastTransfer, DTbl);
  LastTransfer.Downloaded := GetSynchroOrder(t.UpConfig.Synchro, DTbl);

  oldaskendtime:= AlwaysAskForEndTime;
  AlwaysAskForEndTime := true;
  TimeTableToDB(t, t, LastTransfer, MainForm.HistoryTable, did);
  AlwaysAskForEndTime := oldaskendtime;

  WorkupFile := true;
end;

end.

