DataStream updated

This commit is contained in:
Michael Schimmel
2026-01-21 16:05:59 +01:00
parent ef4583c2ae
commit b90d29f2bc
3 changed files with 128 additions and 100 deletions
+13 -31
View File
@@ -102,11 +102,8 @@ type
private private
FOnEvent: TNotifyEvent; FOnEvent: TNotifyEvent;
{ Private declarations } { Private declarations }
{$ifdef TICKDATA} FFileCache: IDataFileCache<TArray<TBytes>>;
FServer: IDataServer<TAuraAskBidFileItem>; FServer: IDataServer;
{$else}
FServer: IDataServer<TOhlcItem>;
{$endif}
FSymbols: TFuture<TArray<String>>; FSymbols: TFuture<TArray<String>>;
FTerminate: TEvent; FTerminate: TEvent;
FProcessDone: TState; FProcessDone: TState;
@@ -228,10 +225,11 @@ begin
FTerminate := TEvent.CreateEvent; FTerminate := TEvent.CreateEvent;
// Create an instance of the TAuraTABFileServer. The server can be reused for multiple stream creations. [364] // Create an instance of the TAuraTABFileServer. The server can be reused for multiple stream creations. [364]
FFileCache := TDataFileCache<TArray<TBytes>>.Create;
{$ifdef TICKDATA} {$ifdef TICKDATA}
FServer := TAskBidFileServer.Create('\\COFFEE\TickData\Pepperstone'); FServer := TTickFileServer.Create(FFileCache, '\\COFFEE\TickData\Pepperstone');
{$else} {$else}
FServer := TM1FileServer.Create('\\COFFEE\TickData\Pepperstone'); FServer := TM1FileServer.Create(FFileCache, '\\COFFEE\TickData\Pepperstone');
{$endif} {$endif}
SymbolsComboBox.Enabled := false; SymbolsComboBox.Enabled := false;
@@ -339,7 +337,7 @@ begin
var panel := pnlChart.AddPanel; var panel := pnlChart.AddPanel;
panel.AddDoubleSeries(equity, TAlphaColors.Blue, 3); panel.AddDoubleSeries(equity, TAlphaColors.Blue, 3);
FProcessDone := FProcessDone + FServer.ProcessData(Symbol, terminated, ticker.Consumer); FProcessDone := FProcessDone + (FServer as IM1FileServer).ProcessData(Symbol, terminated, ticker.Consumer);
end; end;
@@ -587,44 +585,28 @@ begin
var terminated := TFlag.CreateObserver(FTerminate.Signal).State; var terminated := TFlag.CreateObserver(FTerminate.Signal).State;
{$ifdef TICKDATA} {$ifdef TICKDATA}
var ticker := TConverter.CreateTicker<TDataPoint<TAskBidItem>>; var ticker := TConverter.CreateTicker<TDataPoint<TTickRecord>>;
var lastPrice := var lastPrice :=
ticker.Chain<TDataPoint<Double>>( ticker.Producer.Chain<TDataPoint<Double>>(
function(const Tick: TDataPoint<TAskBidItem>): TDataPoint<Double> function(const Tick: TDataPoint<TTickRecord>): TDataPoint<Double>
begin begin
Result.Time := Tick.Time; Result.Time := Tick.Time;
Result.Data := 0.5 * (Tick.Data.Ask + Tick.Data.Bid); Result.Data := 0.5 * (Tick.Data.Ask + Tick.Data.Bid);
end end
); );
var OhlcPoint := lastPrice.Chain<TDataPoint<TOhlcItem>>(TConverter.CreateTickAggregation(Timeframe)); var OhlcPoint := lastPrice.Chain<TDataPoint<TOhlcItem>>(TTradeConverter.CreateTickAggregation(Timeframe));
OhlcPoint.Producer.Link(Consumer); OhlcPoint.CreateLink(Consumer);
var Producer := FProcessDone := FProcessDone + (FServer as ITickFileServer).ProcessData(Symbol, terminated, ticker.Consumer);
TConverter<TArray<TDataPoint<TAuraAskBidFileItem>>, TArray<TDataPoint<TAskBidItem>>>.CreateGeneric(
function(const Values: TArray<TDataPoint<TAuraAskBidFileItem>>): TArray<TDataPoint<TAskBidItem>>
begin
SetLength(Result, Length(Values));
for var i := 0 to High(Result) do
begin
Result[i].Time := Values[i].Time;
Result[i].Data.Ask := Values[i].Data.Ask;
Result[i].Data.Bid := Values[i].Data.Bid;
end;
end
);
Producer.Producer.Link(ticker);
FProcessDone := FProcessDone + FServer.ProcessData(Symbol, terminated, Producer);
{$else} {$else}
var ticker := TConverter.CreateTicker<TDataPoint<TOhlcItem>>; var ticker := TConverter.CreateTicker<TDataPoint<TOhlcItem>>;
ticker.Producer.Chain(Consumer); ticker.Producer.Chain(Consumer);
FProcessDone := FProcessDone + FServer.ProcessData(Symbol, terminated, ticker.Consumer); FProcessDone := FProcessDone + (FServer as IM1FileServer).ProcessData(Symbol, terminated, ticker.Consumer);
{$endif} {$endif}
end; end;
+3 -2
View File
@@ -77,11 +77,12 @@ end;
procedure TDataFileCache<T>.Clear; procedure TDataFileCache<T>.Clear;
begin begin
FLock.Enter;
try
// Wait for any pending load operations to finish before clearing. // Wait for any pending load operations to finish before clearing.
for var cachedFile in FCachedFiles.Values do for var cachedFile in FCachedFiles.Values do
cachedFile.Data.WaitFor; cachedFile.Data.WaitFor;
FLock.Enter;
try
FCachedFiles.Clear; FCachedFiles.Clear;
finally finally
FLock.Leave; FLock.Leave;
+110 -65
View File
@@ -34,29 +34,30 @@ type
Bid: Double; Bid: Double;
end; end;
// Interface for an instantiable data server. IDataServer = interface
IDataServer<T: record> = interface
procedure ClearCache; procedure ClearCache;
function ProcessData(const Symbol: String; const Terminated: TState; const Processor: IConsumer<TArray<TDataPoint<T>>>): TState;
function EnumerateSymbols: TFuture<TArray<String>>; function EnumerateSymbols: TFuture<TArray<String>>;
end; end;
// Interface for an instantiable data server.
IDataServer<T: record> = interface(IDataServer)
function ProcessData(const Symbol: String; const Terminated: TState; const Processor: IConsumer<TArray<TDataPoint<T>>>): TState;
end;
// Metadata for a monthly data file (SYMBOL_YYYY_MM.EXT) // Metadata for a monthly data file (SYMBOL_YYYY_MM.EXT)
TDataFile = record TDataFile = record
private private
FExtension: String;
FPath: String; FPath: String;
FSymbol: String; FSymbol: String; // Includes extension, e.g. "EURUSD.M1"
FYear: Integer; FYear: Integer;
FMonth: Integer; FMonth: Integer;
FBasename: String; FBasename: String;
function GetIsValid: Boolean; function GetIsValid: Boolean;
public public
constructor Create(const APath, ASymbol, AExtension: String; AYear, AMonth: Integer; const ABasename: String); constructor Create(const APath, ASymbol: String; AYear, AMonth: Integer; const ABasename: String);
function GetBaseFileName: string; function GetBaseFileName: string;
function GetFullFileName: string; function GetFullFileName: string;
property IsValid: Boolean read GetIsValid; property IsValid: Boolean read GetIsValid;
property Extension: String read FExtension;
property Path: String read FPath; property Path: String read FPath;
property Symbol: String read FSymbol; property Symbol: String read FSymbol;
property Year: Integer read FYear; property Year: Integer read FYear;
@@ -64,22 +65,19 @@ type
end; end;
// Generic server base handling the pipeline and preloading. // Generic server base handling the pipeline and preloading.
TDataServer<T: record; R: record> = class(TInterfacedObject, IDataServer<T>) TDataServer<T: record; R: record> = class(TInterfacedObject, IDataServer, IDataServer<T>)
private private
FCachedFiles: TDataFileCache<TArray<TArray<TDataPoint<T>>>>; // Cache stores binary blobs of the MAPPED data (TDataPoint<T>)
FCachedFiles: IDataFileCache<TArray<TBytes>>;
FPath: String; FPath: String;
FSymbols: TFuture<TDictionary<String, TArray<TDataFile>>>; FSymbols: TFuture<TDictionary<String, TArray<TDataFile>>>;
function GetPath: String; function GetPath: String;
function ProcessChunks( function ProcessChunks(const MappedBlobs: TArray<TBytes>; Terminated: TState; Processor: IConsumer<TArray<TDataPoint<T>>>): TState;
const DataChunks: TArray<TArray<TDataPoint<T>>>;
Terminated: TState;
Processor: IConsumer<TArray<TDataPoint<T>>>
): TState;
function ProcessFile( function ProcessFile(
FileInfo: TDataFile; FileInfo: TDataFile;
const DataFile: TFuture<TArray<TArray<TDataPoint<T>>>>; const DataFile: TFuture<TArray<TBytes>>;
const Terminated: TState; const Terminated: TState;
Processor: IConsumer<TArray<TDataPoint<T>>> Processor: IConsumer<TArray<TDataPoint<T>>>
): TState; ): TState;
@@ -87,17 +85,22 @@ type
strict private strict private
class var class var
FLoadGate: TLatch; FLoadGate: TLatch;
class constructor CreateClass;
protected protected
// Maps the raw file record to the internal TDataPoint<T> // Maps the raw file record to the internal TDataPoint<T>
function MapRecord(const RecordBuffer: R): TDataPoint<T>; virtual; abstract; function MapRecord(const RecordBuffer: R): TDataPoint<T>; virtual; abstract;
function DoLoad(const FileName: string; const Gate: TLatch): TFuture<TArray<TArray<TDataPoint<T>>>>;
// Returns Future of Processed Blobs (Serialized TDataPoint<T>)
function DoLoad(const FileName: string; const Gate: TLatch): TFuture<TArray<TBytes>>;
function GetNextFile(const Curr: TDataFile): TDataFile; virtual; function GetNextFile(const Curr: TDataFile): TDataFile; virtual;
function ReadCompressedData(const InputStream: TStream): TArray<TArray<TDataPoint<T>>>; function ReadCompressedData(const InputStream: TStream): TArray<TBytes>;
function ReadUncompressedData(const InputStream: TStream): TArray<TArray<TDataPoint<T>>>; function ReadUncompressedData(const InputStream: TStream): TArray<TBytes>;
public public
constructor Create(const APath: String); // Cache is injected. It stores TArray<TBytes>.
constructor Create(const ACache: IDataFileCache<TArray<TBytes>>; const APath: String);
destructor Destroy; override; destructor Destroy; override;
procedure AfterConstruction; override; procedure AfterConstruction; override;
procedure ClearCache; procedure ClearCache;
@@ -105,18 +108,27 @@ type
function FindFirstFile(const Symbol: string): TFuture<TDataFile>; function FindFirstFile(const Symbol: string): TFuture<TDataFile>;
function ParseFileName(const FileName: string): TDataFile; function ParseFileName(const FileName: string): TDataFile;
function EnumerateSymbols: TFuture<TArray<String>>; function EnumerateSymbols: TFuture<TArray<String>>;
function LoadDataFile(const DataFile: TDataFile): TFuture<TArray<TArray<TDataPoint<T>>>>;
function LoadDataFile(const DataFile: TDataFile): TFuture<TArray<TBytes>>;
function ProcessData(const Symbol: String; const Terminated: TState; const Processor: IConsumer<TArray<TDataPoint<T>>>): TState; function ProcessData(const Symbol: String; const Terminated: TState; const Processor: IConsumer<TArray<TDataPoint<T>>>): TState;
property Path: String read GetPath; property Path: String read GetPath;
end; end;
TTickFileServer = class(TDataServer<TTickRecord, TTickRecord>) ITickFileServer = interface(IDataServer<TTickRecord>)
['{A6DE8B82-B0FB-48DB-A39A-CE91D861C5D2}']
end;
TTickFileServer = class(TDataServer<TTickRecord, TTickRecord>, ITickFileServer)
protected protected
function MapRecord(const RecordBuffer: TTickRecord): TDataPoint<TTickRecord>; override; function MapRecord(const RecordBuffer: TTickRecord): TDataPoint<TTickRecord>; override;
end; end;
TM1FileServer = class(TDataServer<TOhlcItem, TM1Record>) IM1FileServer = interface(IDataServer<TOhlcItem>)
['{7BB980D9-1969-49A4-991F-908F2DDAC1A0}']
end;
TM1FileServer = class(TDataServer<TOhlcItem, TM1Record>, IM1FileServer)
protected protected
function MapRecord(const RecordBuffer: TM1Record): TDataPoint<TOhlcItem>; override; function MapRecord(const RecordBuffer: TM1Record): TDataPoint<TOhlcItem>; override;
end; end;
@@ -135,11 +147,10 @@ const
{ TDataFile } { TDataFile }
constructor TDataFile.Create(const APath, ASymbol, AExtension: String; AYear, AMonth: Integer; const ABasename: String); constructor TDataFile.Create(const APath, ASymbol: String; AYear, AMonth: Integer; const ABasename: String);
begin begin
FPath := APath; FPath := APath;
FSymbol := ASymbol; FSymbol := ASymbol;
FExtension := AExtension;
FYear := AYear; FYear := AYear;
FMonth := AMonth; FMonth := AMonth;
FBasename := ABasename; FBasename := ABasename;
@@ -151,8 +162,11 @@ begin
end; end;
function TDataFile.GetFullFileName: string; function TDataFile.GetFullFileName: string;
var
ext: string;
begin begin
Result := TPath.Combine(FPath, FBasename + FExtension); ext := TPath.GetExtension(FSymbol);
Result := TPath.Combine(FPath, FBasename + ext);
end; end;
function TDataFile.GetIsValid: Boolean; function TDataFile.GetIsValid: Boolean;
@@ -162,17 +176,20 @@ end;
{ TDataServer<T, R> } { TDataServer<T, R> }
constructor TDataServer<T, R>.Create(const APath: String); class constructor TDataServer<T, R>.CreateClass;
begin
FLoadGate := TLatch.CreateLatch(0);
end;
constructor TDataServer<T, R>.Create(const ACache: IDataFileCache<TArray<TBytes>>; const APath: String);
begin begin
inherited Create; inherited Create;
FPath := APath; FPath := APath;
FCachedFiles := TDataFileCache<TArray<TArray<TDataPoint<T>>>>.Create; FCachedFiles := ACache;
end; end;
destructor TDataServer<T, R>.Destroy; destructor TDataServer<T, R>.Destroy;
begin begin
ClearCache;
FCachedFiles.Free;
inherited Destroy; inherited Destroy;
end; end;
@@ -184,6 +201,7 @@ end;
procedure TDataServer<T, R>.ClearCache; procedure TDataServer<T, R>.ClearCache;
begin begin
if Assigned(FCachedFiles) then
FCachedFiles.Clear; FCachedFiles.Clear;
end; end;
@@ -227,7 +245,7 @@ begin
TComparer<TDataFile>.Construct( TComparer<TDataFile>.Construct(
function(const Left, Right: TDataFile): Integer function(const Left, Right: TDataFile): Integer
begin begin
if Left.Year <> Right.Year then if (Left.Year <> Right.Year) then
Result := Left.Year - Right.Year Result := Left.Year - Right.Year
else else
Result := Left.Month - Right.Month; Result := Left.Month - Right.Month;
@@ -264,7 +282,7 @@ begin
var var
symbolFiles: TArray<TDataFile>; symbolFiles: TArray<TDataFile>;
begin begin
if Symbols.TryGetValue(symName, symbolFiles) and (Length(symbolFiles) > 0) then if (Symbols.TryGetValue(symName, symbolFiles)) and (Length(symbolFiles) > 0) then
Result := symbolFiles[0] Result := symbolFiles[0]
else else
Result := Default(TDataFile); Result := Default(TDataFile);
@@ -287,7 +305,7 @@ begin
foundIndex := -1; foundIndex := -1;
for var i := 0 to High(allSymbolFiles) do for var i := 0 to High(allSymbolFiles) do
begin begin
if allSymbolFiles[i].GetBaseFileName = Curr.GetBaseFileName then if (allSymbolFiles[i].GetBaseFileName = Curr.GetBaseFileName) then
begin begin
foundIndex := i; foundIndex := i;
break; break;
@@ -314,39 +332,37 @@ begin
ext := TPath.GetExtension(fileNameNoPath); ext := TPath.GetExtension(fileNameNoPath);
baseName := TPath.GetFileNameWithoutExtension(fileNameNoPath); baseName := TPath.GetFileNameWithoutExtension(fileNameNoPath);
// Format: SYMBOL_YYYY_MM
parts := baseName.Split(['_']); parts := baseName.Split(['_']);
if Length(parts) < 3 then if (Length(parts) < 3) then
exit; exit;
// Use reverse indexing for year and month
if (not TryStrToInt(parts[High(parts)], month)) or (not TryStrToInt(parts[High(parts) - 1], year)) then if (not TryStrToInt(parts[High(parts)], month)) or (not TryStrToInt(parts[High(parts) - 1], year)) then
exit; exit;
symbol := string.Join('_', Copy(parts, 0, Length(parts) - 2)); symbol := string.Join('_', Copy(parts, 0, Length(parts) - 2)) + ext;
Result := TDataFile.Create(TPath.GetDirectoryName(FileName), symbol, ext, year, month, baseName); Result := TDataFile.Create(TPath.GetDirectoryName(FileName), symbol, year, month, baseName);
end; end;
function TDataServer<T, R>.LoadDataFile(const DataFile: TDataFile): TFuture<TArray<TArray<TDataPoint<T>>>>; function TDataServer<T, R>.LoadDataFile(const DataFile: TDataFile): TFuture<TArray<TBytes>>;
begin begin
if DataFile.IsValid then if (DataFile.IsValid) then
Result := Result :=
FCachedFiles.GetOrAdd( FCachedFiles.GetOrAdd(
DataFile.GetFullFileName, DataFile.GetFullFileName,
function(const Filename: String): TFuture<TArray<TArray<TDataPoint<T>>>> begin Result := DoLoad(Filename, FLoadGate); end function(const Filename: String): TFuture<TArray<TBytes>> begin Result := DoLoad(Filename, FLoadGate); end
) )
else else
Result := TFuture<TArray<TArray<TDataPoint<T>>>>.Construct(TArray<TArray<TDataPoint<T>>>(nil)); Result := TFuture<TArray<TBytes>>.Construct(TArray<TBytes>(nil));
end; end;
function TDataServer<T, R>.DoLoad(const FileName: string; const Gate: TLatch): TFuture<TArray<TArray<TDataPoint<T>>>>; function TDataServer<T, R>.DoLoad(const FileName: string; const Gate: TLatch): TFuture<TArray<TBytes>>;
begin begin
var capFileName := FileName; var capFileName := FileName;
Result := Result :=
TFuture<TBytes> TFuture<TBytes>
.Construct(Gate.Enqueue, function: TBytes begin Result := TFile.ReadAllBytes(capFileName); end) .Construct(Gate.Enqueue, function: TBytes begin Result := TFile.ReadAllBytes(capFileName); end)
.Chain<TArray<TArray<TDataPoint<T>>>>( .Chain<TArray<TBytes>>(
function(Bytes: TBytes): TArray<TArray<TDataPoint<T>>> function(Bytes: TBytes): TArray<TBytes>
var var
compStream: TBytesStream; compStream: TBytesStream;
begin begin
@@ -359,7 +375,7 @@ begin
end); end);
end; end;
function TDataServer<T, R>.ReadCompressedData(const InputStream: TStream): TArray<TArray<TDataPoint<T>>>; function TDataServer<T, R>.ReadCompressedData(const InputStream: TStream): TArray<TBytes>;
var var
zip: TZipFile; zip: TZipFile;
decompStream: TStream; decompStream: TStream;
@@ -375,14 +391,14 @@ begin
// Search for the .bin entry created by C# // Search for the .bin entry created by C#
for var i := 0 to zip.FileCount - 1 do for var i := 0 to zip.FileCount - 1 do
begin begin
if zip.FileNames[i].EndsWith('.bin', true) then if (zip.FileNames[i].EndsWith('.bin', true)) then
begin begin
entryIdx := i; entryIdx := i;
break; break;
end; end;
end; end;
if entryIdx = -1 then if (entryIdx = -1) then
exit; exit;
zip.Read(entryIdx, decompStream, header); zip.Read(entryIdx, decompStream, header);
@@ -396,37 +412,54 @@ begin
end; end;
end; end;
function TDataServer<T, R>.ReadUncompressedData(const InputStream: TStream): TArray<TArray<TDataPoint<T>>>; function TDataServer<T, R>.ReadUncompressedData(const InputStream: TStream): TArray<TBytes>;
var var
totalRecords, chunkCount, i, j, recsInChunk: Integer; totalRecords, chunkCount, i, j, recsInChunk: Integer;
rawBuffer: TArray<R>; inputRecordSize, outputPointSize: Integer;
chunkBuffer: TArray<TDataPoint<T>>;
rawBufferChunk: TArray<R>; // Only buffer one chunk at a time
begin begin
Result := nil; Result := nil;
totalRecords := InputStream.Size div sizeof(R); inputRecordSize := SizeOf(R);
if totalRecords = 0 then outputPointSize := SizeOf(TDataPoint<T>);
exit;
// Fast block read into memory // Ensure we are working with complete records
SetLength(rawBuffer, totalRecords); if (InputStream.Size mod inputRecordSize <> 0) then
InputStream.Position := 0; raise Exception.Create('Stream size is not a multiple of record size.');
InputStream.ReadBuffer(rawBuffer[0], InputStream.Size);
totalRecords := InputStream.Size div inputRecordSize;
if (totalRecords = 0) then
exit;
chunkCount := (totalRecords + ChunkSize - 1) div ChunkSize; chunkCount := (totalRecords + ChunkSize - 1) div ChunkSize;
SetLength(Result, chunkCount); SetLength(Result, chunkCount);
SetLength(rawBufferChunk, ChunkSize); // Reuse buffer
InputStream.Position := 0;
for i := 0 to chunkCount - 1 do for i := 0 to chunkCount - 1 do
begin begin
recsInChunk := Min(ChunkSize, totalRecords - (i * ChunkSize)); recsInChunk := Min(ChunkSize, totalRecords - (i * ChunkSize));
SetLength(Result[i], recsInChunk);
// Read only needed bytes for this chunk
InputStream.ReadBuffer(rawBufferChunk[0], recsInChunk * inputRecordSize);
// Temp buffer for mapped objects
SetLength(chunkBuffer, recsInChunk);
for j := 0 to recsInChunk - 1 do for j := 0 to recsInChunk - 1 do
begin begin
Result[i][j] := MapRecord(rawBuffer[i * ChunkSize + j]); chunkBuffer[j] := MapRecord(rawBufferChunk[j]);
end; end;
// Serialize mapped data to TBytes
SetLength(Result[i], recsInChunk * outputPointSize);
Move(chunkBuffer[0], Result[i][0], recsInChunk * outputPointSize);
end; end;
end; end;
function TDataServer<T, R>.ProcessChunks( function TDataServer<T, R>.ProcessChunks(
const DataChunks: TArray<TArray<TDataPoint<T>>>; const MappedBlobs: TArray<TBytes>;
Terminated: TState; Terminated: TState;
Processor: IConsumer<TArray<TDataPoint<T>>> Processor: IConsumer<TArray<TDataPoint<T>>>
): TState; ): TState;
@@ -434,16 +467,28 @@ begin
var callProcess := var callProcess :=
function(Idx: Integer): TFunc<TState> function(Idx: Integer): TFunc<TState>
begin begin
var cChunk := DataChunks[Idx]; // Capture the blob
var cBlob := MappedBlobs[Idx];
Result := Result :=
function: TState function: TState
var
dataChunk: TArray<TDataPoint<T>>;
count: Integer;
begin begin
if not Terminated.IsSet then if not Terminated.IsSet then
Result := Processor.Consume(cChunk); begin
// Quick deserialize (Mem Copy)
count := Length(cBlob) div SizeOf(TDataPoint<T>);
SetLength(dataChunk, count);
if count > 0 then
Move(cBlob[0], dataChunk[0], Length(cBlob));
Result := Processor.Consume(dataChunk);
end;
end; end;
end; end;
for var i := 0 to High(DataChunks) do for var i := 0 to High(MappedBlobs) do
begin begin
if not Terminated.IsSet then if not Terminated.IsSet then
begin begin
@@ -455,7 +500,7 @@ end;
function TDataServer<T, R>.ProcessFile( function TDataServer<T, R>.ProcessFile(
FileInfo: TDataFile; FileInfo: TDataFile;
const DataFile: TFuture<TArray<TArray<TDataPoint<T>>>>; const DataFile: TFuture<TArray<TBytes>>;
const Terminated: TState; const Terminated: TState;
Processor: IConsumer<TArray<TDataPoint<T>>> Processor: IConsumer<TArray<TDataPoint<T>>>
): TState; ): TState;
@@ -469,9 +514,9 @@ begin
Result := Result :=
DataFile.Chain( DataFile.Chain(
function(const DataChunks: TArray<TArray<TDataPoint<T>>>): TState function(const MappedBlobs: TArray<TBytes>): TState
begin begin
var done := ProcessChunks(DataChunks, cTerminated, Processor); var done := ProcessChunks(MappedBlobs, cTerminated, Processor);
Result := Result :=
TaskManager TaskManager
.RunTask(done, function: TState begin Result := ProcessFile(nextFileInfo, nextFile, cTerminated, Processor); end); .RunTask(done, function: TState begin Result := ProcessFile(nextFileInfo, nextFile, cTerminated, Processor); end);