Skip to content

Commit

Permalink
Merge pull request #2050 from eliasbruvik/FIX-1899
Browse files Browse the repository at this point in the history
FIX-1899 Curve data streaming
  • Loading branch information
eliasbruvik authored Oct 2, 2023
2 parents 5d5a36e + 5c65d83 commit 8c8e1b3
Show file tree
Hide file tree
Showing 17 changed files with 439 additions and 139 deletions.
2 changes: 1 addition & 1 deletion Src/Witsml/Data/Curves/DateTimeIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public override int GetHashCode()
{
return this.Value.GetHashCode();
}

public static TimeSpan operator -(DateTimeIndex index1, DateTimeIndex index2)
{
return index1.Value - index2.Value;
Expand Down
2 changes: 1 addition & 1 deletion Src/Witsml/Data/Curves/Index.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public abstract class Index : IComparable<Index>
{
return index1.CompareTo(index2) >= 0;
}

public static Index operator -(Index index1, Index index2)
{
return index1 switch
Expand Down
10 changes: 5 additions & 5 deletions Src/Witsml/Data/Curves/TimeSpanIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,25 @@ namespace Witsml.Data.Curves;
public class TimeSpanIndex : Index
{
private const string TimeSpanPattern = @"hh\:mm\:ss";

public TimeSpan Value { get; }

public TimeSpanIndex(TimeSpan value)
{
Value = value;
}

public TimeSpanIndex(long milliseconds)
{
Value = TimeSpan.FromMilliseconds(milliseconds);
}

[Obsolete("AddEpsilon is deprecated due to assuming 3 decimals of precision for depth indexes. Some WITSML servers do not use 3 decimals.")]
public override Index AddEpsilon()
{
throw new System.NotImplementedException();
}

public override int CompareTo(Index that)
{
TimeSpanIndex thatWitsmlTimeSpan = (TimeSpanIndex)that;
Expand Down Expand Up @@ -55,7 +55,7 @@ public override bool IsNullValue()
{
return Value == TimeSpan.Zero;
}

public override string ToString()
{
return GetValueAsString();
Expand Down
5 changes: 3 additions & 2 deletions Src/WitsmlExplorer.Api/Jobs/AnalyzeGapJob.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;

using WitsmlExplorer.Api.Models;

namespace WitsmlExplorer.Api.Jobs;
Expand All @@ -12,7 +13,7 @@ public record AnalyzeGapJob : Job
/// Log reference object
/// </summary>
public LogObject LogReference { get; init; }

/// <summary>
/// Array of mnemonics names
/// </summary>
Expand All @@ -22,7 +23,7 @@ public record AnalyzeGapJob : Job
/// Size of the GAP for depth
/// </summary>
public double GapSize { get; set; }

/// <summary>
/// Size of the GAP for dateTime
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ public interface IObjectReference
public string Name { get; }
public string WellName { get; }
public string WellboreName { get; }
}
}
2 changes: 1 addition & 1 deletion Src/WitsmlExplorer.Api/Models/ObjectOnWellbore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace WitsmlExplorer.Api.Models
{
public class ObjectOnWellbore : IObjectReference
public class ObjectOnWellbore : IObjectReference
{
public string Uid { get; init; }
public string WellUid { get; init; }
Expand Down
9 changes: 5 additions & 4 deletions Src/WitsmlExplorer.Api/Workers/AnalyzeGapWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using WitsmlExplorer.Api.Models;
using WitsmlExplorer.Api.Models.Reports;
using WitsmlExplorer.Api.Services;

using Index = Witsml.Data.Curves.Index;

namespace WitsmlExplorer.Api.Workers;
Expand All @@ -30,7 +31,7 @@ public class AnalyzeGapWorker : BaseWorker<AnalyzeGapJob>, IWorker
{
public JobType JobType => JobType.AnalyzeGaps;
public AnalyzeGapWorker(ILogger<AnalyzeGapJob> logger, IWitsmlClientProvider witsmlClientProvider) : base(witsmlClientProvider, logger) { }

/// <summary>
/// Find all gaps for selected mnemonics and required size of gap. If no mnemonics are selected, all mnemonics on the log will be analyzed.
/// </summary>
Expand All @@ -45,7 +46,7 @@ public AnalyzeGapWorker(ILogger<AnalyzeGapJob> logger, IWitsmlClientProvider wit
bool isDepthLog = job.LogReference.IndexType == WitsmlLog.WITSML_INDEX_TYPE_MD;
List<AnalyzeGapReportItem> gapReportItems = new();
List<string[]> logDataRows = new();

var witsmlLog = await WorkerTools.GetLog(GetTargetWitsmlClientOrThrow(), job.LogReference, ReturnElements.HeaderOnly);
if (witsmlLog == null)
{
Expand All @@ -56,7 +57,7 @@ public AnalyzeGapWorker(ILogger<AnalyzeGapJob> logger, IWitsmlClientProvider wit

var jobMnemonics = job.Mnemonics.Any() ? job.Mnemonics.ToList() : witsmlLog.LogCurveInfo.Select(x => x.Mnemonic).ToList();
await using LogDataReader logDataReader = new(GetTargetWitsmlClientOrThrow(), witsmlLog, new List<string>(jobMnemonics), Logger);

WitsmlLogData logData = await logDataReader.GetNextBatch();
var logMnemonics = logData?.MnemonicList.Split(CommonConstants.DataSeparator).Select((value, index) => new { index, value }).ToList();
while (logData != null)
Expand Down Expand Up @@ -148,7 +149,7 @@ private IEnumerable<AnalyzeGapReportItem> GetAnalyzeGapReportItem(string mnemoni
Index lastValueIndex = inputIndexList.FirstOrDefault();

if (lastValueIndex == null) return gapValues;

foreach (var inputIndex in inputIndexList.Skip(1))
{
var gapSize = isLogIncreasing ? (inputIndex - lastValueIndex) : (lastValueIndex - inputIndex);
Expand Down
3 changes: 3 additions & 0 deletions Src/WitsmlExplorer.Frontend/components/Constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export const DateFormat = {
DATETIME_MS: "DD.MM.YYYY HH:mm:ss.SSS"
};

export const MILLIS_IN_SECOND = 1000;
export const SECONDS_IN_MINUTE = 60;

export const STORAGE_THEME_KEY = "selectedTheme";
export const STORAGE_TIMEZONE_KEY = "selectedTimeZone";
export const STORAGE_MODE_KEY = "selectedMode";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,25 @@ interface CurveValuesPlotProps {
data: any[];
columns: ExportableContentTableColumn<CurveSpecification>[];
name: string;
autoRefresh: boolean;
isDescending?: boolean;
}

export const CurveValuesPlot = React.memo((props: CurveValuesPlotProps): React.ReactElement => {
const { data, columns, name } = props;
const { data, columns, name, autoRefresh, isDescending = false } = props;
const {
operationState: { colors }
} = useContext(OperationContext);
const chart = useRef<ECharts>(null);
const selectedLabels = useRef<Record<string, boolean>>(null);
const scrollIndex = useRef<number>(0);
const horizontalZoom = useRef<[number, number]>([0, 100]);

const chartOption = React.useMemo(() => getChartOption(data, columns, name, colors), [data, columns, name, colors]);
const chartOption = getChartOption(data, columns, name, colors, isDescending, autoRefresh, selectedLabels.current, scrollIndex.current, horizontalZoom.current);

const onLegendChange = (params: { name: string; selected: Record<string, boolean> }) => {
const actionType = Object.values(params.selected).every((s) => s === false) ? "legendSelect" : "legendUnSelect";
const shouldShowAll = Object.values(params.selected).every((s) => s === false);
const actionType = shouldShowAll ? "legendSelect" : "legendUnSelect";
chart.current.dispatchAction({ type: "legendSelect", name: params.name });
for (const legend in params.selected) {
if (legend !== params.name) {
Expand All @@ -32,10 +38,29 @@ export const CurveValuesPlot = React.memo((props: CurveValuesPlotProps): React.R
});
}
}
selectedLabels.current = {
...Object.keys(params.selected).reduce((acc, key) => {
acc[key] = shouldShowAll;
return acc;
}, {} as Record<string, boolean>),
[params.name]: true
};
};

const onLegendScroll = (params: { scrollDataIndex: number }) => {
scrollIndex.current = params.scrollDataIndex;
};

const onDataZoom = (params: { dataZoomId: string; start: number; end: number }) => {
if (params.dataZoomId == "horizontalZoom") {
horizontalZoom.current = [params.start, params.end];
}
};

const handleEvents = {
legendselectchanged: onLegendChange
legendselectchanged: onLegendChange,
legendscroll: onLegendScroll,
datazoom: onDataZoom
};

return (
Expand All @@ -53,8 +78,20 @@ export const CurveValuesPlot = React.memo((props: CurveValuesPlotProps): React.R
});
CurveValuesPlot.displayName = "CurveValuesPlot";

const getChartOption = (data: any[], columns: ExportableContentTableColumn<CurveSpecification>[], name: string, colors: Colors) => {
const valueOffsetFromColumn = 0.01;
const getChartOption = (
data: any[],
columns: ExportableContentTableColumn<CurveSpecification>[],
name: string,
colors: Colors,
isDescending: boolean,
autoRefresh: boolean,
selectedLabels: Record<string, boolean>,
scrollIndex: number,
horizontalZoom: [number, number]
) => {
const VALUE_OFFSET_FROM_COLUMN = 0.01;
const AUTO_REFRESH_SIZE = 300;
if (autoRefresh) data = data.slice(-AUTO_REFRESH_SIZE); // Slice to avoid lag while streaming
const indexCurve = columns[0].columnOf.mnemonic;
const indexUnit = columns[0].columnOf.unit;
const isTimeLog = columns[0].type == ContentType.DateTime;
Expand All @@ -73,7 +110,7 @@ const getChartOption = (data: any[], columns: ExportableContentTableColumn<Curve
return {
title: {
left: "center",
text: name,
text: name + (autoRefresh ? ` (last ${AUTO_REFRESH_SIZE} rows)` : ""),
textStyle: {
color: colors.text.staticIconsDefault
}
Expand All @@ -87,7 +124,9 @@ const getChartOption = (data: any[], columns: ExportableContentTableColumn<Curve
selectedMode: "onlyHover",
textStyle: {
color: colors.text.staticIconsDefault
}
},
selected: selectedLabels,
scrollDataIndex: scrollIndex
},
grid: {
left: "30px",
Expand All @@ -104,8 +143,8 @@ const getChartOption = (data: any[], columns: ExportableContentTableColumn<Curve
},
xAxis: {
type: "value",
min: (value: { min: number; max: number }) => value.min - valueOffsetFromColumn,
max: (value: { min: number; max: number }) => (value.max - value.min < 1 ? value.min + 1 - valueOffsetFromColumn : dataColumns.length),
min: (value: { min: number; max: number }) => value.min - VALUE_OFFSET_FROM_COLUMN,
max: (value: { min: number; max: number }) => (value.max - value.min < 1 ? value.min + 1 - VALUE_OFFSET_FROM_COLUMN : dataColumns.length),
minInterval: 1,
maxInterval: 1,
splitLine: {
Expand All @@ -120,6 +159,7 @@ const getChartOption = (data: any[], columns: ExportableContentTableColumn<Curve
color: colors.text.staticIconsDefault,
hideOverlap: false,
showMaxLabel: false,
showMinLabel: autoRefresh || null,
formatter: (param: number) => {
const index = Math.floor(param);
if (index >= dataColumns.length) return "";
Expand All @@ -131,7 +171,7 @@ const getChartOption = (data: any[], columns: ExportableContentTableColumn<Curve
},
yAxis: {
type: isTimeLog ? "time" : "value",
inverse: true,
inverse: !isDescending,
min: (value: { min: number }) => value.min - 0.001, // The edge points can disappear, so make sure everything is shown
max: (value: { max: number }) => value.max + 0.001,
axisLabel: {
Expand All @@ -142,29 +182,35 @@ const getChartOption = (data: any[], columns: ExportableContentTableColumn<Curve
}
},
dataZoom: [
autoRefresh
? null
: {
orient: "vertical",
filterMode: "empty",
type: "inside"
},
autoRefresh
? null
: {
orient: "vertical",
filterMode: "empty",
type: "slider",
labelFormatter: () => ""
},
{
orient: "vertical",
filterMode: "empty",
type: "inside"
},
{
orient: "vertical",
filterMode: "empty",
type: "slider",
labelFormatter: () => ""
},
{
id: "horizontalZoom",
orient: "horizontal",
filterMode: "empty",
type: "slider",
startValue: 0,
endValue: 8,
start: horizontalZoom[0],
end: horizontalZoom[1],
minValueSpan: 1,
maxValueSpan: 12,
labelFormatter: () => ""
}
],
animation: false,
backgroundColor: colors.ui.backgroundDefault,
series: dataColumns.map((col, i) => {
const minMaxValue = minMaxValues.find((v) => v.curve == col.columnOf.mnemonic);
return {
Expand All @@ -176,7 +222,7 @@ const getChartOption = (data: any[], columns: ExportableContentTableColumn<Curve
const index = row[indexCurve];
const value = row[col.columnOf.mnemonic];
const normalizedValue = (value - minMaxValue.minValue) / (minMaxValue.maxValue - minMaxValue.minValue || 1);
const offsetNormalizedValue = normalizedValue * (1 - 2 * valueOffsetFromColumn) + valueOffsetFromColumn + i;
const offsetNormalizedValue = normalizedValue * (1 - 2 * VALUE_OFFSET_FROM_COLUMN) + VALUE_OFFSET_FROM_COLUMN + i;
return [offsetNormalizedValue, index];
})
};
Expand Down
Loading

0 comments on commit 8c8e1b3

Please sign in to comment.