From f8626ecdad4dc3e1c5eb421ce63e9ab5252ed991 Mon Sep 17 00:00:00 2001
From: jdomnitz <380352+jdomnitz@users.noreply.github.com>
Date: Wed, 1 Jan 2025 00:41:16 -0500
Subject: [PATCH] Implement cluster inheritance, map fields and handle poor
cluster definition consistency
---
Generator/ClusterGenerator.cs | 111 +-
Generator/Clusters/ColorControl.xml | 1028 +++++++++++++++
Generator/Clusters/Groups.xml | 197 +++
.../Label-Cluster-FixedLabelCluster.xml | 77 ++
.../Clusters/Label-Cluster-LabelCluster.xml | 88 ++
.../Label-Cluster-UserLabelCluster.xml | 78 ++
Generator/Clusters/LevelControl.xml | 319 +++++
...s-BridgedDeviceBasicInformationCluster.xml | 162 +++
Generator/GeneratorUtil.cs | 10 +
Generator/Schema/Cluster.cs | 39 +-
.../Application/ColorControlCluster.cs | 1150 +++++++++++++++++
.../Application/LevelControlCluster.cs | 453 +++++++
.../Clusters/Application/On-OffCluster.cs | 7 +
MatterDotNet/Clusters/ClusterBase.cs | 27 +
MatterDotNet/Clusters/UnknownCluster.cs | 2 +-
.../Clusters/Utility/AccessControlCluster.cs | 7 +
.../AdministratorCommissioningCluster.cs | 7 +
.../Utility/BasicInformationCluster.cs | 7 +
.../BridgedDeviceBasicInformationCluster.cs | 41 +
.../Clusters/Utility/DescriptorCluster.cs | 7 +
.../Clusters/Utility/FixedLabelCluster.cs | 51 +
.../Utility/GeneralCommissioningCluster.cs | 7 +
.../Utility/GeneralDiagnosticsCluster.cs | 7 +
.../Utility/GroupKeyManagementCluster.cs | 7 +
.../Clusters/Utility/GroupsCluster.cs | 275 ++++
.../Clusters/Utility/IdentifyCluster.cs | 7 +
MatterDotNet/Clusters/Utility/LabelCluster.cs | 63 +
.../NodeOperationalCredentialsCluster.cs | 10 +-
.../Clusters/Utility/UserLabelCluster.cs | 58 +
29 files changed, 4254 insertions(+), 48 deletions(-)
create mode 100644 Generator/Clusters/ColorControl.xml
create mode 100644 Generator/Clusters/Groups.xml
create mode 100644 Generator/Clusters/Label-Cluster-FixedLabelCluster.xml
create mode 100644 Generator/Clusters/Label-Cluster-LabelCluster.xml
create mode 100644 Generator/Clusters/Label-Cluster-UserLabelCluster.xml
create mode 100644 Generator/Clusters/LevelControl.xml
create mode 100644 Generator/Clusters/bridge-clusters-BridgedDeviceBasicInformationCluster.xml
create mode 100644 MatterDotNet/Clusters/Application/ColorControlCluster.cs
create mode 100644 MatterDotNet/Clusters/Application/LevelControlCluster.cs
create mode 100644 MatterDotNet/Clusters/Utility/BridgedDeviceBasicInformationCluster.cs
create mode 100644 MatterDotNet/Clusters/Utility/FixedLabelCluster.cs
create mode 100644 MatterDotNet/Clusters/Utility/GroupsCluster.cs
create mode 100644 MatterDotNet/Clusters/Utility/LabelCluster.cs
create mode 100644 MatterDotNet/Clusters/Utility/UserLabelCluster.cs
diff --git a/Generator/ClusterGenerator.cs b/Generator/ClusterGenerator.cs
index 33a2834..64a1a5e 100644
--- a/Generator/ClusterGenerator.cs
+++ b/Generator/ClusterGenerator.cs
@@ -36,7 +36,8 @@ public static void Generate()
Cluster? cluster = deserializer.Deserialize(File.OpenRead(clusterxml)) as Cluster;
if (cluster == null)
throw new IOException("Failed to parse cluster " + clusterxml);
- clusterBase.WriteLine($" case {cluster.name.Replace(" ", "").Replace('/', '_')}.CLUSTER_ID:\n return new {cluster.name.Replace(" ", "").Replace('/', '_')}(endPoint);");
+ if (!string.IsNullOrEmpty(cluster.clusterIds.clusterId.id))
+ clusterBase.WriteLine($" case {cluster.name.Replace(" ", "").Replace('/', '_')}.CLUSTER_ID:\n return new {cluster.name.Replace(" ", "").Replace('/', '_')}(endPoint);");
WriteClass(cluster);
}
}
@@ -71,24 +72,30 @@ private static void WriteClass(Cluster cluster)
private static void WriteClass(Cluster cluster, TextWriter writer)
{
includes.Add("MatterDotNet.Protocol.Parsers");
- includes.Add("MatterDotNet.Protocol.Payloads");
includes.Add("MatterDotNet.Protocol.Sessions");
writer.WriteLine();
writer.WriteLine($"namespace MatterDotNet.Clusters.{GeneratorUtil.SanitizeName(cluster.classification.role) ?? "Misc"}\n{{");
writer.WriteLine(" /// ");
writer.WriteLine($" /// {cluster.name}");
writer.WriteLine(" /// ");
- writer.WriteLine($" [ClusterRevision(CLUSTER_ID, {cluster.revision})]");
- writer.WriteLine(" public class " + cluster.name.Replace(" ", "").Replace('/', '_') + " : ClusterBase");
+ if (!string.IsNullOrEmpty(cluster.clusterIds.clusterId.id))
+ writer.WriteLine($" [ClusterRevision(CLUSTER_ID, {cluster.revision})]");
+ writer.WriteLine(" public class " + GeneratorUtil.SanitizeClassName(cluster.name) + (string.IsNullOrEmpty(cluster.classification.baseCluster) ? " : ClusterBase" : $" : {GeneratorUtil.SanitizeClassName(cluster.classification.baseCluster)}Cluster"));
writer.WriteLine(" {");
- writer.WriteLine(" internal const uint CLUSTER_ID = " + cluster.clusterIds.clusterId.id + ";");
+ if (!string.IsNullOrEmpty(cluster.clusterIds.clusterId.id))
+ writer.WriteLine(" internal const uint CLUSTER_ID = " + cluster.clusterIds.clusterId.id + ";");
writer.WriteLine();
writer.WriteLine(" /// ");
writer.WriteLine($" /// {cluster.name}");
writer.WriteLine(" /// ");
- writer.WriteLine(" public " + cluster.name.Replace(" ", "").Replace('/', '_') + "(ushort endPoint) : base(CLUSTER_ID, endPoint) { }");
+ if (string.IsNullOrEmpty(cluster.clusterIds.clusterId.id))
+ writer.WriteLine(" public " + GeneratorUtil.SanitizeClassName(cluster.name) + "(uint cluster, ushort endPoint) : base(cluster, endPoint) { }");
+ else
+ writer.WriteLine(" public " + GeneratorUtil.SanitizeClassName(cluster.name) + "(ushort endPoint) : base(CLUSTER_ID, endPoint) { }");
+ if (string.IsNullOrEmpty(cluster.classification.baseCluster) && !string.IsNullOrEmpty(cluster.clusterIds.clusterId.id))
+ writer.WriteLine($" /// \n protected {GeneratorUtil.SanitizeClassName(cluster.name)}(uint cluster, ushort endPoint) : base(cluster, endPoint) {{ }}");
writer.WriteLine();
- if (cluster.dataTypes.@enum != null || (cluster.features != null && cluster.features.Length > 0))
+ if (cluster.dataTypes?.@enum != null || (cluster.features != null && cluster.features.Length > 0))
{
writer.WriteLine(" #region Enums");
bool firstEnum=true;
@@ -97,7 +104,7 @@ private static void WriteClass(Cluster cluster, TextWriter writer)
firstEnum = false;
WriteFeatures(cluster.features, writer);
}
- if (cluster.dataTypes.@enum != null)
+ if (cluster.dataTypes?.@enum != null)
{
foreach (clusterDataTypesEnum enumType in cluster.dataTypes.@enum)
{
@@ -110,7 +117,7 @@ private static void WriteClass(Cluster cluster, TextWriter writer)
WriteEnum(enumType, writer);
}
}
- if (cluster.dataTypes.bitmap != null)
+ if (cluster.dataTypes?.bitmap != null)
{
foreach (clusterDataTypesBitfield bitmapType in cluster.dataTypes.bitmap)
{
@@ -126,9 +133,10 @@ private static void WriteClass(Cluster cluster, TextWriter writer)
writer.WriteLine(" #endregion Enums");
writer.WriteLine();
}
- if (cluster.dataTypes.@struct != null)
+ if (cluster.dataTypes?.@struct != null)
{
includes.Add("System.Diagnostics.CodeAnalysis");
+ includes.Add("MatterDotNet.Protocol.Payloads");
writer.WriteLine(" #region Records");
bool firstRecord = true;
foreach (clusterDataTypesStruct structType in cluster.dataTypes.@struct)
@@ -154,6 +162,7 @@ private static void WriteClass(Cluster cluster, TextWriter writer)
{
if (command.field == null)
continue;
+ includes.Add("MatterDotNet.Protocol.Payloads");
if (firstPayload)
firstPayload = false;
else
@@ -187,6 +196,7 @@ private static void WriteClass(Cluster cluster, TextWriter writer)
}
writer.WriteLine(" #endregion Attributes");
}
+ writer.WriteLine($"\n /// \n public override string ToString() {{\n return \"{cluster.name}\";\n }}");
writer.WriteLine(" }");
writer.Write("}");
writer.Flush();
@@ -221,7 +231,7 @@ private static void WriteStruct(clusterCommand command, bool toServer, TextWrite
if (field.type.EndsWith("Enum"))
writer.WriteLine(" = " + field.type + "." + field.@default + ";");
else
- writer.WriteLine(" = " + SanitizeDefault(field.@default, "") + ";");
+ writer.WriteLine(" = " + SanitizeDefault(field.@default, field.type) + ";");
}
else
writer.WriteLine();
@@ -234,8 +244,8 @@ private static void WriteStruct(clusterCommand command, bool toServer, TextWrite
{
if (field.type == null || field.disallowConform != null) //Reserved/removed fields
continue;
- int? from = null;
- int? to = null;
+ long? from = null;
+ long? to = null;
if (field.constraint != null)
{
switch (field.constraint.type)
@@ -243,19 +253,21 @@ private static void WriteStruct(clusterCommand command, bool toServer, TextWrite
case "min":
case "minCount":
case "minLength":
- if (int.TryParse(field.constraint.value, out int fromVal))
+ if (long.TryParse(field.constraint.value, out long fromVal))
from = fromVal;
break;
case "max":
case "maxCount":
case "maxLength":
- if (int.TryParse(field.constraint.value, out int toVal))
+ if (long.TryParse(field.constraint.value, out long toVal))
to = toVal;
break;
case "between":
case "lengthBetween":
- to = field.constraint.to;
- from = field.constraint.from;
+ if (long.TryParse(field.constraint.to, out long parsedTo))
+ to = parsedTo;
+ if (long.TryParse(field.constraint.from, out long parsedFrom))
+ from = parsedFrom;
break;
case "desc":
break;
@@ -271,7 +283,7 @@ private static void WriteStruct(clusterCommand command, bool toServer, TextWrite
writer.WriteLine(" }");
}
- private static void WriteStructType(bool optional, bool nullable, string type, string? entryType, int id, int? from, int? to, string name, Cluster cluster, TextWriter writer)
+ private static void WriteStructType(bool optional, bool nullable, string type, string? entryType, int id, long? from, long? to, string name, Cluster cluster, TextWriter writer)
{
string totalIndent = " ";
if (optional)
@@ -356,6 +368,7 @@ private static void WriteStructType(bool optional, bool nullable, string type, s
break;
case "uint8":
case "enum8":
+ case "map8":
case "tag":
case "namespace":
case "fabric-idx":
@@ -371,6 +384,7 @@ private static void WriteStructType(bool optional, bool nullable, string type, s
writer.WriteLine(");");
return;
case "uint16":
+ case "map16":
case "enum16":
case "group-id":
case "endpoint-no":
@@ -389,6 +403,7 @@ private static void WriteStructType(bool optional, bool nullable, string type, s
return;
case "uint24":
case "uint32":
+ case "map32":
case "epoch-s":
case "cluster-id":
case "attrib-id":
@@ -441,6 +456,7 @@ private static void WriteStructType(bool optional, bool nullable, string type, s
case "uint48":
case "uint56":
case "uint64":
+ case "map64":
case "epoch-us":
case "fabric-id":
case "node-id":
@@ -506,7 +522,7 @@ private static void WriteStructType(bool optional, bool nullable, string type, s
writer.WriteLine(");");
}
- private static void WriteFieldReader(bool optional, bool nullable, string type, string? entryType, string id, int? from, int? to, string name, string structName, Cluster cluster, TextWriter writer)
+ private static void WriteFieldReader(bool optional, bool nullable, string type, string? entryType, string id, long? from, long? to, string name, string structName, Cluster cluster, TextWriter writer)
{
string totalIndent = " ";
if (id != "-1" && type != "list" && id != "i")
@@ -558,6 +574,7 @@ private static void WriteFieldReader(bool optional, bool nullable, string type,
break;
case "uint8":
case "enum8":
+ case "map8":
case "tag":
case "namespace":
case "fabric-idx":
@@ -566,6 +583,7 @@ private static void WriteFieldReader(bool optional, bool nullable, string type,
break;
case "uint16":
case "enum16":
+ case "map16":
case "group-id":
case "endpoint-no":
case "vendor-id":
@@ -575,6 +593,7 @@ private static void WriteFieldReader(bool optional, bool nullable, string type,
break;
case "uint24":
case "uint32":
+ case "map32":
case "epoch-s":
case "cluster-id":
case "attrib-id":
@@ -589,6 +608,7 @@ private static void WriteFieldReader(bool optional, bool nullable, string type,
case "uint48":
case "uint56":
case "uint64":
+ case "map64":
case "epoch-us":
case "fabric-id":
case "node-id":
@@ -913,8 +933,15 @@ private static void WriteStruct(clusterDataTypesStruct structType, Cluster clust
writer.WriteLine($" internal {GeneratorUtil.SanitizeName(structType.name)}(object[] fields) {{");
writer.WriteLine(" FieldReader reader = new FieldReader(fields);");
foreach (clusterDataTypesStructField field in structType.field)
- WriteFieldReader(field.mandatoryConform == null, field.quality?.nullable == true, field.type, field.entry?.type, field.id.ToString(), field.constraint?.fromSpecified == true ? field.constraint.from : null, field.constraint?.toSpecified == true ? field.constraint.to : null, field.name, structType.name, cluster, writer);
-
+ {
+ long? to = null;
+ long? from = null;
+ if (field.constraint?.toSpecified == true && long.TryParse(field.constraint.to, out long toVal))
+ to = toVal;
+ if (field.constraint?.fromSpecified == true && long.TryParse(field.constraint.from, out long fromVal))
+ from = fromVal;
+ WriteFieldReader(field.mandatoryConform == null, field.quality?.nullable == true, field.type, field.entry?.type, field.id.ToString(), from, to, field.name, structType.name, cluster, writer);
+ }
writer.WriteLine(" }");
foreach (clusterDataTypesStructField field in structType.field)
{
@@ -938,7 +965,7 @@ private static void WriteStruct(clusterDataTypesStruct structType, Cluster clust
if (HasEnum(cluster, field.type) || HasBitmap(cluster, field.type))
writer.WriteLine(" = " + field.type + "." + field.@default + ";");
else
- writer.WriteLine(" = " + SanitizeDefault(field.@default!, "") + ";");
+ writer.WriteLine(" = " + SanitizeDefault(field.@default!, field.type) + ";");
}
else
writer.WriteLine();
@@ -947,8 +974,8 @@ private static void WriteStruct(clusterDataTypesStruct structType, Cluster clust
writer.WriteLine(" writer.StartStructure(structNumber);");
foreach (clusterDataTypesStructField field in structType.field)
{
- int? from = null;
- int? to = null;
+ long? from = null;
+ long? to = null;
if (field.constraint != null)
{
switch (field.constraint.type)
@@ -956,19 +983,21 @@ private static void WriteStruct(clusterDataTypesStruct structType, Cluster clust
case "min":
case "minCount":
case "minLength":
- if (int.TryParse(field.constraint.value, out int fromVal))
+ if (long.TryParse(field.constraint.value, out long fromVal))
from = fromVal;
break;
case "max":
case "maxCount":
case "maxLength":
- if (int.TryParse(field.constraint.value, out int toVal))
+ if (long.TryParse(field.constraint.value, out long toVal))
to = toVal;
break;
case "between":
case "lengthBetween":
- to = field.constraint.to;
- from = field.constraint.from;
+ if (long.TryParse(field.constraint.from, out long parsedFrom))
+ from = parsedFrom;
+ if (long.TryParse(field.constraint.to, out long parsedTo))
+ to = parsedTo;
break;
case "desc":
break;
@@ -1029,7 +1058,7 @@ private static void WriteFeatures(clusterFeature[] features, TextWriter writer)
foreach (clusterFeature item in features)
{
writer.WriteLine(" /// \n /// " + item.summary.Replace("[[ref_", "") + "\n /// ");
- writer.WriteLine(" " + item.name + " = " + (1 << item.bit) + ",");
+ writer.WriteLine(" " + GeneratorUtil.SanitizeName(item.name) + " = " + (1 << item.bit) + ",");
}
writer.WriteLine(" }");
}
@@ -1041,7 +1070,7 @@ private static void WriteAttribute(Cluster cluster, clusterAttribute attribute,
hasDefault = false;
if (hasDefault && HasBitmap(cluster, attribute.type) && !HasBitmapValue(cluster, attribute.type, attribute.@default!))
hasDefault = false;
- if (attribute.access.read)
+ if (attribute?.access?.read == true)
{
writer.WriteLine($" /// \n /// Get the {GeneratorUtil.FieldNameToComment(attribute.name)} attribute\n /// ");
writer.Write(" public async Task<");
@@ -1063,7 +1092,13 @@ private static void WriteAttribute(Cluster cluster, clusterAttribute attribute,
writer.WriteLine($" FieldReader reader = new FieldReader((IList