Registriert seit: 29. Dez 2006
Ort: NRW
831 Beiträge
Delphi 10.4 Sydney
|
[FireDAC][DatS]-1.Name[View] ist in der Liste doppelt vorhanden
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 = ' JSON' then begin
// Weg über die vorhandene API-hier nicht relevant
end
else if Methode = ' QUERY' then 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)
|