Как сделать получение данных из базы данных и файлов поточно-ориентированным в моей процедуре idTCPServer OnExecute? - PullRequest
1 голос
/ 10 января 2020

У меня есть Indy TCP Server, работающий на Windows 8.1 с RAD Studio 10.3 Delphi. Клиент приложения для телефона подключается к серверу и после получения приветствия от сервера приложение для телефона отправляет запрос на указание c данных с сервера - все хорошо до этого момента.

В OnExecute Процедуру на сервере я называю процедурой (LogInFromPhone), которая получает некоторые данные из базы данных, а также из некоторых других файлов. Эти данные отображаются в памятке в приложении «Сервер», а затем отправляются обратно клиенту, который их запросил. То, как я кодировал сервер OnExecute, похоже, работает нормально, но я знаю, что это неправильный способ - не поточнобезопасный.

Я потратил много часов на поиск форумов и c, пытаясь чтобы узнать, как именно это должно быть сделано. Большинство более продвинутых примеров недостаточно подробны, чтобы новичок, как я, мог полностью понять. Я был бы очень признателен за подробный пример с описаниями, с помощью которых я могу получить это право.

Это код сервера OnExecute, который у меня есть:

procedure TMainForm.IdTCPServer1Execute(AContext: TIdContext);
var  DataRequestFromClient : string;
begin

  DataRequestFromClient := AContext.Connection.IOHandler.ReadLn;    //receive request from client

  StringIn:= DataRequestFromClient;

  LogInFromPhone;        //Gets Data from DataBase and ini Files and returns with Data in 'PhoneString'


  if PhoneString <> '' then
    AContext.Connection.IOHandler.WriteLn(PhoneString);  //send back data in PhoneString to client

end;



procedure TMainForm.LogInFromPhone;
{Entry: Received UserName, Password & Devices required to be sent back to Phone is in 'StringIn'  e.g  Nu_Smith,Wp_2222,_1,*
 Exit: String to send back to Phone is in 'PhoneString'}
var found: boolean;  TempString: string;
    i, CPos, UPos, AstrasPos: integer;
begin

  Memo1.Lines.Add(StringIn);  //view most recent Log Ins
  While Memo1.Lines.Count > 100 do Memo1.Lines.Delete(0);   //limit Memo1 lines to 100

  //Pick out UserName & Password values. Username_Smith,Password_2222,
  TempString:= StringIn;
  UPos:= pos('_',TempString);
  delete(TempString,1,UPos);
  CPos:= pos(',',TempString);
  UserName:= copy (TempString,1,CPos-1);      //UserName
  delete(TempString,1,CPos);

  UPos:= pos('_',TempString);
  delete(TempString,1,UPos);
  CPos:= pos(',',TempString);
  Password:= copy (TempString,1,CPos-1);      //Password
  delete(TempString,1,CPos);

  UPos:= pos('_',TempString);
  delete(TempString,1,UPos);
  AstrasPos:= pos('*',TempString);
  DeviceString:= copy (TempString,1,AstrasPos);      //DeviceString
  delete(TempString,1,CPos);

  Edit5.Text:= DeviceString;
  Edit3.Text:= UserName;         //show who logged in
  Edit4.Text:= Password;

  //Lookup Client Data Base to get ESP No and Site Address
  found := false;
  ADOTable1.First();        //start at top of table
  for i := 1 to ADOTable1.RecordCount do
  begin
    if (ADOTable1.FindField('User Name').AsString = UserName) AND
       (ADOTable1.FindField('Password').AsString = Password) then
    begin
      //correct LogIn
      ESPNumber:= ADOTable1.FindField('ESP No').AsString;  //get value
      SiteAddress:= ADOTable1.FindField('Site Address').AsString; 

      ConvertLogInData; //convert ESPNo(00002) &SiteAddress(1Fh_31)
      Edit1.Text:= ESPNumber;
      Edit2.Text:= SiteAddress;

      CreatePathName;        //Create PathName

      GetSystemGridData;    //load System Grid

      GetTagdata;            //get data from ini File

      Exit;
    end;
  ADOTable1.Next();
  end;
if not found = true then
  PhoneString:= 'LogInFail*'
end;



procedure TMainForm.CreatePathName; 
{Load existing or create new directory.       
Exit: 'MainPathName' contains directory  e.g C:\FarmTrenz\ESP_00079\SiteAdd_0Ch_12\TagDB\2018\July\}
var CurrentYear, CurrentMonth: string;
begin
  CurrentYear:= FormatDateTime('yyyy', (now));  //format = DataDate:  18/10/17
  CurrentMonth:= FormatDateTime('mmmm', (now)); //DataTime: 17:28:32

  MainPathname:= 'C:\FarmTrenz\' + 'ESP_' + ESPNumber + '\';
  if not DirectoryExists(MainPathname) then
    if not CreateDir(MainPathname) then       //make ESP Number folder
      raise Exception.Create(MainPathname);

  MainPathname:= MainPathname + 'SiteAdd_' + SiteAddress + '\';
  if not DirectoryExists(MainPathname) then
    if not CreateDir(MainPathname) then     //make Site Address folder
      raise Exception.Create(MainPathname);

  MainPathname:= MainPathname + 'TagDB' + '\';
  if not DirectoryExists(MainPathname) then
    if not CreateDir(MainPathname) then    //make Tag DataBase folder
      raise Exception.Create(MainPathname);

  TagDBPath:= MainPathName;
  MainPathname:= MainPathname + CurrentYear + '\';
  if not DirectoryExists(MainPathname) then
    if not CreateDir(MainPathname) then          //make year folder
      raise Exception.Create(MainPathname);

  MainPathname:= MainPathname + CurrentMonth + '\';
  if not DirectoryExists(MainPathname) then
    if not CreateDir(MainPathname) then          //make month folder
      raise Exception.Create(MainPathname);
end;






procedure TMainForm.GetSystemGridData;
{Get Device Signal Types to be sent back to Phone.
 Load CheckListBox settings from existing file. If no file exists then check all CheckListBoxes for all Signals}
var i: integer;
begin
  IniFile:= TIniFile.Create(MainForm.TagDBPath + 'PhoneSystemsList.ini');   //read TagDB file
  try
    if inifile.SectionExists('PhoneSystemsList') then
    begin
      IniFile.ReadSection('PhoneSystemsList', Form2.Checklistbox1.Items);  //does 'section' exist

      for i := 0 to Form2.Checklistbox1.Items.Count - 1 do          //get complete System List
        Form2.CheckListbox1.Checked[i] := IniFile.ReadBool('PhoneSystemsList', Form2.Checklistbox1.Items[i], False);
      end
      else
        Form2.CheckListBox1.CheckAll(cbChecked, false, true);   //No file so check all boxes
  finally
    IniFile.Free;
  end;
end;






procedure TMainForm.GetTagData;            
{Get Data for Selected Device Types to send back to Phone.
Entry: 'DeviceString' contains the Device numbers to get Data from to send back to Phone.      
Exit: Selected Data is in 'PhoneString'}
var CPos: integer;
TempString: string;
begin
  //  Username_Smith,Password_2222,_1,3,27,17,*     DeviceString = 1,2,27,17,*
  PhoneString:= '';     //clear for starting a new Phone String.
  TempString:= DeviceString;
  while TempString <> '*' do
  begin
    CPos:= pos(',',TempString);
    DeviceType:= copy(TempString,1,CPos-1);      //DeviceString
    delete(TempString,1,CPos);

    if DeviceType <> '' then
    begin
      case StrToInt(DeviceType) of  //process each selected Device Type
        0:  ;
        1:  GetTIMTagData;
        2:  GetTIMTagData;
        3:  GetKIMTagData;
        4:  GetKIMTagData;
        5:  GetPIMTagData;
        6:  GetPIMTagData;
        7:  GetPLinkTagData;
        8:  ;
        9:  GetSumpTagData;
       10: GetPondTagData;
       11: GetBoreTagData;
       12: GetTankTagData;
       13: GetFenceTagData;
       14: GetFlowTagData;
       15: GetHydrantTagData;
       16: GetSiloTagData;
       17: GetVatTagData;
       18: GetMovmentTagData;
       19: ;
       20: GetSoilTagData;
       21: GetMagFlowTagData;
       22: ;
       23: GetLevelTagData;
       24: GetPWeatherTagData;
       25: GetTWeatherTagData;
       26: GetTankLinkTagData;
       27: GetDPLinkTagData;
       28: GetHubTagData;
       29: GetFencePulseTagData;
       30: GetLarallTagData;
       31: GetVatBasicTagData;
      end;
    end;
  end;
end;




////////////////////////////////////Start of TIM Tx Data  01 \\\\\\\\\\\\\\\\\
procedure TMainForm.GetTIMTagData;
{Open TagDB.ini file for this Client. Pick out all selected signal types and add to 'PhoneString for sending back to Client Phone. 
Entry: 'TagDBPath' contains directory of where to find current Data for this Device.     
Exit: Phone Message to send back is in 'PhoneString'}
var TempInt, BitIndex, TimIDCode: integer;
    TIMValue: string;
begin
  FoundaDevice:= false;
  for TimIDCode := 1 to 5 do             //do for all 5 TIM Tx Units
  begin
    IniFile:= TIniFile.Create(TagDBPath+'TagDB.ini');   //read Tag ini file
  try
    ValueString:= IniFile.ReadString('TimTx'+IntToStr(TimIDCode)+'_Speed','Value','999');    //Speed
    if ValueString <> '999' then
    begin
      PhoneString:= PhoneString + '01,';  //signify start of TIM Messages for phone
      FoundaDevice:= true;
      TagLastUpDate:= IniFile.ReadString('TimTx'+IntToStr(TimIDCode)+'_Speed','LastUpdate','0');
      CheckTagAge;    //check if Tag Value isn't too old
      If TagValid = True then TIMValue:=  IntToStr(HexToDec(ValueString))+ ' spr' else TIMValue:= 'N/A';
      if Form2.CheckListBox1.Checked[0] then
        PhoneString:= PhoneString + 'TIM '+IntToStr(TimIDCode)+' Speed:  ' + TIMValue + ',';  //create phone string

      ValueString:= IniFile.ReadString('TimTx'+IntToStr(TimIDCode)+'_Pressure','Value','0');   //Pressure
      If TagValid = True then TIMValue:= IntToStr(HexToDec(ValueString))+ ' psi' else TIMValue:= 'N/A';
      if Form2.CheckListBox1.Checked[1] then
        PhoneString:= PhoneString + 'TIM '+IntToStr(TimIDCode)+' Press:  ' + TIMValue + ',';    //add to phone string

      ValueString:= IniFile.ReadString('TimTx'+IntToStr(TimIDCode)+'_BatVolts','Value','0');  //Bat Volts
      ConvertToOneDecimalPlace;   //convert 'ValueString' to 12.8 Volts
      If TagValid = True then TIMValue:= ValueString + ' Volts' else TIMValue:= 'N/A';
      if Form2.CheckListBox1.Checked[2] then
        PhoneString:= PhoneString + 'TIM '+IntToStr(TimIDCode)+' BatV:  ' + TIMValue + ',';     //add to phone string

      ValueString:= IniFile.ReadString('TimTx'+IntToStr(TimIDCode)+'_Status1', 'Value','0');           //Status1
      If TagValid = True then TIMStatus1:= ValueString;
    end
    else TagValid:= false;

  finally
    IniFile.Free;
  end;

  {Process Status1 Bits}
  if TagValid <> false then
  begin
    TempInt:= StrToInt('$' + TIMStatus1); {value to extract is a Hex value saved as a string in 'Status1'}
    for BitIndex:= 0 to 7 do
      if TempInt and (1 shl BitIndex) <> 0 then
      begin
        case BitIndex of
          0: RStop:= '1';
          1: RStart:= '1';
          2: PhoneString:= PhoneString + 'TIM '+IntToStr(TimIDCode)+' Moving 2 Slow Alm,';   //add to phone string
          3: Start_B:= '1';
          4: PhoneString:= PhoneString + 'TIM '+IntToStr(TimIDCode)+' Not Moving Alm,';   //add to phone string
          5: PhoneString:= PhoneString + 'TIM '+IntToStr(TimIDCode)+' Low Bat Alm,';   //add to phone string
          6: PhoneString:= PhoneString + 'TIM '+IntToStr(TimIDCode)+' EOT Alm,';       //add to phone string
          7: GSPFail:= '1';
        end;
      end;
    end;
    If FoundaDevice = True then
    begin
      FoundaDevice:= false;
      PhoneString:= PhoneString + '*';
    end;
  end;

  Edit22.Text:= PhoneString;
end;
////////////////////////////////////End of TIM Tx Data \\\\\\\\\\\\\\\\\\\\\\\

Привет, Реми - Спасибо за ваш ответ. Я добавил код в соответствии с просьбой. Вы увидите, что (LogInFromPhone) является общим c для всех клиентов и местами использует элементы управления пользовательским интерфейсом. Как мне вызвать и обработать это потокобезопасным способом?

...