Skip to content

Commit

Permalink
Clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
oguzhankoral committed Nov 12, 2024
1 parent c04328b commit a626339
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 289 deletions.
279 changes: 149 additions & 130 deletions SpeckleAutomateDotnetExample/AutomateFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
using Speckle.Automate.Sdk.Schema;
using Speckle.Core.Models;
using Speckle.Core.Models.Extensions;
using Speckle.Core.Models.GraphTraversal;

namespace TestAutomateFunction;

public enum SpeckleType
{
Wall,
Window,
Roof
Roof,
}

public static class AutomateFunction
Expand All @@ -18,31 +21,85 @@ public static async Task Run(
FunctionInputs functionInputs
)
{
var climateZone = GetClimateZone(functionInputs.ClimateZone);

Console.WriteLine("Starting execution");
_ = typeof(ObjectsKit).Assembly; // INFO: Force objects kit to initialize

Console.WriteLine("Receiving version");
var commitObject = await automationContext.ReceiveVersion();

var flatten = commitObject.Flatten().ToList();
var failedObjectIds = new List<string>();
var failedWallIds = CheckWalls(automationContext, functionInputs, flatten);
failedObjectIds.AddRange(failedWallIds);
var failedWindowIds = CheckWindows(automationContext, functionInputs, flatten);
failedObjectIds.AddRange(failedWindowIds);
var failedRoofIds = CheckRoofs(automationContext, functionInputs, flatten);
failedObjectIds.AddRange(failedRoofIds);

if (failedObjectIds.Count > 0)
var rootObject = await automationContext.ReceiveVersion();

Console.WriteLine("Flatten the root object");
var flatten = rootObject.Flatten().ToList();

Console.WriteLine("Traverse the objects by type");
var walls = GetByType(flatten, SpeckleType.Wall);
var windows = GetByType(flatten, SpeckleType.Window);
var roofs = GetByType(flatten, SpeckleType.Roof);

Console.WriteLine("Checking for compliance");
var failedObjects = new List<ObjectToCheck>();
var failedWalls = CheckCompliance(walls, climateZone, SpeckleType.Wall);
var failedWindows = CheckCompliance(windows, climateZone, SpeckleType.Window);
var failedRoofs = CheckCompliance(roofs, climateZone, SpeckleType.Roof);

failedObjects.AddRange(failedWalls);
failedObjects.AddRange(failedWindows);
failedObjects.AddRange(failedRoofs);

Console.WriteLine("Reporting compliance for failed objects");
AttachReportToFailedObjects(automationContext, failedObjects);

Console.WriteLine("Reporting the status of all automation");
ReportStatus(
automationContext,
functionInputs,
walls.Count(),
failedWalls.Count(),
windows.Count(),
failedWindows.Count(),
roofs.Count(),
failedRoofs.Count()
);
}

private static void AttachReportToFailedObjects(
AutomationContext automationContext,
IEnumerable<ObjectToCheck> failedObjects
)
{
foreach (var failedObject in failedObjects)
{
var speckleTypeString = failedObject.SpeckleType.ToString();
automationContext.AttachResultToObjects(
ObjectResultLevel.Error,
speckleTypeString,
new[] { failedObject.Id },
$"{speckleTypeString[..^1]} expected to have maximum {failedObject.ExpectedUValue} U-value but it is {failedObject.UValue}."
);
}
}

private static void ReportStatus(
AutomationContext automationContext,
FunctionInputs functionInputs,
int numberOfWalls,
int numberOfFailedWalls,
int numberOfWindows,
int numberOfFailedWindows,
int numberOfRoofs,
int numberOfFailedRoofs
)
{
if (numberOfFailedWalls + numberOfFailedWindows + numberOfFailedRoofs > 0)
{
var message = "";
if (functionInputs.CheckWalls)
{
var wallCount = GetByType(flatten, SpeckleType.Wall).Count();
message += "WALLS:\n";
if (wallCount > 0)
if (numberOfWalls > 0)
{
message += $"{failedWallIds.Count}/{wallCount} wall(s) failed.\n";
message += $"{numberOfFailedWalls}/{numberOfWalls} wall(s) failed.\n";
}
else
{
Expand All @@ -51,25 +108,23 @@ FunctionInputs functionInputs
}
if (functionInputs.CheckWindows)
{
var windowCount = GetByType(flatten, SpeckleType.Window).Count();
message += "WINDOWS:\n";
if (windowCount > 0)
if (numberOfWindows > 0)
{
message += $"{failedWindowIds.Count}/{windowCount} window(s) failed.\n";
message += $"{numberOfFailedWindows}/{numberOfWindows} window(s) failed.\n";
}
else
{
message += "There are no windows\n\n";
}
}

if (functionInputs.CheckWindows)
{
var roofCount = GetByType(flatten, SpeckleType.Roof).Count();
message += "ROOFS:\n";
if (roofCount > 0)
if (numberOfRoofs > 0)
{
message += $"{failedRoofIds.Count}/{roofCount} roof(s) failed.\n";
message += $"{numberOfFailedRoofs}/{numberOfRoofs} roof(s) failed.\n";
}
else
{
Expand All @@ -80,132 +135,96 @@ FunctionInputs functionInputs
}
else
{
automationContext.MarkRunSuccess($"Your building is compliant with selected climate zone!");
automationContext.MarkRunSuccess(
$"Your building is compliant with selected climate zone!"
);
}
}

private static List<string> CheckWalls(AutomationContext automationContext, FunctionInputs functionInputs, IEnumerable<Base> flatten)
private static double GetExpectedValue(
ClimateZone climateZone,
SpeckleType speckleType
)
{
if (!functionInputs.CheckWalls)
{
return new List<string>();
}

var walls = GetByType(flatten, SpeckleType.Wall);
var uValues = walls.Select(GetThermalResistance);
// Attempt to parse ClimateZone as a ClimateZones enum
if (Enum.TryParse(functionInputs.ClimateZone, out ClimateZones climateZoneEnum))
switch (speckleType)
{
var expectedValue = UValues.Wall[climateZoneEnum];
var failedObjectIds = uValues.Where(val => val.value < expectedValue).Select(v => (v.id, v.value)).ToList();

foreach (var (id, value) in failedObjectIds)
{
automationContext.AttachResultToObjects(
ObjectResultLevel.Error,
"Walls",
new []{id},
$"Wall expected to have maximum {expectedValue} U-value but it is {value}."
);
}
return failedObjectIds.Select(i => i.id).ToList();
case SpeckleType.Wall:
return UValues.Wall[climateZone];
case SpeckleType.Window:
return UValues.Window[climateZone];
case SpeckleType.Roof:
return UValues.Roof[climateZone];
default:
return 0;
}

// Handle the case where the ClimateZone string is not a valid ClimateZones value
throw new ArgumentException($"Invalid ClimateZone: {functionInputs.ClimateZone}");
}

private static List<string> CheckWindows(AutomationContext automationContext, FunctionInputs functionInputs, IEnumerable<Base> flatten)
{
if (!functionInputs.CheckWindows)
{
return new List<string>();
}

var walls = GetByType(flatten, SpeckleType.Window);
var uValues = walls.Select(GetThermalResistance);
if (Enum.TryParse(functionInputs.ClimateZone, out ClimateZones climateZoneEnum))
{
var expectedValue = UValues.Window[climateZoneEnum];
var failedObjectIds = uValues.Where(val => val.value < expectedValue).Select(v => (v.id, v.value)).ToList();

foreach (var (id, value) in failedObjectIds)
{
automationContext.AttachResultToObjects(
ObjectResultLevel.Error,
"Windows",
new []{id},
$"Window expected to have maximum {expectedValue} U-value but it is {value}."
);
}
return failedObjectIds.Select(i => i.id).ToList();
}

// Handle the case where the ClimateZone string is not a valid ClimateZones value
throw new ArgumentException($"Invalid ClimateZone: {functionInputs.ClimateZone}");
private static IEnumerable<ObjectToCheck> CheckCompliance(
IEnumerable<Base> objects,
ClimateZone climateZone,
SpeckleType speckleType
)
{
var expectedValue = GetExpectedValue(climateZone, speckleType);
var objectsToCheck = objects.Select(o => new ObjectToCheck(
o,
expectedValue,
speckleType
));
return objectsToCheck.Where(obj => obj.UValue < expectedValue);
}
private static List<string> CheckRoofs(AutomationContext automationContext, FunctionInputs functionInputs, IEnumerable<Base> flatten)

private static ClimateZone GetClimateZone(string climateZoneString)
{
if (!functionInputs.CheckRoofs)
if (Enum.TryParse(climateZoneString, out ClimateZone climateZoneEnum))
{
return new List<string>();
return climateZoneEnum;
}

var walls = GetByType(flatten, SpeckleType.Roof);
var uValues = walls.Select(GetThermalResistance);
if (Enum.TryParse(functionInputs.ClimateZone, out ClimateZones climateZoneEnum))
{
var expectedValue = UValues.Roof[climateZoneEnum];
var failedObjectIds = uValues.Where(val => val.value < expectedValue).Select(v => (v.id, v.value)).ToList();

foreach (var (id, value) in failedObjectIds)
{
automationContext.AttachResultToObjects(
ObjectResultLevel.Error,
"Roofs",
new []{id},
$"Roof expected to have maximum {expectedValue} U-value but it is {value}."
);
}
return failedObjectIds.Select(i => i.id).ToList();
}

// Handle the case where the ClimateZone string is not a valid ClimateZones value
throw new ArgumentException($"Invalid ClimateZone: {functionInputs.ClimateZone}");
throw new ArgumentException($"Invalid ClimateZone: {climateZoneString}");
}

private static IEnumerable<Base> GetByType(IEnumerable<Base> objects, SpeckleType speckleType)
private static IEnumerable<Base> GetByType(
IEnumerable<Base> objects,
SpeckleType speckleType
)
{
return objects.Where(b => b.speckle_type == SpeckleTypes[speckleType] &&
(string)b["category"]! == SpeckleCategories[speckleType]);
return objects.Where(b =>
b.speckle_type == SpeckleTypes[speckleType]
&& (string)b["category"]! == SpeckleCategories[speckleType]
);
}

private static (string id, double value) GetThermalResistance(Base obj)
private static IEnumerable<Base> GetByType<T>(Base root)
where T : Base
{
var properties = obj["properties"] as Dictionary<string, object>;
if (properties is null)
{
return (obj.id, 0);
}
var typeParameters = properties!["Type Parameters"] as Dictionary<string, object>;
var analyticalProperties = typeParameters!["Analytical Properties"] as Dictionary<string, object>;
var u = analyticalProperties!["Heat Transfer Coefficient (U)"] as Dictionary<string, object>;
var value = u!["value"] is double ? (double)u!["value"] : 0;
return (obj.id, value);
var traversal = new GraphTraversal();
return traversal
.Traverse(root)
.Where(obj => obj.Current is T)
.Select(obj => obj.Current);
}

private static readonly Dictionary<SpeckleType, string> SpeckleTypes = new Dictionary<SpeckleType, string>()
{
{ SpeckleType.Wall, "Objects.BuiltElements.Wall:Objects.BuiltElements.Revit.RevitWall"},
{ SpeckleType.Window, "Objects.BuiltElements.Revit.RevitElement"},
{ SpeckleType.Roof, "Objects.BuiltElements.Roof:Objects.BuiltElements.Revit.RevitRoof.RevitRoof:Objects.BuiltElements.Revit.RevitRoof.RevitExtrusionRoof"},
};

private static readonly Dictionary<SpeckleType, string> SpeckleCategories = new Dictionary<SpeckleType, string>()
{
{ SpeckleType.Wall, "Walls"},
{ SpeckleType.Window, "Windows"},
{ SpeckleType.Roof, "Roofs"},
};

private static readonly Dictionary<SpeckleType, string> SpeckleTypes =
new()
{
{
SpeckleType.Wall,
"Objects.BuiltElements.Wall:Objects.BuiltElements.Revit.RevitWall"
},
{ SpeckleType.Window, "Objects.BuiltElements.Revit.RevitElement" },
{
SpeckleType.Roof,
"Objects.BuiltElements.Roof:Objects.BuiltElements.Revit.RevitRoof.RevitRoof:Objects.BuiltElements.Revit.RevitRoof.RevitExtrusionRoof"
},
};

private static readonly Dictionary<SpeckleType, string> SpeckleCategories =
new()
{
{ SpeckleType.Wall, "Walls" },
{ SpeckleType.Window, "Windows" },
{ SpeckleType.Roof, "Roofs" },
};
}
Loading

0 comments on commit a626339

Please sign in to comment.