Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEW-FEATURE] RenderableContentControl optimalization #278

Merged
merged 9 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,6 @@
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\AXSharp.connectors\src\AXSharp.Connector\AXSharp.Connector.csproj" />
</ItemGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>Full</DebugType>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,16 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Web"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\AXSharp.Presentation.Blazor\AXSharp.Presentation.Blazor.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" />
</ItemGroup>

<ItemGroup>
<Folder Include="wwwroot\css\" />
<Folder Include="wwwroot\js\" />
</ItemGroup>


<ItemGroup>
<ProjectReference Include="..\AXSharp.Presentation.Blazor\AXSharp.Presentation.Blazor.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public partial class RenderableComponentBase : ComponentBase, IRenderableCompone
[Parameter]
public object RccContainer { get; set; } = new Object();

[Parameter] public int PollingInterval { get; set; }
[Parameter] public int PollingInterval { get; set; } = 250;

/// <summary>
/// Disposes this object as well as communication resources used by this component.
Expand Down Expand Up @@ -159,17 +159,50 @@ public void UpdateValuesOnChangeOutFocus(OnlinerBase tag)

protected void HandlePropertyChanged(object sender, PropertyChangedEventArgs a)
{
InvokeAsync(StateHasChanged);
if (ShouldBeUpdated(sender as ITwinElement))
{
InvokeAsync(StateHasChanged);
}
}

protected void HandleShadowPropertyChanged(object sender, ValueChangedEventArgs a)
{
InvokeAsync(StateHasChanged);
}

protected virtual bool ShouldBeUpdated(ITwinElement element)
{
if (element == null)
return true;

var renderableContentControl = (RccContainer as RenderableContentControl);

if(renderableContentControl != null)
{
if (renderableContentControl.Context.GetParent().GetConnector().SubscriptionMode == ReadSubscriptionMode.AutoSubscribeUsedVariables)
{
return true;
}

if (renderableContentControl.Context.GetParent().GetConnector().SubscriptionMode == ReadSubscriptionMode.Polling)
{
return this.PolledElements.Contains(element);
}
}

return true;
}

protected void HandlePropertyChangedOnOutFocus(object sender, PropertyChangedEventArgs a)
{
if(!HasFocus) InvokeAsync(StateHasChanged);
if (!HasFocus)
{
if (ShouldBeUpdated(sender as ITwinElement))
{
InvokeAsync(StateHasChanged);
}
}

}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using AXSharp.Connector.ValueTypes;
using Microsoft.AspNetCore.Components;
using Serilog;
using System;
using System.Collections.Generic;
using System.ComponentModel;
Expand All @@ -15,6 +16,7 @@ public partial class TemplateBaseOnline<T> : TemplateBase<T>

protected override Task OnInitializedAsync()
{
AddToPolling(this.Onliner, this.PollingInterval);
UpdateValuesOnChangeOutFocus(Onliner);
return base.OnInitializedAsync();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Irony"/>
<PackageReference Include="Irony.Interpreter"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp"/>
<PackageReference Include="Irony" />
<PackageReference Include="Irony.Interpreter" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\AXSharp.connectors\src\AXSharp.Connector\AXSharp.Connector.csproj" />
Expand Down
11 changes: 11 additions & 0 deletions src/AXSharp.compiler/src/AXSharp.Cs.Compiler/Helpers/CsHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
// https://github.com/ix-ax/axsharp/blob/dev/LICENSE
// Third party licenses: https://github.com/ix-ax/axsharp/blob/master/notices.md

using System.Text;
using AX.ST.Semantic.Model.Declarations;
using AX.ST.Syntax.Tree;
using AXSharp.Connector;

namespace AXSharp.Compiler.Cs.Helpers;

Expand Down Expand Up @@ -43,6 +45,15 @@ public static string CreateGenericSwapperMethodToPlainer(string methodName, stri
return $"public async {qualifier} Task<T> {methodName}<T>(){{\n return await (dynamic)this.{methodName}Async();\n}}";
}

public static string CreateGenericHasChangedMethodMethod(string methodName, string pocoTypeName, bool isExtended = false)
{
var qualifier = isExtended ? "override" : "virtual";
var sb = new StringBuilder();
sb.AppendLine("///<inheritdoc/>\r\n");
sb.AppendLine($"public async {qualifier} Task<bool> {TwinObjectExtensions.HasChangedMethodName}<T>(T plain){{\n return await this.{methodName}((dynamic)plain);\n}}");
return sb.ToString();
}

public static string CreateGenericSwapperMethodFromPlainer(string methodName, string pocoTypeName, bool isExtended)
{
var qualifier = isExtended ? "override" : "virtual";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ public void CreateClassDeclaration(IClassDeclarationSyntax classDeclarationSynta
AddToSource(CsOnlinerPlainerShadowToPlainBuilder.Create(visitor, classDeclaration, this, isExtended).Output);
AddToSource(CsOnlinerPlainerShadowToPlainProtectedBuilder.Create(visitor, classDeclaration, this, isExtended).Output);
AddToSource(CsOnlinerPlainerPlainToShadowBuilder.Create(visitor, classDeclaration, this, isExtended).Output);

AddToSource(CsOnlinerHasChangedBuilder.Create(visitor, classDeclaration, this, isExtended).Output);

AddPollingMethod(isExtended);
AddCreatePocoMethod(classDeclaration, isExtended);
Expand Down Expand Up @@ -296,6 +298,7 @@ public void CreateStructuredType(IStructTypeDeclarationSyntax structTypeDeclarat
AddToSource(CsOnlinerPlainerShadowToPlainProtectedBuilder.Create(visitor, structuredTypeDeclaration, this).Output);
AddToSource(CsOnlinerPlainerPlainToShadowBuilder.Create(visitor, structuredTypeDeclaration, this).Output);

AddToSource(CsOnlinerHasChangedBuilder.Create(visitor, structuredTypeDeclaration, this).Output);
AddPollingMethod(false);

AddCreatePocoMethod(structuredTypeDeclaration, false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// AXSharp.Compiler.Cs
// Copyright (c) 2023 Peter Kurhajec (PTKu), MTS, and Contributors. All Rights Reserved.
// Contributors: https://github.com/ix-ax/axsharp/graphs/contributors
// See the LICENSE file in the repository root for more information.
// https://github.com/ix-ax/axsharp/blob/dev/LICENSE
// Third party licenses: https://github.com/ix-ax/axsharp/blob/master/notices.md

using AX.ST.Semantic;
using AX.ST.Semantic.Model.Declarations;
using AX.ST.Semantic.Model.Declarations.Types;
using AXSharp.Compiler.Core;
using AXSharp.Compiler.Cs.Helpers;
using AXSharp.Connector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AXSharp.Compiler.Cs.Onliner
{
internal class CsOnlinerHasChangedBuilder : ICombinedThreeVisitor
{
private readonly StringBuilder _memberDeclarations = new();

protected CsOnlinerHasChangedBuilder(ISourceBuilder sourceBuilder)
{
SourceBuilder = sourceBuilder;
}

private ISourceBuilder SourceBuilder { get; }

public string Output => _memberDeclarations.ToString().FormatCode();


public void CreateFieldDeclaration(IFieldDeclaration fieldDeclaration, IxNodeVisitor visitor)
{
if (fieldDeclaration.IsMemberEligibleForTranspile(SourceBuilder, "POCO"))
{
CreateAssignment(fieldDeclaration.Type, fieldDeclaration);
}
}

public void CreateInterfaceDeclaration(IInterfaceDeclaration interfaceDeclaration, IxNodeVisitor visitor)
{
//
}

public void CreateVariableDeclaration(IVariableDeclaration variableDeclaration, IxNodeVisitor visitor)
{
if (variableDeclaration.IsMemberEligibleForTranspile(SourceBuilder, "POCO"))
{
CreateAssignment(variableDeclaration.Type, variableDeclaration);
}
}

internal void CreateAssignment(ITypeDeclaration typeDeclaration, IDeclaration declaration)
{
var index_var_name = "i760901_3001_mimi";
switch (typeDeclaration)
{
case IInterfaceDeclaration interfaceDeclaration:
break;
case IClassDeclaration classDeclaration:
//case IAnonymousTypeDeclaration anonymousTypeDeclaration:
case IStructuredTypeDeclaration structuredTypeDeclaration:
AddToSource($" if(await {declaration.Name}.{MethodName}(plain.{declaration.Name}, latest.{declaration.Name})) somethingChanged = true;");
break;
case IArrayTypeDeclaration arrayTypeDeclaration:
if (arrayTypeDeclaration.IsMemberEligibleForConstructor(SourceBuilder))
{
switch (arrayTypeDeclaration.ElementTypeAccess.Type)
{
case IClassDeclaration classDeclaration:
case IStructuredTypeDeclaration structuredTypeDeclaration:
AddToSource(
$"for (int {index_var_name} = 0; {index_var_name} < latest.{declaration.Name}.Length; {index_var_name}++)\r\n{{\r\n if (await {declaration.Name}.ElementAt({index_var_name}).DetectsAnyChangeAsync(plain.{declaration.Name}[{index_var_name}], latest.{declaration.Name}[{index_var_name}]))\r\n somethingChanged = true;\r\n}}");
break;
case IScalarTypeDeclaration scalarTypeDeclaration:
case IStringTypeDeclaration stringTypeDeclaration:
AddToSource(
$"for (int {index_var_name} = 0; {index_var_name} < latest.{declaration.Name}.Length; {index_var_name}++)\r\n{{\r\n if (latest.{declaration.Name}.ElementAt({index_var_name}) != plain.{declaration.Name}[{index_var_name}])\r\n somethingChanged = true;\r\n}}");
break;
}
}

break;
case IReferenceTypeDeclaration referenceTypeDeclaration:
break;
case IEnumTypeDeclaration enumTypeDeclaration:
AddToSource($" if(plain.{declaration.Name} != ({declaration.Type.FullyQualifiedName})latest.{declaration.Name}) somethingChanged = true;");
break;
case INamedValueTypeDeclaration namedValueTypeDeclaration:
AddToSource($" if(plain.{declaration.Name} != {declaration.Name}.LastValue) somethingChanged = true;");
break;
case IScalarTypeDeclaration scalarTypeDeclaration:
case IStringTypeDeclaration stringTypeDeclaration:
AddToSource($" if(plain.{declaration.Name} != {declaration.Name}.LastValue) somethingChanged = true;");
break;
}
}

public void CreateArrayTypeDeclaration(IArrayTypeDeclaration arrayTypeDeclaration, IxNodeVisitor visitor)
{
//
}


protected void AddToSource(string token, string separator = " ")
{
_memberDeclarations.Append($"{token}{separator}");
}

public void AddTypeConstructionParameters(string parametersString)
{
AddToSource(parametersString);
}

protected static readonly string MethodName = $"Detects{TwinObjectExtensions.HasChangedMethodName}";

public static CsOnlinerHasChangedBuilder Create(IxNodeVisitor visitor, IStructuredTypeDeclaration semantics,
ISourceBuilder sourceBuilder)
{
var builder = new CsOnlinerHasChangedBuilder(sourceBuilder);

builder.AddToSource(CsHelpers.CreateGenericHasChangedMethodMethod(MethodName, $"Pocos.{semantics.FullyQualifiedName}"));

builder.AddToSource("///<summary>\n");
builder.AddToSource("///Compares if the current plain object has changed from the previous object.This method is used by the framework to determine if the object has changed and needs to be updated.\n");
builder.AddToSource("///[!NOTE] Any member in the hierarchy that is ignored by the compilers (e.g. when CompilerOmitAttribute is used) will not be compared, and therefore will not be detected as changed.\n");
builder.AddToSource("///</summary>\n");


builder.AddToSource($"public async Task<bool> {MethodName}(Pocos.{semantics.FullyQualifiedName} plain, Pocos.{semantics.FullyQualifiedName} latest = null){{\n");
builder.AddToSource("var somethingChanged = false;");
builder.AddToSource("if(latest == null) latest = await this._OnlineToPlainNoacAsync();");
builder.AddToSource("return await Task.Run(async () => {\n");
semantics.Fields.ToList().ForEach(p => p.Accept(visitor, builder));
builder.AddToSource($"plain = latest;");
builder.AddToSource($"return somethingChanged;");
builder.AddToSource($"}});");
builder.AddToSource($"}}");
return builder;
}

public static CsOnlinerHasChangedBuilder Create(IxNodeVisitor visitor, IClassDeclaration semantics,
ISourceBuilder sourceBuilder, bool isExtended)
{
var builder = new CsOnlinerHasChangedBuilder(sourceBuilder);

builder.AddToSource(CsHelpers.CreateGenericHasChangedMethodMethod(MethodName, $"Pocos.{semantics.FullyQualifiedName}", isExtended));

var qualifier = isExtended ? "new" : string.Empty;

builder.AddToSource("///<summary>\n");
builder.AddToSource("///Compares if the current plain object has changed from the previous object.This method is used by the framework to determine if the object has changed and needs to be updated.\n");
builder.AddToSource("///[!NOTE] Any member in the hierarchy that is ignored by the compilers (e.g. when CompilerOmitAttribute is used) will not be compared, and therefore will not be detected as changed.\n");
builder.AddToSource("///</summary>\n");
builder.AddToSource($"public {qualifier} async Task<bool> {MethodName}(Pocos.{semantics.FullyQualifiedName} plain, Pocos.{semantics.FullyQualifiedName} latest = null){{\n");

builder.AddToSource("if(latest == null) latest = await this._OnlineToPlainNoacAsync();");
builder.AddToSource("var somethingChanged = false;");

builder.AddToSource("return await Task.Run(async () => {\n");
if (isExtended)
{
builder.AddToSource($"if(await base.{MethodName}(plain)) return true;");
}

semantics.Fields.ToList().ForEach(p => p.Accept(visitor, builder));

builder.AddToSource($"plain = latest;");
builder.AddToSource($"return somethingChanged;");
builder.AddToSource($"}});");
builder.AddToSource($"}}");
return builder;
}

/// <inheritdoc />
public void CreateDocComment(IDocComment semanticTypeAccess, ICombinedThreeVisitor data)
{
AddToSource(semanticTypeAccess.AddDocumentationComment(SourceBuilder));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -299,14 +299,23 @@ private void CompareOutputs(string memberName)
var actualFileContent = File.ReadAllText(actualSourceFile);
var expectedFileContent = File.Exists(expectedSourceFile) ? File.ReadAllText(expectedSourceFile) : string.Empty;

var actualFileContentLines = actualFileContent.Split("\n").Select(a => a.Trim()).ToArray();
var expectedFileContentLines = expectedFileContent.Split("\n").Select(a => a.Trim()).ToArray();

for (int i = 0; i < expectedFileContentLines.Length; i++)
{
Assert.Equal(expectedFileContentLines[i], actualFileContentLines[i]);
}


output.WriteLine("------------------------ actual ------------------");
output.WriteLine(actualSourceFile);
output.WriteLine(actualFileContent);
output.WriteLine("------------------------ expected ------------------");
output.WriteLine(expectedSourceFile);
output.WriteLine(expectedFileContent);

Assert.Equal(expectedFileContent, actualFileContent);

}

public string GetMethodName()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ public Task PlainToShadow<T>(T plain)
{
throw new NotImplementedException();
}

public Task<bool> AnyChangeAsync<T>(T plain)
{
throw new NotImplementedException();
}
}
public class GenericMemberB : IPlain
{
Expand Down
Loading