Test-Apps für Gemini Api und Blockly
This commit is contained in:
@@ -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>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
unit ApiKey;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
const Value = 'AIzaSyALccoMxn0_wcHswavhu5rzdglLeH6gVlI';
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -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.
@@ -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
|
||||||
@@ -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.
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user