Einzelnen Beitrag anzeigen

Ykcim

Registriert seit: 29. Dez 2006
Ort: NRW
831 Beiträge
 
Delphi 10.4 Sydney
 
#1

[FireDAC][DatS]-1.Name[View] ist in der Liste doppelt vorhanden

  Alt 13. Dez 2023, 16:56
Datenbank: MySQL • Version: 5 • Zugriff über: FireDac
Hallo Zusammen,
ich entwickle meine erste MultiThread Client-Server App und kämpfe mit Fehlermeldungen, die ich leider nicht einsortieren kann und zu denen ich auch keine weiterführende Hilfe / Posts gefunden habe...

Es werden von 10 Maschinen der aktuelle Status aus einer Datenbank abgefragt. Für jede Abfrage wird in der ClientApp ein eigener Thread der die Daten seiner Maschine vom Server abfragt.aufgemacht. Das funktioniert fehlerfrei wenn ich die serverseitige Datenbankabfrage weglasse.

Wenn ich clientseitig eine einzelne Maschine abfrage, funktioniert es auch ohne Probleme, nur wenn ich alle 10 Maschinen gleichzeitig abfrage, kommt es zu der Fehlermeldung:
Zitat:
Erste Gelegenheit für Exception bei $7518F932. Exception-Klasse EFDException mit Meldung '[FireDAC][DatS]-1. Name [View] ist in der Liste doppelt vorhanden'. Prozess SrvrApp.exe (16920)
Die Datenbankverbindung habe ich via eines TFDManager hergestellt. Den habe ich auf persistent gestellt und den CursorKind auf "ckStatic".
Die aufrufende Procedure erstellt vorab ein Object vom Typ TMxSQL, sodass es dort zu keiner Kollision kommen kann.

Der Fehler scheint beim Öffnen der Query in dieser Funktion zu entstehen:
Delphi-Quellcode:
procedure TMxSQL.Get_Settings(var Cols: TCols; var Rows: TRows; Tabelle: string; AStream: TStream);
var Logic: TLogic;
      qry_Settings: TFDQuery;
begin
   Logic:= TLogic.Create;
   Logic.Set_Query_FDMngr(qry_Settings, 'BDHMySQL');
   Try
      qry_Settings.sql.Add('select * from ' + Tabelle);
      ExecQuery(qry_Settings, Cols, Rows); //Hier wird die Query geöffnet und das Ergebnis in Arrays of string gespeichert
      if Assigned(AStream) then begin
         qry_Settings.SaveToStream(AStream, sfJSON);
      end;
   Finally
      Logic.Free;
   End;
end;
Aber ich vermute, dass es sich um ein generelles Problem handelt und nichts wirklich mit dieser Funktion zu tun hat.

Nachstehend mal der gesamte Weg:

ClientAPP:
Delphi-Quellcode:
procedure TFrm_Main_BSC.AdvGlowButton2Click(Sender: TObject); //Hier werden alle Maschine Staties abgefragt
begin
   Actually_MLifeData(Pnl_Top_CL1, true); //Wenn nur eine abgefragt wird, gibt es keine Probleme
   Actually_MLifeData(Pnl_Top_CL2, true);
   Actually_MLifeData(Pnl_Top_CL3, true);
   Actually_MLifeData(Pnl_Top_CL4, true);
   Actually_MLifeData(Pnl_Top_CL5, true);
   Actually_MLifeData(Pnl_Bottom_CL1, true);
   Actually_MLifeData(Pnl_Bottom_CL2, true);
   Actually_MLifeData(Pnl_Bottom_CL3, true);
   Actually_MLifeData(Pnl_Bottom_CL4, true);
   Actually_MLifeData(Pnl_Bottom_CL5, true);
end;
Actually_MLifeData
Delphi-Quellcode:
procedure TFrm_Main_BSC.Actually_MLifeData(BPanel: TPanel; RefreshData: boolean = false);
var CB:TDBAdvMultiColumnDropDown;
      MThread: TMyThreads;
begin
   MThread:= TMyThreads.Create;
   TThread.NameThreadForDebugging(BPanel.Name);
   Try
      CB := TDBAdvMultiColumnDropDown(FindComponent('MCDrbBx_'+BPanel.Name));
      if CB.Items.Items[CB.ItemIndex].Text[1] = 'then
         Exit;

      MThread.TH_Actually_FactoryScreen_Start(True);
      MThread.TH_Actually_FactoryScreen.Panel := BPanel;
      MThread.TH_Actually_FactoryScreen.Refresh := RefreshData;
      MThread.TH_Actually_FactoryScreen.MaschinenId := CB.Items.Items[CB.ItemIndex].Text[1];
      MThread.TH_Actually_FactoryScreen.Write_FactoryPanel := Write_MLifeData2;
      MThread.TH_Actually_FactoryScreen.Resume;
   Finally
      BPanel.Refresh;
      MThread.Free;
   End;
end;
Thread-Definition
Delphi-Quellcode:
{ TMyThread_Actually_FactoryScreen }

procedure TMyThread_Actually_FactoryScreen.Execute;
var MxSQL: TMxSQL;
      Logic: TLogic;
begin
   inherited;
   MxSQL:= TMxSQL.Create;
   Logic:= TLogic.Create;
   Try
      MxSQL.Get_MLifeData(fCols_MLifeData, fRows_MLifeData, fMaschinenId, fPanel.Name, fRefresh); //Hier werden die Daten von der ServerApp abgefragt
      Synchronize(procedure
                  begin
                     if Assigned(fWrite_FactoryPanel) then begin
                        fWrite_FactoryPanel(fPanel, fMaschinenId, fCols_MLifeData, fRows_MLifeData);
                     end;
                  end);
   Finally
      MxSQL.Free;
      Logic.Free;
   End;
end;
Anfrage der Daten von der ServerAPP
Delphi-Quellcode:
function TMxSQL.Get_MLifeData(var Cols: TCols; var Rows: TRows; Maschinen_ID, BPanel: string; RefreshData: boolean = false): TStream;
var LClient: TxDataClient;
      LService: IMyReportsService;
      LSTream: TMemoryStream;
      RStream: TMemoryStream;
      MTable: TFDMemTable;
      Logic: TLogic;
      StreamString: WideString;
      Methode: string;
      Cols_Set_Main: TCols;
      Rows_Set_Main: TRows;
begin
   MTable:= TFDMemTable.Create(nil);
   LClient := TXDataClient.Create;
   LStream := TMemoryStream.Create;
   RStream := TMemorySTream.Create;
   Logic := TLogic.Create;
   Try
      GetSettings('hlp_properties', Cols_Set_Main, Rows_Set_Main);
      ReadSettings('LifeMonitor_Methode',Cols_Set_Main, Rows_Set_Main);
      LClient.Uri:= DB_Unit.xData_Connect.URL;
      LService:= LClient.Service<IMyReportsService>;
      Methode := ReadSettings('LifeMonitor_Methode', Cols_Set_Main, Rows_Set_Main);
      if Methode = 'JSONthen begin
       // Weg über die vorhandene API-hier nicht relevant
      end
      else if Methode = 'QUERYthen begin
         LStream:=LService.Get_LifeMData(Maschinen_ID, RefreshData) as TMemoryStream;
         LStream.Position:= 0;
         MTable.Close;
         MTable.FieldDefs.Clear;
         MTable.Fields.clear;
         MTable.LoadFromStream(LStream, sfJSON);
         Logic.MTable_ColsRows(MTable, Cols, Rows);
      end
      else begin
         ShowMessage('Methode unbekannt');
         Exit;
      end;
      Result:= RStream;
   Finally
      Logic.Free;
      LClient.Free;
      LStream.Free;
      MTable.Free;
   end;
end;
Client Write Procedure ist in der Form definiert und schreibt die Daten in die entsprechende Felder.

Server AP
Delphi-Quellcode:
function TMyReportsService.Get_LifeMData(Maschinen_ID: string; RefreshData: boolean = false; SendData: boolean = true): TStream;
var MxSQL: TMxSQL;
      LStream: TMemoryStream;
begin
   MxSQL := TMxSQL.Create;
   LStream := TMemoryStream.Create;
   Try
      MxSQL.Get_act_MachineData(Maschinen_ID, LStream, RefreshData, SendData);
      Result := LStream;
   Finally
      MxSQL.Free;
   End;
end;
Abfrage der Daten
Delphi-Quellcode:
procedure TMxSQL.Get_act_MachineData(Machine_ID: string; LStream: TMemoryStream; RefreshData, SendData: boolean);
var Logic: TLogic;
      MsQuery: TFDQuery;
      CDMQuery: TFDQuery;
      DataTable: TFDMemTable;
      Operationslist: string;
      Cols_MData, Cols_OData: TCols;
      Rows_MData, Rows_OData: TRows;
      Cols_Set: TCols;
      Rows_Set: TRows;
begin
   if (RefreshData = false) and (DB_Modul.Tmr_LM_RefreshData.Enabled = false) and (SendData = true) then begin
      RefreshData := true;
      DB_Modul.Tmr_LM_RefreshData.Enabled := true;
      DB_Modul.FLM_OfflineState := 0;
      DB_Modul.Write_LM_Protokoll('RefreshTimer aktiviert.');
   end;

   if (RefreshData = false) and (DB_Modul.Tmr_LM_RefreshData.Enabled = true) and (SendData = true) then begin
      DB_Modul.Tmr_LM_RefreshData.Enabled := true;
      DB_Modul.FLM_OfflineState := 0;
      DB_Modul.Write_LM_Protokoll('OfflineState-Counter zurückgesetzt.');
   end;
   if (DB_Modul.Tmr_LM_RefreshData.Enabled = false) then begin
      DB_Modul.Tmr_LM_RefreshData.Enabled := true;
      DB_Modul.FLM_OfflineState := 0;
      DB_Modul.Write_LM_Protokoll('RefreshTimer aktiviert. OfflineState-Counter zurückgesetzt.');
   end;

   Logic := TLogic.create;
   try
      Logic.Set_Query_FDMngr(MsQuery, 'BDHAPS');
      Logic.Set_Query_FDMngr(CDMQuery, 'BDHCDM');
      if (RefreshData = true) then begin
         DB_Modul.Write_LM_Protokoll('RefreshProzess (' + Machine_ID + ') aktiviert.');
         Get_Settings(Cols_Set, Rows_Set, 'hlp_properties'); //In dieser Funktion scheint der Fehler zu entstehen
         DB_Modul.Write_LM_Protokoll('GetSettings (' + Machine_ID + ')');
         OperationsList := Read_Settings('Life_Data_OperationList', Cols_Set, Rows_Set);
         DB_Modul.Write_LM_Protokoll('ReadSettings (' + Machine_ID + ')');
         MsQuery.SQL.Add('SELECT TOP 1 '+
                                'CASE WHEN ( '+
                                  'concat(rj.JOB_ID, ' + QuotedStr(' ') + ', JOB_NAME) is NULL) then '+
                                  'LAG(Concat(rj.JOB_ID, ' + QuotedStr(' ') + ', rj.JOB_NAME)) OVER (ORDER BY re.TIME_LOCAL DESC) ELSE '+
                                  'concat(rj.JOB_ID, ' + QuotedStr(' ') + ',rj.JOB_NAME) end AS JOB_NAME, '+
                                'CONVERT( varchar( 10 ), re.Time_Local, 104 ) AS Datum, '+
                                'convert(char(5), re.Time_Local, 108) AS Startzeit, '+
                                'rd.device_id, '+
                                'rd.device_name, '+
                                'ro.OPERATION_NAME, '+
                                'ry.FAMILY_NAME AS Operator, '+
                                'CASE WHEN ( '+
                                  'DateDiff(minute, re.Time_Local, LAG(re.Time_Local) OVER (ORDER BY re.TIME_LOCAL DESC)) is NULL) then '+
                                  'Concat(DateDiff(minute, re.Time_Local, GETDATE()),' + QuotedStr(':00 (') + ',convert(char(5), re.Time_Local, 108), ' + QuotedStr(')') + ') ELSE '+
                                  'ConCat(DateDiff(minute, re.Time_Local, LAG(re.Time_Local) OVER (ORDER BY re.TIME_LOCAL DESC)),' + QuotedStr(':00 (') + ',convert(char(5), re.Time_Local, 108), ' + QuotedStr(')') + ') end AS Dauer, '+
                                '(SELECT SPEED FROM RBC_DEVICE_BASIC_INTERVALS RDB WHERE RDB.DEVICE_BASIC_INTERVAL_KEY = '+
                                  '(SELECT MAX(Device_Basic_interval_key) FROM RBC_DEVICE_BASIC_INTERVALS RDBI WHERE RDBI.DEVICE_KEY = re.DEVICE_KEY)) AS Speed, '+
                                'CASE WHEN (max(ws.GOOD_CYCLES) = 0 OR max(ws.planned_good_cycles)=0) THEN 0 ELSE '+
                                    'ROUND(max(ws.GOOD_CYCLES)/max(ws.planned_good_cycles),2)*100 END as PERCENT_COMPLETED, '+
                                'ws.PLANNED_GOOD_AMOUNT, '+
                                'ws.GOOD_CYCLES, '+
                                'ro.OPERATION_KEY, '+
                                'ws.WORK_STEP_NAME, '+
                                'sj.CUSTOMER_NAME '+
                         'from RPS_EVENTS re '+
                         'LEFT JOIN RPS_WORK_STEPS ws ON ws.WORK_STEP_KEY = re.WORK_STEP_KEY '+
                         'LEFT JOIN RPS_JOBS rj ON rj.JOB_KEY = ws.JOB_KEY '+
                         'LEFT JOIN RPS_EMPLOYEE_ACTIVITIES ea ON ea.WORK_STEP_KEY = ws.WORK_STEP_KEY '+
                         'LEFT JOIN RPS_OPERATIONS ro ON ro.OPERATION_KEY = re.OPERATION_KEY '+
                         'LEFT JOIN RBC_DEVICES rd ON rd.device_key = re.device_key '+
                         'LEFT JOIN RBC_EMPLOYEES ry ON ry.EMPLOYEE_KEY = ea.EMPLOYEE_KEY '+
                         'LEFT JOIN SPS_JOB sj ON sj.JOBID = rj.JOB_ID '+
                         'WHERE rd.DEVICE_ID = :Device_ID '+
                         'AND CONVERT( date, re.TIME_LOCAL) = CONVERT( date, GETDATE()) '+
                         'AND ( re.OPERATION_KEY IN ( ' + OperationsList + ' ) OR (re.OPERATION_KEY IS NULL) AND (rd.DEVICE_ID = ' + QuotedStr('Suprasetter@BDHSHOOTER') + ')) '+
                         'GROUP BY rj.JOB_NAME, '+
                                  'rj.job_id, '+
                                  're.Time_Local, '+
                                  're.Device_key, '+
                                  'rd.device_id, '+
                                  'rd.device_name, '+
                                  're.OPERATION_KEY, '+
                                  'ro.OPERATION_KEY, '+
                                  'ro.OPERATION_NAME, '+
                                  'ry.FIRST_NAME, '+
                                  'ry.FAMILY_NAME, '+
                                  'ws.PERCENT_COMPLETED, '+
                                  'ws.PLANNED_GOOD_AMOUNT, '+
                                  'ws.GOOD_CYCLES, '+
                                  'ws.WORK_STEP_NAME, '+
                                  'sj.CUSTOMER_NAME '+
                         'ORDER BY re.TIME_LOCAL desc ');
         MsQuery.ParamByName('Device_ID').AsString := Machine_ID;
         MsQuery.Open;
         //Daten in Datentabelle kopieren und für Clientabfragen verfügbar machen
         DataTable := TFDMemtable(DB_Modul.FindComponent('FDTbl_LM_' + StringReplace(Machine_ID, '@','_',[rfIgnoreCase, rfReplaceAll])));
         DataTable.CopyDataSet(MsQuery, [coStructure, coRestart, coAppend]);
         DB_Modul.Write_LM_Protokoll('Refresh DatenTabelle Maschine ' + Machine_ID);
         DB_Modul.Write_LM_Protokoll('RefreshProzess (' + Machine_ID + ') beendet.')
      end;

      if SendData then begin
         if Assigned(LStream) then begin
            DataTable := TFDMemtable(DB_Modul.FindComponent('FDTbl_LM_' + StringReplace(Machine_ID, '@','_',[rfIgnoreCase, rfReplaceAll])));
            if DataTable.State = dsInactive then begin
               DB_Modul.Write_LM_Protokoll('FDTable('+Machine_ID+') ist inaktiv. Wartezeit 2 Sek.');
               Sleep(2000);
            end;
            if DataTable.State = dsBrowse then begin
               DataTable.SaveToStream(LStream, sfJSON);
               DB_Modul.Write_LM_Protokoll('Sent Daten ('+Machine_ID+') LifeMonitor.');
            end
            else begin
               DB_Modul.Write_LM_Protokoll('ERROR: FDTable nicht aktiv ('+Machine_ID+').');
            end;
         end
         else begin
            DB_Modul.Write_LM_Protokoll('ERROR: No Stream assigned ('+Machine_ID+').');
         end;
      end;
   Finally
      CDMQuery.Free;
      MsQuery.Free;
      Logic.Free;
   end;
end;;
Der Fehler scheint beim Öffnen der Query in dieser Funktion zu entstehen:
Delphi-Quellcode:
procedure TMxSQL.Get_Settings(var Cols: TCols; var Rows: TRows; Tabelle: string; AStream: TStream);
var Logic: TLogic;
      qry_Settings: TFDQuery;
begin
   Logic:= TLogic.Create;
   Logic.Set_Query_FDMngr(qry_Settings, 'BDHMySQL');
   Try
      qry_Settings.sql.Add('select * from ' + Tabelle);
      ExecQuery(qry_Settings, Cols, Rows); //Hier wird die Query geöffnet und das Ergebnis in Arrays of string gespeichert
      if Assigned(AStream) then begin
         qry_Settings.SaveToStream(AStream, sfJSON);
      end;
   Finally
      Logic.Free;
   End;
end;
ExecQuery
Delphi-Quellcode:
function TMxSQL.ExecQuery (query: TFDQuery; var Cols: TCols; var Rows: TRows; AddRows: integer = 0): integer;
var I, J: integer;
begin
   Query.Open; //Hier kracht es
   SetLength(Cols,0);
   SetLength(Rows,0, 0);
   SetLength(Cols,Query.FieldCount);
   SetLength(Rows,Query.FieldCount, Query.RecordCount+AddRows); //+1 für FloatFooter
   for J:=0 to Query.FieldCount -1 do begin
      Cols[J]:=Query.Fields.Fields[J].FieldName;
   end;
   for I:=0 to Query.RecordCount -1 do begin
      for J:=0 to Query.FieldCount -1 do begin
         Rows[J,I]:=Query.Fields.Fields[J].AsString;
      end;
      query.Next;
   end;
   Result := Query.RecordCount;
end;

Aber ich vermute, dass es sich um ein generelles Problem handelt und nichts wirklich mit dieser Funktion zu tun hat.

Ich wäre für Eure Unterstützung wirklich sehr dankbar. Habe keine Idee mehr, wie ich mich dem Problem noch nähern soll.

Vielen Dank
Patrick
Patrick

Geändert von Ykcim (13. Dez 2023 um 17:04 Uhr)
  Mit Zitat antworten Zitat