Test-Apps für Gemini Api und Blockly

This commit is contained in:
Michael Schimmel
2025-06-10 17:08:09 +02:00
parent 312be15cc8
commit 0e598d595e
8 changed files with 1555 additions and 35 deletions
+108
View File
@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Blockly zu Umgangssprache - Mit Schleifen & Zählern</title>
<style>
body { font-family: sans-serif; }
#container { display: flex; }
#outputArea { margin-left: 20px; }
#codeOutput { width: 400px; height: 580px; font-size: 14px; white-space: pre-wrap; border: 1px solid #ccc; }
#blocklyDiv { height: 600px; width: 800px; }
.button-bar { margin-bottom: 10px; }
</style>
</head>
<body>
<h1>Mein "Umgangssprache"-Generator</h1>
<p>Bauen Sie links Ihre Blöcke. Rechts erscheint die textliche Beschreibung.</p>
<div class="button-bar">
<button onclick="saveWorkspace()">Speichern</button>
<button onclick="loadWorkspace()">Letzten Stand laden</button>
</div>
<div id="container">
<div id="blocklyDiv"></div>
<div id="outputArea">
<h2>Erzeugter Text:</h2>
<textarea id="codeOutput" readonly></textarea>
</div>
</div>
<script src="https://unpkg.com/blockly/blockly_compressed.js"></script>
<script src="https://unpkg.com/blockly/blocks_compressed.js"></script>
<script src="https://unpkg.com/blockly/msg/de.js"></script>
<script>
window.onload = function() {
let workspace;
// === UNSER UMGANGSSPRACHE-GENERATOR ===
Blockly.Human = new Blockly.Generator('Human');
Blockly.Human.ORDER_ATOMIC = 0;
Blockly.Human.scrub_ = function(block, code, opt_thisOnly) { const nextBlock = block.nextConnection && block.nextConnection.targetBlock(); const nextCode = opt_thisOnly ? '' : Blockly.Human.blockToCode(nextBlock); return code + nextCode; };
Blockly.Human.prefixLines = function(text, prefix) { return prefix + text.split(/\n(?!$)/).join('\n' + prefix); };
// --- Generatoren für die einzelnen Blöcke ---
Blockly.Human.forBlock['controls_if'] = function(block) { let conditionCode = Blockly.Human.valueToCode(block, 'IF0', Blockly.Human.ORDER_ATOMIC) || 'eine Bedingung erfüllt ist'; let branchCode = Blockly.Human.statementToCode(block, 'DO0') || 'nichts.\n'; let code = `Also, überprüfe, ${conditionCode}.\n`; code += `Wenn das zutrifft, dann mach Folgendes:\n`; code += Blockly.Human.prefixLines(branchCode, ' '); return code; };
Blockly.Human.forBlock['logic_compare'] = function(block) { const OPERATORS = {'EQ': 'ist gleich', 'NEQ': 'ist nicht gleich', 'LT': 'ist kleiner als', 'LTE': 'ist kleiner oder gleich', 'GT': 'ist größer als', 'GTE': 'ist größer oder gleich'}; const operator = OPERATORS[block.getFieldValue('OP')]; const value_a = Blockly.Human.valueToCode(block, 'A', Blockly.Human.ORDER_ATOMIC) || 'irgendetwas'; const value_b = Blockly.Human.valueToCode(block, 'B', Blockly.Human.ORDER_ATOMIC) || 'irgendetwas anderes'; const code = `ob der Wert von ${value_a} ${operator} als ${value_b}`; return [code, Blockly.Human.ORDER_ATOMIC]; };
Blockly.Human.forBlock['math_number'] = function(block) { return [String(block.getFieldValue('NUM')), Blockly.Human.ORDER_ATOMIC]; };
Blockly.Human.forBlock['variables_get'] = function(block) { const varId = block.getFieldValue('VAR'); const variable = block.workspace.getVariableById(varId); const varName = variable ? variable.name : 'unbekannt'; return [`der Variable "${varName}"`, Blockly.Human.ORDER_ATOMIC]; };
Blockly.Human.forBlock['variables_set'] = function(block) { const varId = block.getFieldValue('VAR'); const variable = block.workspace.getVariableById(varId); const varName = variable ? variable.name : 'unbekannt'; const value = Blockly.Human.valueToCode(block, 'VALUE', Blockly.Human.ORDER_ATOMIC) || 'nichts'; return `Setze die Variable "${varName}" auf den Wert ${value}.\n`; };
Blockly.Human.forBlock['text_print'] = function(block) { const value = Blockly.Human.valueToCode(block, 'TEXT', Blockly.Human.ORDER_ATOMIC) || '"nichts"'; return `Gib auf dem Bildschirm aus: ${value}\n`; };
Blockly.Human.forBlock['text'] = function(block) { return [`"${block.getFieldValue('TEXT')}"`, Blockly.Human.ORDER_ATOMIC]; };
Blockly.Human.forBlock['controls_repeat_ext'] = function(block) { const repeats = Blockly.Human.valueToCode(block, 'TIMES', Blockly.Human.ORDER_ATOMIC) || 'einige Male'; const branch = Blockly.Human.statementToCode(block, 'DO') || 'mache nichts.\n'; let code = `Wiederhole die folgenden Schritte ${repeats} Mal:\n`; code += Blockly.Human.prefixLines(branch, ' '); return code; };
// NEU: Generator für den "ändere Variable um"-Block
Blockly.Human.forBlock['math_change'] = function(block) {
const varId = block.getFieldValue('VAR');
const variable = block.workspace.getVariableById(varId);
const varName = variable ? variable.name : 'unbekannt';
const delta = Blockly.Human.valueToCode(block, 'DELTA', Blockly.Human.ORDER_ATOMIC) || '0';
return `Ändere die Variable "${varName}" um den Wert ${delta}.\n`;
};
// === INITIALISIERUNG VON BLOCKLY ===
const toolboxXml = `
<xml>
<category name="Logik" colour="%{BKY_LOGIC_HUE}">
<block type="controls_if"></block>
<block type="logic_compare"></block>
</category>
<category name="Schleifen" colour="%{BKY_LOOPS_HUE}">
<block type="controls_repeat_ext">
<value name="TIMES"><shadow type="math_number"><field name="NUM">10</field></shadow></value>
</block>
</category>
<category name="Text" colour="%{BKY_TEXTS_HUE}">
<block type="text_print"></block>
<block type="text"></block>
</category>
<category name="Mathematik" colour="%{BKY_MATH_HUE}">
<block type="math_number"></block>
<block type="math_change">
<value name="DELTA"><shadow type="math_number"><field name="NUM">1</field></shadow></value>
</block>
</category>
<category name="Variablen" colour="%{BKY_VARIABLES_HUE}" custom="VARIABLE"></category>
</xml>`;
workspace = Blockly.inject('blocklyDiv', {
toolbox: toolboxXml
});
// === Funktionen für Speichern und Laden (unverändert) ===
window.saveWorkspace = function() { const state = Blockly.serialization.workspaces.save(workspace); localStorage.setItem('blocklyWorkspace', JSON.stringify(state)); alert('Arbeitsbereich gespeichert!'); }
window.loadWorkspace = function() { const stateString = localStorage.getItem('blocklyWorkspace'); if (!stateString) { return; } const state = JSON.parse(stateString); Blockly.serialization.workspaces.load(state, workspace); }
function updateCode(event) { if (event.type == Blockly.Events.UI) return; const code = Blockly.Human.workspaceToCode(workspace); document.getElementById('codeOutput').value = code; }
workspace.addChangeListener(updateCode);
loadWorkspace();
};
</script>
</body>
</html>
+9
View File
@@ -0,0 +1,9 @@
unit ApiKey;
interface
const Value = 'AIzaSyALccoMxn0_wcHswavhu5rzdglLeH6gVlI';
implementation
end.
+16
View File
@@ -0,0 +1,16 @@
program GeminiAccess;
uses
FastMM5,
Vcl.Forms,
MainUnit in 'MainUnit.pas' {Form1},
ApiKey in 'ApiKey.pas';
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
File diff suppressed because it is too large Load Diff
Binary file not shown.
+52
View File
@@ -0,0 +1,52 @@
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 1011
ClientWidth = 1105
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -12
Font.Name = 'Segoe UI'
Font.Style = []
OnCreate = FormCreate
OnDestroy = FormDestroy
DesignSize = (
1105
1011)
TextHeight = 15
object AnswerMemo: TMemo
Left = 0
Top = 0
Width = 1105
Height = 809
Anchors = [akLeft, akTop, akRight, akBottom]
ScrollBars = ssVertical
TabOrder = 0
end
object ExecButton: TButton
Left = 992
Top = 928
Width = 75
Height = 25
Anchors = [akRight, akBottom]
Caption = 'Execute'
TabOrder = 1
OnClick = ExecButtonClick
end
object PromptMemo: TMemo
Left = 0
Top = 815
Width = 1105
Height = 89
Anchors = [akLeft, akRight, akBottom]
ScrollBars = ssVertical
TabOrder = 2
end
object ApplicationEvents: TApplicationEvents
OnIdle = ApplicationEventsIdle
Left = 136
Top = 104
end
end
+204
View File
@@ -0,0 +1,204 @@
unit MainUnit;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
System.Net.HttpClient, System.Net.HttpClientComponent, System.JSON, System.Net.Mime, Vcl.AppEvnts,
Myc.Futures, System.Generics.Collections, ApiKey;
type
// NEU: Ein Record, um einen einzelnen Redebeitrag in der Konversation zu speichern
TChatTurn = record
Role: string; // 'user' oder 'model'
Text: string;
end;
TForm1 = class(TForm)
AnswerMemo: TMemo;
ExecButton: TButton;
ApplicationEvents: TApplicationEvents;
PromptMemo: TMemo;
procedure ExecButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ApplicationEventsIdle(Sender: TObject; var Done: Boolean);
private
FHttpClient: TNetHTTPClient;
FMyApiKey: string;
FAnswer: TFuture<String>;
FChatHistory: TList<TChatTurn>; // NEU: Liste für den Konversationsverlauf
procedure SendPrompt;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
const
GEMINI_MODEL = 'gemini-2.5-flash-preview-05-20';
API_BASE_URL = 'https://generativelanguage.googleapis.com/v1beta/models/';
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
FHttpClient := TNetHTTPClient.Create(nil);
FMyApiKey := ApiKey.Value; // Eingelesen aus ApiKey.pas
// NEU: Initialisiert die Liste für den Konversationsverlauf
FChatHistory := TList<TChatTurn>.Create;
// Optional: Füge hier einen initialen System-Prompt hinzu, wenn gewünscht.
// Dieser wird dann immer als erste Nachricht mitgesendet.
// var initialTurn: TChatTurn;
// initialTurn.Role := 'user';
// initialTurn.Text := 'Du bist ein Delphi-Entwicklungs-Assistent...';
// FChatHistory.Add(initialTurn);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FAnswer.WaitFor;
FHttpClient.Free;
FChatHistory.Free; // NEU: Gibt die Verlaufsliste frei
end;
procedure TForm1.ApplicationEventsIdle(Sender: TObject; var Done: Boolean);
var
answerText: string;
modelTurn: TChatTurn;
begin
if FAnswer.Done.IsSet and not ExecButton.Enabled then
begin
answerText := FAnswer.Value;
AnswerMemo.Lines.Add(answerText);
// NEU: Füge die Antwort des Modells zum Verlauf hinzu
if not answerText.StartsWith('Fehler!') then
begin
modelTurn.Role := 'model';
modelTurn.Text := answerText;
FChatHistory.Add(modelTurn);
end;
FAnswer := FAnswer.Null;
ExecButton.Enabled := True;
end;
end;
procedure TForm1.ExecButtonClick(Sender: TObject);
begin
SendPrompt;
end;
procedure TForm1.SendPrompt;
var
userPrompt: string;
userTurn: TChatTurn;
begin
if not ExecButton.Enabled then
Exit;
userPrompt := PromptMemo.Lines.Text;
if userPrompt.IsEmpty then
begin
ShowMessage('Bitte geben Sie einen Prompt ein.');
Exit;
end;
// NEU: Füge die aktuelle Benutzereingabe zum Verlauf hinzu
userTurn.Role := 'user';
userTurn.Text := userPrompt;
FChatHistory.Add(userTurn);
// NEU: Leere das Eingabefeld für die nächste Nachricht
PromptMemo.Clear;
ExecButton.Enabled := False;
AnswerMemo.Lines.Add('');
AnswerMemo.Lines.Add('--- [USER] ---');
AnswerMemo.Lines.Add(userPrompt);
AnswerMemo.Lines.Add('... sende Anfrage an Gemini ...');
FAnswer := TFuture<string>.Construct(
function: string
var
jsonRequest: TJSONObject;
jsonContents: TJSONArray;
requestBody: TStringStream;
response: IHTTPResponse;
responseJson: TJSONValue;
apiUrl: string;
turn: TChatTurn;
begin
try
// 1. JSON-Body aus dem gesamten Konversationsverlauf erstellen
jsonRequest := TJSONObject.Create;
try
jsonContents := TJSONArray.Create;
jsonRequest.AddPair('contents', jsonContents);
// NEU: Iteriere durch den Verlauf und baue die JSON-Struktur auf
for turn in FChatHistory do
begin
var jsonTurn := TJSONObject.Create;
jsonTurn.AddPair('role', TJSONString.Create(turn.Role));
var jsonParts := TJSONArray.Create;
jsonTurn.AddPair('parts', jsonParts);
var jsonText := TJSONObject.Create;
jsonText.AddPair('text', TJSONString.Create(turn.Text));
jsonParts.Add(jsonText);
jsonContents.Add(jsonTurn);
end;
// 2. Request senden
requestBody := TStringStream.Create(jsonRequest.ToString, TEncoding.UTF8);
try
apiUrl := API_BASE_URL + GEMINI_MODEL + ':generateContent?key=' + FMyApiKey;
response := FHttpClient.Post(apiUrl, requestBody, nil);
// 3. Antwort verarbeiten
if (response.StatusCode = 200) then
begin
responseJson := TJSONObject.ParseJSONValue(response.ContentAsString(TEncoding.UTF8));
try
Result := responseJson.GetValue<TJSONArray>('candidates')
.Items[0].GetValue<TJSONObject>('content')
.GetValue<TJSONArray>('parts')
.Items[0].GetValue<string>('text');
finally
responseJson.Free;
end;
end
else
begin
Result := 'Fehler! Status: ' + response.StatusCode.ToString + ' - ' + response.StatusText +
sLineBreak + response.ContentAsString;
end;
finally
requestBody.Free;
end;
finally
jsonRequest.Free;
end;
except
on E: Exception do
begin
Result := '--- FEHLER IM TASK ---' + sLineBreak + E.Message;
end;
end;
end);
end;
end.
+36 -35
View File
@@ -5,7 +5,7 @@ unit Myc.Trade.DataStream;
Provides a data server for loading time-series data and streaming it. Provides a data server for loading time-series data and streaming it.
This unit contains the core components for handling historical data: This unit contains the core components for handling historical data:
- TDataRecord: A generic record for a single time-stamped data entry. - TAuraFileDataRecord: A generic record for a single time-stamped data entry.
- IDataServer/TAuraDataServer: A server component responsible for loading and caching - IDataServer/TAuraDataServer: A server component responsible for loading and caching
series of data files from disk. Each server instance manages its own cache, series of data files from disk. Each server instance manages its own cache,
but all instances share a central load gate. but all instances share a central load gate.
@@ -29,11 +29,6 @@ uses
Myc.Trade.DataPoint; Myc.Trade.DataPoint;
type type
// A generic record for a single time-stamped data entry.
TDataRecord<T: record> = packed record
TimeStamp: TDateTime;
Data: T;
end;
// Represents a generic data stream capable of providing sequential data chunks. // Represents a generic data stream capable of providing sequential data chunks.
IDataStream<T> = interface IDataStream<T> = interface
@@ -69,10 +64,16 @@ type
procedure ClearCache; procedure ClearCache;
end; end;
// A generic record for a single time-stamped data entry.
TAuraFileDataRecord<T: record> = packed record
TimeStamp: TDateTime;
Data: T;
end;
IAuraDataServer<T: record> = interface(IDataServer<T>) IAuraDataServer<T: record> = interface(IDataServer<T>)
['{481E27DE-DC58-49AF-B8A8-B6316980C423}'] ['{481E27DE-DC58-49AF-B8A8-B6316980C423}']
function LoadDataFile(const FileName: string): TFuture<TArray<TDataRecord<T>>>; function LoadDataFile(const FileName: string): TFuture<TArray<TAuraFileDataRecord<T>>>;
function LoadDataSeries(const FileName: string): TFuture<TArray<TDataRecord<T>>>; function LoadDataSeries(const FileName: string): TFuture<TArray<TAuraFileDataRecord<T>>>;
function EnumerateAssetFiles: TArray<String>; function EnumerateAssetFiles: TArray<String>;
end; end;
@@ -87,7 +88,7 @@ type
Name: String; Name: String;
Age: TDateTime; Age: TDateTime;
LastUsed: TDateTime; LastUsed: TDateTime;
Data: TFuture<TArray<TDataRecord<T>>>; Data: TFuture<TArray<TAuraFileDataRecord<T>>>;
end; end;
private private
// The cache is per-instance. // The cache is per-instance.
@@ -101,8 +102,8 @@ type
FLoadGate: TLatch; FLoadGate: TLatch;
protected protected
class function ReadCompressedData(const InputStream: TStream): TArray<TDataRecord<T>>; static; class function ReadCompressedData(const InputStream: TStream): TArray<TAuraFileDataRecord<T>>; static;
class function ReadUncompressedData(const InputStream: TStream): TArray<TDataRecord<T>>; static; class function ReadUncompressedData(const InputStream: TStream): TArray<TAuraFileDataRecord<T>>; static;
public public
constructor Create(const APath: String); constructor Create(const APath: String);
@@ -123,8 +124,8 @@ type
procedure ClearCache; procedure ClearCache;
function EnumerateAssetFiles: TArray<String>; function EnumerateAssetFiles: TArray<String>;
function LoadDataFile(const FileName: string): TFuture<TArray<TDataRecord<T>>>; function LoadDataFile(const FileName: string): TFuture<TArray<TAuraFileDataRecord<T>>>;
function LoadDataSeries(const FileName: string): TFuture<TArray<TDataRecord<T>>>; function LoadDataSeries(const FileName: string): TFuture<TArray<TAuraFileDataRecord<T>>>;
property Path: String read GetPath; property Path: String read GetPath;
end; end;
@@ -135,9 +136,9 @@ type
FDataServer: TAuraDataServer<T>; FDataServer: TAuraDataServer<T>;
FIsLiveData: TMutable<Boolean>.IWriteable; FIsLiveData: TMutable<Boolean>.IWriteable;
FCurrentFileName: string; FCurrentFileName: string;
FCurrentData: TFuture<TArray<TDataRecord<T>>>; FCurrentData: TFuture<TArray<TAuraFileDataRecord<T>>>;
FNextFileName: string; FNextFileName: string;
FNextData: TFuture<TArray<TDataRecord<T>>>; FNextData: TFuture<TArray<TAuraFileDataRecord<T>>>;
FCurrPosInFile: Int64; FCurrPosInFile: Int64;
FCurrentIdx: Int64; FCurrentIdx: Int64;
FLastTimeStamp: TDateTime; FLastTimeStamp: TDateTime;
@@ -314,12 +315,12 @@ begin
Result := FPath; Result := FPath;
end; end;
function TAuraDataServer<T>.LoadDataFile(const FileName: string): TFuture<TArray<TDataRecord<T>>>; function TAuraDataServer<T>.LoadDataFile(const FileName: string): TFuture<TArray<TAuraFileDataRecord<T>>>;
var var
parsedPath, parsedSymbol: string; parsedPath, parsedSymbol: string;
parsedYear, parsedMonth: Integer; parsedYear, parsedMonth: Integer;
begin begin
Result := TFuture<TArray<TDataRecord<T>>>.Null; Result := TFuture<TArray<TAuraFileDataRecord<T>>>.Null;
if not TryParseFileName(FileName, parsedPath, parsedSymbol, parsedYear, parsedMonth) then if not TryParseFileName(FileName, parsedPath, parsedSymbol, parsedYear, parsedMonth) then
exit; exit;
@@ -368,8 +369,8 @@ begin
.Construct( .Construct(
TLatch.Enqueue(FLoadGate), // Use shared class var FLoadGate TLatch.Enqueue(FLoadGate), // Use shared class var FLoadGate
function: TBytes begin Result := TFile.ReadAllBytes(capFileName); end) function: TBytes begin Result := TFile.ReadAllBytes(capFileName); end)
.Chain<TArray<TDataRecord<T>>>( .Chain<TArray<TAuraFileDataRecord<T>>>(
function(bytes: TBytes): TArray<TDataRecord<T>> function(bytes: TBytes): TArray<TAuraFileDataRecord<T>>
begin begin
var zipMemoryStream := TBytesStream.Create(bytes); var zipMemoryStream := TBytesStream.Create(bytes);
try try
@@ -383,9 +384,9 @@ begin
else if TFile.Exists(FileName) then else if TFile.Exists(FileName) then
begin begin
Result := Result :=
TFuture<TArray<TDataRecord<T>>>.Construct( TFuture<TArray<TAuraFileDataRecord<T>>>.Construct(
TLatch.Enqueue(FLoadGate), // Use shared class var FLoadGate TLatch.Enqueue(FLoadGate), // Use shared class var FLoadGate
function: TArray<TDataRecord<T>> function: TArray<TAuraFileDataRecord<T>>
begin begin
if TFile.Exists(capFileName) then if TFile.Exists(capFileName) then
begin begin
@@ -418,11 +419,11 @@ begin
end; end;
end; end;
function TAuraDataServer<T>.LoadDataSeries(const FileName: string): TFuture<TArray<TDataRecord<T>>>; function TAuraDataServer<T>.LoadDataSeries(const FileName: string): TFuture<TArray<TAuraFileDataRecord<T>>>;
var var
loadedState: TState; loadedState: TState;
loadedFiles: TArray<TFuture<TArray<TDataRecord<T>>>>; loadedFiles: TArray<TFuture<TArray<TAuraFileDataRecord<T>>>>;
liveData: TFuture<TArray<TDataRecord<T>>>; liveData: TFuture<TArray<TAuraFileDataRecord<T>>>;
tabFiles: TList<string>; tabFiles: TList<string>;
currentFile: string; currentFile: string;
liveFilePath: string; liveFilePath: string;
@@ -452,7 +453,7 @@ begin
end; end;
end; end;
var loadedFileList := TList<TFuture<TArray<TDataRecord<T>>>>.Create; var loadedFileList := TList<TFuture<TArray<TAuraFileDataRecord<T>>>>.Create;
var loadStates := TList<TState>.Create; var loadStates := TList<TState>.Create;
try try
for currentFile in tabFiles do for currentFile in tabFiles do
@@ -462,7 +463,7 @@ begin
loadStates.Add(data.Done); loadStates.Add(data.Done);
end; end;
liveData := TFuture<TArray<TDataRecord<T>>>.Null; liveData := TFuture<TArray<TAuraFileDataRecord<T>>>.Null;
if liveFilePath <> '' then if liveFilePath <> '' then
begin begin
liveData := LoadDataFile(liveFilePath); liveData := LoadDataFile(liveFilePath);
@@ -481,11 +482,11 @@ begin
end; end;
Result := Result :=
TFuture<TArray<TDataRecord<T>>>.Construct( TFuture<TArray<TAuraFileDataRecord<T>>>.Construct(
loadedState, loadedState,
function: TArray<TDataRecord<T>> function: TArray<TAuraFileDataRecord<T>>
begin begin
var tickList := TList<TDataRecord<T>>.Create; var tickList := TList<TAuraFileDataRecord<T>>.Create;
try try
var overallLastTabTickTime: TDateTime := 0; var overallLastTabTickTime: TDateTime := 0;
var cnt := 0; var cnt := 0;
@@ -508,7 +509,7 @@ begin
); );
end; end;
class function TAuraDataServer<T>.ReadCompressedData(const InputStream: TStream): TArray<TDataRecord<T>>; class function TAuraDataServer<T>.ReadCompressedData(const InputStream: TStream): TArray<TAuraFileDataRecord<T>>;
var var
decompressionStream: TStream; decompressionStream: TStream;
localHeader: TZipHeader; localHeader: TZipHeader;
@@ -547,7 +548,7 @@ begin
end; end;
end; end;
class function TAuraDataServer<T>.ReadUncompressedData(const InputStream: TStream): TArray<TDataRecord<T>>; class function TAuraDataServer<T>.ReadUncompressedData(const InputStream: TStream): TArray<TAuraFileDataRecord<T>>;
var var
fileSize: Int64; fileSize: Int64;
recordCount, bytesRead: Integer; recordCount, bytesRead: Integer;
@@ -555,10 +556,10 @@ begin
SetLength(Result, 0); SetLength(Result, 0);
InputStream.Position := 0; InputStream.Position := 0;
fileSize := InputStream.Size; fileSize := InputStream.Size;
if (fileSize = 0) or ((fileSize mod SizeOf(TDataRecord<T>)) <> 0) then if (fileSize = 0) or ((fileSize mod SizeOf(TAuraFileDataRecord<T>)) <> 0) then
exit; exit;
recordCount := fileSize div SizeOf(TDataRecord<T>); recordCount := fileSize div SizeOf(TAuraFileDataRecord<T>);
if recordCount > 0 then if recordCount > 0 then
begin begin
SetLength(Result, recordCount); SetLength(Result, recordCount);
@@ -596,8 +597,8 @@ end;
function TAuraFileStream<T>.GetChunk(var Data: array of TDataPoint<T>): Integer; function TAuraFileStream<T>.GetChunk(var Data: array of TDataPoint<T>): Integer;
var var
item: TDataRecord<T>; item: TAuraFileDataRecord<T>;
currData: TArray<TDataRecord<T>>; currData: TArray<TAuraFileDataRecord<T>>;
begin begin
Result := 0; Result := 0;
if not FCurrentData.Done.IsSet then if not FCurrentData.Done.IsSet then