Files
UnrealEngine/Engine/Source/Developer/TraceServices/Private/Model/TableMergeTask.cpp
2025-05-18 13:04:45 +08:00

211 lines
6.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "TableMergeTask.h"
#include "Logging/TokenizedMessage.h"
#include "Tasks/Task.h"
#include "Async/TaskGraphInterfaces.h"
namespace TraceServices
{
#define LOCTEXT_NAMESPACE "TableMergeTask"
FTableMergeTask::FTableMergeTask(const TSharedPtr<IUntypedTable>& InTableA, const TSharedPtr<IUntypedTable>& InTableB, FTableMergeService::TableDiffCallback InCallback)
: TableA(InTableA)
, TableB(InTableB)
, Callback(InCallback)
{
}
void FTableMergeTask::operator()()
{
const ETableDiffResult Result = MergeTables();
TSharedPtr<FTableDiffCallbackParams> Params = MakeShared<FTableDiffCallbackParams>();
Params->Result = Result;
Params->Table = TableC;
Params->Messages = Messages;
FFunctionGraphTask::CreateAndDispatchWhenReady(
[Callback = this->Callback, Params]()
{
Callback(Params);
},
TStatId(),
nullptr,
ENamedThreads::GameThread);
}
TraceServices::ETableDiffResult FTableMergeTask::MergeTables()
{
const ITableLayout& LayoutA = TableA->GetLayout();
const ITableLayout& LayoutB = TableB->GetLayout();
if (LayoutA.GetColumnCount() != LayoutB.GetColumnCount())
{
AddError(LOCTEXT("IncompatibleLayoutColumnCount", "Layout incompatible: column count is different."));
return ETableDiffResult::EFail;
}
TableC = MakeShared<TImportTable<FImportTableRow>>();
if (!BuildCLayout(LayoutA, LayoutB, TableC->EditLayout()))
{
return ETableDiffResult::EFail;
}
const TSharedPtr<IUntypedTableReader> TableReaderA = MakeShareable(TableA->CreateReader());
const TSharedPtr<IUntypedTableReader> TableReaderB = MakeShareable(TableB->CreateReader());
FillResultUsing<FOnlyA, Sign::Positive>(*TableReaderA);
FillResultUsing<FOnlyB, Sign::Negative>(*TableReaderB);
return ETableDiffResult::ESuccess;
}
bool FTableMergeTask::BuildCLayout(const ITableLayout& LayoutA, const ITableLayout& LayoutB, TTableLayout<FImportTableRow>& LayoutC)
{
for (uint64 ColIndex = 0; ColIndex < LayoutA.GetColumnCount(); ++ColIndex)
{
const ETableColumnType ColumnType = LayoutA.GetColumnType(ColIndex);
const TCHAR* ColumnName = LayoutA.GetColumnName(ColIndex);
const uint32 Flags = LayoutA.GetColumnDisplayHintFlags(ColIndex);
ensure(ColumnType == LayoutB.GetColumnType(ColIndex));
ensure(FCString::Stricmp(ColumnName, LayoutB.GetColumnName(ColIndex)) == 0);
auto ProjectorFuncCreator = [&LayoutC]
{
return [ColIndex = LayoutC.GetColumnCount()](const FImportTableRow& Row)
{
return Row.GetValue(ColIndex);
};
};
FString DiffColumnName = FString::Format(TEXT("{0} (A-B)"), {ColumnName});
FString AColumnName = FString::Format(TEXT("{0} (A)"), {ColumnName});
FString BColumnName = FString::Format(TEXT("{0} (B)"), {ColumnName});
switch (ColumnType)
{
case TableColumnType_CString:
LayoutC.AddColumn<const TCHAR*>(ColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FBoth>(ColIndex);
break;
case TableColumnType_Bool:
{
if ((Flags & TableColumnDisplayHint_Summable) != 0)
{
// DIFF column
LayoutC.AddColumn<bool>(*DiffColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FMerged>(ColIndex);
// OriginalA
LayoutC.AddColumn<bool>(*AColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FOnlyA>(ColIndex);
// OriginalB
LayoutC.AddColumn<bool>(*BColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FOnlyB>(ColIndex);
}
else
{
LayoutC.AddColumn<bool>(ColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FBoth>(ColIndex);
}
}
break;
case TableColumnType_Int:
{
if ((Flags & TableColumnDisplayHint_Summable) != 0)
{
// DIFF column
LayoutC.AddColumn<int64>(*DiffColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FMerged>(ColIndex);
// OriginalA
LayoutC.AddColumn<uint64>(*AColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FOnlyA>(ColIndex);
// OriginalB
LayoutC.AddColumn<uint64>(*BColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FOnlyB>(ColIndex);
}
else
{
AddError(FText::Format(
LOCTEXT("LayoutInclompatibleIntNotSummmable", "Column {0} has int values but is not summable."),
{FText::FromStringView(ColumnName)}));
return false;
}
}
break;
case TableColumnType_Float:
{
if ((Flags & TableColumnDisplayHint_Summable) != 0)
{
// DIFF column
LayoutC.AddColumn<float>(*DiffColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FMerged>(ColIndex);
// OriginalA
LayoutC.AddColumn<float>(*AColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FOnlyA>(ColIndex);
// OriginalB
LayoutC.AddColumn<float>(*BColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FOnlyB>(ColIndex);
}
else
{
AddError(FText::Format(
LOCTEXT("LayoutInclompatibleFloatNotSummmable", "Column {0} has float values but is not summable."),
{FText::FromStringView(ColumnName)}));
return false;
}
}
break;
case TableColumnType_Double:
{
if ((Flags & TableColumnDisplayHint_Summable) != 0)
{
// DIFF column
LayoutC.AddColumn<double>(*DiffColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FMerged>(ColIndex);
// OriginalA
LayoutC.AddColumn<double>(*AColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FOnlyA>(ColIndex);
// OriginalB
LayoutC.AddColumn<double>(*BColumnName, ProjectorFuncCreator(), Flags);
ColumnsMap.AddDefaulted_GetRef().Emplace<FOnlyB>(ColIndex);
}
else
{
AddError(FText::Format(
LOCTEXT("LayoutInclompatibleDoubleNotSummmable", "Column {0} has double values but is not summable."),
{FText::FromStringView(ColumnName)}));
return false;
}
}
break;
case TableColumnType_Invalid:
AddError(FText::Format(
LOCTEXT("LayoutInclompatibleInvalidColumn", "Column {0} has invalid type."),
{FText::FromStringView(ColumnName)}));
return false;
default: ;
}
}
return true;
}
void FTableMergeTask::AddError(const FText& Msg)
{
Messages.Add(FTokenizedMessage::Create(EMessageSeverity::Error, Msg));
}
void FTableMergeService::MergeTables(const TSharedPtr<IUntypedTable>& TableA, const TSharedPtr<IUntypedTable>& TableB, TableDiffCallback InCallback)
{
UE::Tasks::Launch(UE_SOURCE_LOCATION, FTableMergeTask(TableA, TableB, InCallback));
}
#undef LOCTEXT_NAMESPACE
} // namespace TraceServices