From 01bd02e094d470d57e92408055d78b34d21706b6 Mon Sep 17 00:00:00 2001 From: Curiosit Date: Sun, 10 Mar 2024 18:05:18 +0100 Subject: [PATCH] Added 5 basic components Added 5 basic components: GetEPDbyUUID; GetEPDs; ListEPDs; ParseEPD, ParseGWP --- PluginGrasshopper/Components/FindMaterial.cs | 105 ------------ PluginGrasshopper/Components/GetEPDbyUUID.cs | 81 +++++++++ PluginGrasshopper/Components/GetEPDs.cs | 80 +++++++++ PluginGrasshopper/Components/ListEPDs.cs | 72 ++++++++ PluginGrasshopper/Components/ParseEPD.cs | 164 +++++++++++++++++++ PluginGrasshopper/Components/ParseGWP.cs | 70 ++++++++ gh-examples/read-epd.gh | Bin 0 -> 14232 bytes 7 files changed, 467 insertions(+), 105 deletions(-) delete mode 100644 PluginGrasshopper/Components/FindMaterial.cs create mode 100644 PluginGrasshopper/Components/GetEPDbyUUID.cs create mode 100644 PluginGrasshopper/Components/GetEPDs.cs create mode 100644 PluginGrasshopper/Components/ListEPDs.cs create mode 100644 PluginGrasshopper/Components/ParseEPD.cs create mode 100644 PluginGrasshopper/Components/ParseGWP.cs create mode 100644 gh-examples/read-epd.gh diff --git a/PluginGrasshopper/Components/FindMaterial.cs b/PluginGrasshopper/Components/FindMaterial.cs deleted file mode 100644 index 356dc65..0000000 --- a/PluginGrasshopper/Components/FindMaterial.cs +++ /dev/null @@ -1,105 +0,0 @@ -using Grasshopper; -using Grasshopper.Kernel; -using System; -using System.IO; -using System.Net; -using System.Xml; -using Newtonsoft.Json; - -namespace PluginTemplate.PluginGrasshopper -{ - public class FindMaterial : GH_Component - { - public FindMaterial() - : base("Find Material by UUID", "FindMat", - "Loads material through API", - "goeko", "API") - { - } - - protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) - { - pManager.AddBooleanParameter("Run", "Run", "Set to true to execute loading process", GH_ParamAccess.item); - pManager.AddTextParameter("UUID", "UUID", "UUID of the material to retrieve", GH_ParamAccess.item); - pManager.AddIntegerParameter("Timeout", "Timeout", "Timeout in seconds (up to 20 seconds)", GH_ParamAccess.item, 5); - } - - protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) - { - pManager.AddTextParameter("Data", "Data", "Loaded data from the database", GH_ParamAccess.item); - } - - protected override void SolveInstance(IGH_DataAccess DA) - { - bool run = false; - string uuid = ""; - int timeout = 5; - - DA.GetData(0, ref run); - DA.GetData(1, ref uuid); // Get the UUID parameter - DA.GetData(2, ref timeout); // Get the Timeout parameter - - // Ensure the timeout value is within the range of 1 to 20 seconds - timeout = Math.Max(1, Math.Min(timeout, 20)); - - if (run && !string.IsNullOrWhiteSpace(uuid)) - { - string loadedData = LoadDataFromDatabase(uuid, timeout * 1000); // Convert timeout to milliseconds - DA.SetData(0, loadedData); - } - } - - private string LoadDataFromDatabase(string uuid, int timeoutMilliseconds) - { - string apiUrl = $"https://www.oekobaudat.de/OEKOBAU.DAT/resource/processes/{uuid}"; - - string responseData = ""; - - try - { - WebRequest request = WebRequest.Create(apiUrl); - request.Timeout = timeoutMilliseconds; // Set the timeout - request.Method = "GET"; - - using (WebResponse response = request.GetResponse()) - using (Stream stream = response.GetResponseStream()) - using (StreamReader reader = new StreamReader(stream)) - { - responseData = reader.ReadToEnd(); - } - - // Process XML data and convert to JSON - responseData = ProcessXmlData(responseData); - } - catch (WebException e) - { - responseData = e.Message; - } - - return responseData; - } - - private string ProcessXmlData(string xmlData) - { - // Load XML data into XmlDocument - XmlDocument xmlDoc = new XmlDocument(); - xmlDoc.LoadXml(xmlData); - - // Create a namespace manager - XmlNamespaceManager nsManager = new XmlNamespaceManager(xmlDoc.NameTable); - nsManager.AddNamespace("sapi", "http://www.ilcd-network.org/ILCD/ServiceAPI"); - - // Select the 'sapi:name' element using the namespace manager - XmlNode nameNode = xmlDoc.SelectSingleNode("//sapi:name", nsManager); - - // Get the value of the 'sapi:name' element - string nameValue = nameNode.InnerText; - - return nameValue; - } - - protected override System.Drawing.Bitmap Icon => ResourceLoader.LoadBitmap("PluginGrasshopper_24.png"); - - public override Guid ComponentGuid => new Guid("f9fa9c30-76c4-45a9-92ac-df104238634e"); - } -} diff --git a/PluginGrasshopper/Components/GetEPDbyUUID.cs b/PluginGrasshopper/Components/GetEPDbyUUID.cs new file mode 100644 index 0000000..1b4173d --- /dev/null +++ b/PluginGrasshopper/Components/GetEPDbyUUID.cs @@ -0,0 +1,81 @@ +using System; +using System.IO; +using System.Net; +using System.Xml; +using Grasshopper.Kernel; +using Newtonsoft.Json; + +namespace PluginTemplate.PluginGrasshopper +{ + public class GetEPDbyUUID : GH_Component + { + private const string OKOBAU_URL = "https://oekobaudat.de/OEKOBAU.DAT/resource/datastocks/cd2bda71-760b-4fcc-8a0b-3877c10000a8"; + + public GetEPDbyUUID() + : base("Get EPD by UUID", "GetEPDbyUUID", + "Get EPD by UUID from Ökobau", + "goeko", "API") + { + } + + protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) + { + pManager.AddBooleanParameter("Run", "Run", "Set to true to execute loading process", GH_ParamAccess.item, false); + pManager.AddTextParameter("UUID", "UUID", "UUID of the EPD to retrieve", GH_ParamAccess.item); + } + + protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) + { + pManager.AddTextParameter("Data", "Data", "Loaded EPD data from Ökobau", GH_ParamAccess.item); + } + + protected override void SolveInstance(IGH_DataAccess DA) + { + bool run = false; + string uuid = ""; + + DA.GetData(0, ref run); + DA.GetData(1, ref uuid); + + if (run && !string.IsNullOrWhiteSpace(uuid)) + { + string epdData = GetFullEPDData(uuid); + DA.SetData(0, epdData); + } + else + { + DA.SetData(0, "Component not executed. Set 'Run' to true and provide a valid UUID to retrieve EPD data."); + } + } + + private string GetFullEPDData(string uuid) + { + string responseData = ""; + + try + { + string apiUrl = $"{OKOBAU_URL}/processes/{uuid}?format=json&view=extended"; + WebRequest request = WebRequest.Create(apiUrl); + request.Method = "GET"; + + using (WebResponse response = request.GetResponse()) + using (Stream stream = response.GetResponseStream()) + using (StreamReader reader = new StreamReader(stream)) + { + responseData = reader.ReadToEnd(); + } + } + catch (WebException e) + { + responseData = e.Message; + } + + return responseData; + } + + protected override System.Drawing.Bitmap Icon => ResourceLoader.LoadBitmap("PluginGrasshopper_24.png"); + + public override Guid ComponentGuid => new Guid("f9fa9c30-76c4-45a9-92ab-df114238634e"); + } +} + diff --git a/PluginGrasshopper/Components/GetEPDs.cs b/PluginGrasshopper/Components/GetEPDs.cs new file mode 100644 index 0000000..768134c --- /dev/null +++ b/PluginGrasshopper/Components/GetEPDs.cs @@ -0,0 +1,80 @@ +using System; +using System.IO; +using System.Net; +using System.Xml; +using Grasshopper.Kernel; +using Newtonsoft.Json; + +namespace PluginTemplate.PluginGrasshopper +{ + public class GetEPDs : GH_Component + { + private const string OKOBAU_URL = "https://oekobaudat.de/OEKOBAU.DAT/resource/datastocks/cd2bda71-760b-4fcc-8a0b-3877c10000a8"; + + public GetEPDs() + : base("Get EPDs from Ökobau", "GetEPDs", + "Get EPDs from Ökobau", + "goeko", "API") + { + } + + protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) + { + pManager.AddBooleanParameter("Run", "Run", "Set to true to execute loading process", GH_ParamAccess.item, false); + pManager.AddIntegerParameter("Limit", "Limit", "Limit the number of EPDs to retrieve", GH_ParamAccess.item, 10); + } + + protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) + { + pManager.AddTextParameter("Data", "Data", "Loaded EPD data from Ökobau", GH_ParamAccess.item); + } + + protected override void SolveInstance(IGH_DataAccess DA) + { + bool run = false; + int limit = 10; + + DA.GetData(0, ref run); + DA.GetData(1, ref limit); + + if (run) + { + string epdData = GetEPDData(limit); + DA.SetData(0, epdData); + } + else + { + DA.SetData(0, "Component not executed. Set 'Run' to true to retrieve EPD data."); + } + } + + private string GetEPDData(int limit) + { + string responseData = ""; + + try + { + string apiUrl = $"{OKOBAU_URL}/processes?format=json&pageSize={limit}"; + WebRequest request = WebRequest.Create(apiUrl); + request.Method = "GET"; + + using (WebResponse response = request.GetResponse()) + using (Stream stream = response.GetResponseStream()) + using (StreamReader reader = new StreamReader(stream)) + { + responseData = reader.ReadToEnd(); + } + } + catch (WebException e) + { + responseData = e.Message; + } + + return responseData; + } + + protected override System.Drawing.Bitmap Icon => ResourceLoader.LoadBitmap("PluginGrasshopper_24.png"); + + public override Guid ComponentGuid => new Guid("32c2713d-749c-4903-9c9e-d2ef70ad38fc"); + } +} diff --git a/PluginGrasshopper/Components/ListEPDs.cs b/PluginGrasshopper/Components/ListEPDs.cs new file mode 100644 index 0000000..2495ec6 --- /dev/null +++ b/PluginGrasshopper/Components/ListEPDs.cs @@ -0,0 +1,72 @@ +using Grasshopper.Kernel; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PluginTemplate.PluginGrasshopper +{ + public class ListEPDs : GH_Component + { + public ListEPDs() + : base("List EPDs", "ListEPDs", + "List EPDs names and UUIDs from JSON data", + "goeko", "Read") + { + } + + protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) + { + pManager.AddTextParameter("JSONData", "JSONData", "JSON data to parse", GH_ParamAccess.item); + } + + protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) + { + pManager.AddTextParameter("Names", "Names", "Names extracted from JSON data", GH_ParamAccess.list); + pManager.AddTextParameter("UUIDs", "UUIDs", "UUIDs extracted from JSON data", GH_ParamAccess.list); + } + + protected override void SolveInstance(IGH_DataAccess DA) + { + string jsonData = ""; + + DA.GetData(0, ref jsonData); + + if (!string.IsNullOrWhiteSpace(jsonData)) + { + try + { + var names = new List(); + var uuids = new List(); + + JObject jsonObject = JObject.Parse(jsonData); + JArray data = (JArray)jsonObject["data"]; + + foreach (JToken item in data) + { + string name = item["name"].ToString(); + string uuid = item["uuid"].ToString(); + + names.Add(name); + uuids.Add(uuid); + } + + DA.SetDataList(0, names); + DA.SetDataList(1, uuids); + } + catch (Exception ex) + { + AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Error parsing JSON data: {ex.Message}"); + } + } + else + { + AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "JSON data is empty."); + } + } + + protected override System.Drawing.Bitmap Icon => ResourceLoader.LoadBitmap("PluginGrasshopper_24.png"); + + public override Guid ComponentGuid => new Guid("9874febf-56d9-40f6-8aa0-7b9efec4c5f2"); + } +} diff --git a/PluginGrasshopper/Components/ParseEPD.cs b/PluginGrasshopper/Components/ParseEPD.cs new file mode 100644 index 0000000..adb4309 --- /dev/null +++ b/PluginGrasshopper/Components/ParseEPD.cs @@ -0,0 +1,164 @@ +using Grasshopper.Kernel; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PluginTemplate.PluginGrasshopper +{ + public class ParseEPD : GH_Component + { + public ParseEPD() + : base("Parse EPD", "ParseEPD", + "Parse EPD result and extract information", + "goeko", "Read") + { + } + + protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) + { + pManager.AddTextParameter("EPDResult", "EPDResult", "EPD result to parse", GH_ParamAccess.item); + } + + protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) + { + pManager.AddTextParameter("Name", "Name", "Name parsed from EPD result", GH_ParamAccess.item); + pManager.AddTextParameter("Unit", "Unit", "Unit parsed from EPD result", GH_ParamAccess.item); + pManager.AddNumberParameter("Amount", "Amount", "Amount parsed from EPD result", GH_ParamAccess.item); + pManager.AddTextParameter("GWPData", "GWPData", "GWP data parsed from EPD result", GH_ParamAccess.item); + } + + protected override void SolveInstance(IGH_DataAccess DA) + { + string epdResult = ""; + + DA.GetData(0, ref epdResult); + + if (!string.IsNullOrWhiteSpace(epdResult)) + { + try + { + string name = ""; + string unit = ""; + double amount = 0; + JObject epdObject = JObject.Parse(epdResult); + + // Extract English name + JToken nameToken = epdObject.SelectToken("processInformation.dataSetInformation.name.baseName") + .FirstOrDefault(x => x["lang"].ToString() == "en")?["value"]; + if (nameToken != null) + name = nameToken.ToString(); + + // Extract unit and amount + JToken flowPropertiesToken = epdObject.SelectToken("exchanges.exchange[0].flowProperties[0]"); + if (flowPropertiesToken != null) + { + unit = flowPropertiesToken["referenceUnit"]?.ToString(); + if (double.TryParse(flowPropertiesToken["meanValue"]?.ToString(), out double parsedAmount)) + { + amount = parsedAmount; + } + else + { + throw new Exception("Failed to parse meanValue to double."); + } + } + + // Extract GWP data + string gwpData = ExtractGWP(epdObject); + + DA.SetData(0, name); + DA.SetData(1, unit); + DA.SetData(2, amount); + DA.SetData(3, gwpData); + } + catch (Exception ex) + { + DA.SetData(0, "Error parsing EPD result: " + ex.Message); + DA.SetData(1, ""); + DA.SetData(2, 0); + DA.SetData(3, ""); + } + } + else + { + DA.SetData(0, "EPD result is empty."); + DA.SetData(1, ""); + DA.SetData(2, 0); + DA.SetData(3, ""); + } + } + + private string ExtractGWP(JObject epdObject) + { + var gwpData = new Dictionary(); + + try + { + var gwpResults = epdObject.SelectToken("LCIAResults.LCIAResult[0].other.anies"); + if (gwpResults != null) + { + foreach (var result in gwpResults) + { + var module = result["module"]?.ToString(); + var value = result["value"]?.ToString(); + + if (!string.IsNullOrEmpty(module) && !string.IsNullOrEmpty(value)) + { + // Parse value to double + double parsedValue; + if (double.TryParse(value, out parsedValue)) + { + // If the module exists in the dictionary, add the value + if (gwpData.ContainsKey(module)) + { + gwpData[module] += parsedValue; + } + else + { + gwpData.Add(module, parsedValue); + } + } + } + } + } + } + catch (Exception e) + { + throw new Exception("Error extracting GWP data: " + e.Message); + } + + // If A1-A3 is not present, calculate and add it + if (!gwpData.ContainsKey("A1-A3")) + { + double sumA1A3 = (gwpData.ContainsKey("A1") ? gwpData["A1"] : 0) + + (gwpData.ContainsKey("A2") ? gwpData["A2"] : 0) + + (gwpData.ContainsKey("A3") ? gwpData["A3"] : 0); + gwpData.Add("A1-A3", sumA1A3); + } + + // Remove individual A1, A2, A3 entries + foreach (var module in new List { "A1", "A2", "A3" }) + { + gwpData.Remove(module); + } + + // Construct the output string + List formattedResults = new List(); + foreach (var kv in gwpData) + { + formattedResults.Add($"{kv.Key}: {kv.Value}"); + } + + return string.Join(", ", formattedResults); + } + + + + + + protected override System.Drawing.Bitmap Icon => ResourceLoader.LoadBitmap("PluginGrasshopper_24.png"); + + public override Guid ComponentGuid => new Guid("98a9c3b5-9911-4a7f-85ec-d56c1cfe62f8"); + } +} diff --git a/PluginGrasshopper/Components/ParseGWP.cs b/PluginGrasshopper/Components/ParseGWP.cs new file mode 100644 index 0000000..79fe79a --- /dev/null +++ b/PluginGrasshopper/Components/ParseGWP.cs @@ -0,0 +1,70 @@ +using Grasshopper.Kernel; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace PluginTemplate.PluginGrasshopper +{ + public class ParseGWP : GH_Component + { + public ParseGWP() + : base("Parse GWP Data", "ParseGWP", + "Parse GWP data from EPD result", + "goeko", "Read") + { + } + + protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) + { + pManager.AddTextParameter("EPDResult", "EPDResult", "EPD result to parse", GH_ParamAccess.item); + } + + protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) + { + pManager.AddNumberParameter("A1-A3", "A1-A3", "GWP data for A1-A3", GH_ParamAccess.item); + pManager.AddNumberParameter("A4", "A4", "GWP data for A4", GH_ParamAccess.item); + pManager.AddNumberParameter("A5", "A5", "GWP data for A5", GH_ParamAccess.item); + pManager.AddNumberParameter("B1", "B1", "GWP data for B1", GH_ParamAccess.item); + pManager.AddNumberParameter("C1", "C1", "GWP data for C1", GH_ParamAccess.item); + pManager.AddNumberParameter("C2", "C2", "GWP data for C2", GH_ParamAccess.item); + pManager.AddNumberParameter("C3", "C3", "GWP data for C3", GH_ParamAccess.item); + pManager.AddNumberParameter("C4", "C4", "GWP data for C4", GH_ParamAccess.item); + pManager.AddNumberParameter("D", "D", "GWP data for D", GH_ParamAccess.item); + } + + protected override void SolveInstance(IGH_DataAccess DA) + { + string epdResult = ""; + + DA.GetData(0, ref epdResult); + + if (!string.IsNullOrWhiteSpace(epdResult)) + { + try + { + string[] results = epdResult.Split(','); + + foreach (string result in results) + { + string[] parts = result.Split(':'); + string category = parts[0].Trim(); + double value = double.Parse(parts[1].Trim()); + DA.SetData(category, value); + } + } + catch (Exception ex) + { + AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Error parsing GWP data: {ex.Message}"); + } + } + else + { + AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "EPD result is empty."); + } + } + + protected override System.Drawing.Bitmap Icon => ResourceLoader.LoadBitmap("PluginGrasshopper_24.png"); + + public override Guid ComponentGuid => new Guid("bf9e2c05-3179-4c38-a145-4b3dfe0c0f79"); + } +} diff --git a/gh-examples/read-epd.gh b/gh-examples/read-epd.gh new file mode 100644 index 0000000000000000000000000000000000000000..ef6778f9a21f331b026535fd8eebcd64c45ed684 GIT binary patch literal 14232 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 literal 0 HcmV?d00001