Skip to content
Open
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
185 changes: 130 additions & 55 deletions dm/devicetype.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/project-chip/alchemy/asciidoc"
"github.com/project-chip/alchemy/internal"
"github.com/project-chip/alchemy/matter"
"github.com/project-chip/alchemy/matter/conformance"
"github.com/project-chip/alchemy/matter/types"
)

Expand Down Expand Up @@ -120,20 +121,41 @@ func renderDeviceType(deviceType *matter.DeviceType) (output string, err error)
if err != nil {
return
}
err = renderElementRequirements(deviceType, cr, clx)
err = renderElementRequirements(deviceType, cr, deviceType.ElementRequirements, clx)
if err != nil {
return
}

}
}

type deviceTypeInstance struct {
DeviceTypeName string
Label string
}

type deviceTypeRequirements struct {
DeviceType *matter.DeviceType
deviceRequirements []*matter.DeviceTypeRequirement
clusterRequirements []*matter.DeviceTypeClusterRequirement
elementRequirements map[*matter.Cluster][]*matter.ElementRequirement
elementRequirements map[string][]*matter.ElementRequirement
}

dtrs := make(map[*matter.DeviceType]*deviceTypeRequirements)
dtrs := make(map[deviceTypeInstance]*deviceTypeRequirements)

for _, dr := range deviceType.DeviceTypeRequirements {
if dr.DeviceType == nil {
continue
}

key := deviceTypeInstance{DeviceTypeName: dr.DeviceType.Name, Label: ""}
dtr, ok := dtrs[key]
if !ok {
dtr = &deviceTypeRequirements{DeviceType: dr.DeviceType}
dtrs[key] = dtr
}
dtr.deviceRequirements = append(dtr.deviceRequirements, dr)
}

for _, cr := range deviceType.ComposedDeviceTypeClusterRequirements {
if cr.DeviceType == nil {
Expand All @@ -143,10 +165,15 @@ func renderDeviceType(deviceType *matter.DeviceType) (output string, err error)
continue
}

dtr, ok := dtrs[cr.DeviceType]
label := cr.InstanceLabel
if label == cr.DeviceType.Name {
label = ""
}
key := deviceTypeInstance{DeviceTypeName: cr.DeviceType.Name, Label: label}
dtr, ok := dtrs[key]
if !ok {
dtr = &deviceTypeRequirements{}
dtrs[cr.DeviceType] = dtr
dtr = &deviceTypeRequirements{DeviceType: cr.DeviceType}
dtrs[key] = dtr
}
dtr.clusterRequirements = append(dtr.clusterRequirements, cr)
}
Expand All @@ -158,36 +185,110 @@ func renderDeviceType(deviceType *matter.DeviceType) (output string, err error)
if er.ElementRequirement.Cluster == nil {
continue
}
dtr, ok := dtrs[er.DeviceType]
label := er.InstanceLabel
if label == er.DeviceType.Name {
label = ""
}
key := deviceTypeInstance{DeviceTypeName: er.DeviceType.Name, Label: label}
dtr, ok := dtrs[key]
if !ok {
continue
dtr = &deviceTypeRequirements{DeviceType: er.DeviceType}
dtrs[key] = dtr
}

foundCluster := false
for _, cr := range dtr.clusterRequirements {
if cr.ClusterRequirement.ClusterID.Equals(er.ElementRequirement.ClusterID) {
foundCluster = true
break
}
}
if !foundCluster {
cr := matter.NewClusterRequirement(er.DeviceType, er.Source())
cr.ClusterID = er.ElementRequirement.ClusterID
cr.ClusterName = er.ElementRequirement.ClusterName
cr.Interface = matter.InterfaceServer // Default to server


dtcr := matter.NewDeviceTypeClusterRequirement(er.DeviceType, cr, er.Source())
dtcr.DeviceTypeID = er.DeviceTypeID
dtcr.DeviceTypeName = er.DeviceTypeName
dtcr.InstanceLabel = er.InstanceLabel

dtr.clusterRequirements = append(dtr.clusterRequirements, dtcr)
}

if dtr.elementRequirements == nil {
dtr.elementRequirements = make(map[*matter.Cluster][]*matter.ElementRequirement)
dtr.elementRequirements = make(map[string][]*matter.ElementRequirement)
}
dtr.elementRequirements[er.ElementRequirement.Cluster] = append(dtr.elementRequirements[er.ElementRequirement.Cluster], er.ElementRequirement)
cidStr := er.ElementRequirement.ClusterID.HexString()
dtr.elementRequirements[cidStr] = append(dtr.elementRequirements[cidStr], er.ElementRequirement)

}

if len(dtrs) > 0 {
cx := c.CreateElement("composedDeviceTypes")
deviceTypes := make([]*matter.DeviceType, 0, len(dtrs))
for _, dtr := range dtrs {
deviceTypes = append(deviceTypes, dtr.clusterRequirements[0].DeviceType)
instances := make([]deviceTypeInstance, 0, len(dtrs))
for key := range dtrs {
instances = append(instances, key)
}
slices.SortStableFunc(deviceTypes, func(a, b *matter.DeviceType) int {
return strings.Compare(a.Name, b.Name)
})
for _, dt := range deviceTypes {
dtr, ok := dtrs[dt]
if !ok {
continue
slices.SortStableFunc(instances, func(a, b deviceTypeInstance) int {
cmp := strings.Compare(a.DeviceTypeName, b.DeviceTypeName)
if cmp != 0 {
return cmp
}
return strings.Compare(a.Label, b.Label)
})
for _, inst := range instances {
dtr := dtrs[inst]
dt := dtr.DeviceType
dte := cx.CreateElement("deviceType")
if dt.ID.Valid() {
dte.CreateAttr("deviceTypeId", dt.ID.HexString())
}
dte.CreateAttr("deviceTypeName", dt.Name)

location := matter.DeviceTypeRequirementLocationUnknown
if len(dtr.deviceRequirements) > 0 {
location = dtr.deviceRequirements[0].Location
} else {
// Fallback to base requirement for the same device type
baseKey := deviceTypeInstance{DeviceTypeName: dt.Name, Label: ""}
if baseDtr, ok := dtrs[baseKey]; ok && len(baseDtr.deviceRequirements) > 0 {
location = baseDtr.deviceRequirements[0].Location
}
}
if location != matter.DeviceTypeRequirementLocationUnknown {
dte.CreateAttr("deviceTypeLocation", location.String())
}

var baseConformance conformance.Set
for _, dr := range deviceType.DeviceTypeRequirements {
if dr.DeviceType == dt {
baseConformance = dr.Conformance
break
}
}

if len(dtr.deviceRequirements) > 0 {
err = renderConformanceElement(dtr.deviceRequirements[0].Conformance, dte, nil)
if err != nil {
return
}
err = renderConstraintElement(dtr.deviceRequirements[0].Constraint, nil, dte, nil)
if err != nil {
return
}
} else if inst.Label != "" {
if baseConformance != nil {
err = renderConformanceElement(baseConformance, dte, nil)
if err != nil {
return
}
}

}
Comment thread
AryaHassanli marked this conversation as resolved.

if len(dtr.clusterRequirements) > 0 {
crx := dte.CreateElement("clusterRequirements")
reqs := make([]*matter.DeviceTypeClusterRequirement, len(dtr.clusterRequirements))
Expand All @@ -203,46 +304,20 @@ func renderDeviceType(deviceType *matter.DeviceType) (output string, err error)
clx := crx.CreateElement("cluster")
clx.CreateAttr("id", cr.ClusterRequirement.ClusterID.HexString())
clx.CreateAttr("name", cr.ClusterRequirement.ClusterName)
err = renderConformanceElement(cr.ClusterRequirement.Conformance, clx, nil)
if err != nil {
return
if len(cr.ClusterRequirement.Conformance) > 0 {
err = renderConformanceElement(cr.ClusterRequirement.Conformance, clx, nil)
if err != nil {
return
}
}
renderQuality(clx, cr.ClusterRequirement.Quality)
renderElementRequirements(dt, cr.ClusterRequirement, clx)
renderElementRequirements(dt, cr.ClusterRequirement, dtr.elementRequirements[cr.ClusterRequirement.ClusterID.HexString()], clx)
}
}
}

}

if len(deviceType.ComposedDeviceTypeClusterRequirements) > 0 {
cx := c.CreateElement("composedDeviceTypes")
reqs := make([]*matter.ClusterRequirement, len(deviceType.ClusterRequirements))
copy(reqs, deviceType.ClusterRequirements)
slices.SortStableFunc(reqs, sortClusterRequirements)
for _, cr := range reqs {
clx := cx.CreateElement("cluster")
clx.CreateAttr("id", cr.ClusterID.HexString())
clx.CreateAttr("name", cr.ClusterName)
switch cr.Interface {
case matter.InterfaceClient:
clx.CreateAttr("side", "client")
case matter.InterfaceServer:
clx.CreateAttr("side", "server")
}
renderQuality(clx, cr.Quality)
err = renderConformanceElement(cr.Conformance, clx, nil)
if err != nil {
return
}
err = renderElementRequirements(deviceType, cr, clx)
if err != nil {
return
}

}
}

x.Indent(2)

var b bytes.Buffer
Expand All @@ -265,9 +340,9 @@ type commandRequirement struct {
fields []*matter.ElementRequirement
}

func renderElementRequirements(deviceType *matter.DeviceType, cr *matter.ClusterRequirement, clx *etree.Element) (err error) {
func renderElementRequirements(deviceType *matter.DeviceType, cr *matter.ClusterRequirement, ers []*matter.ElementRequirement, clx *etree.Element) (err error) {
erMap := make(map[types.EntityType][]*matter.ElementRequirement)
for _, er := range deviceType.ElementRequirements {
for _, er := range ers {
if er.ClusterID.Equals(cr.ClusterID) {
erMap[er.Element] = append(erMap[er.Element], er)
}
Expand All @@ -276,7 +351,7 @@ func renderElementRequirements(deviceType *matter.DeviceType, cr *matter.Cluster
var attributeRequirements []*matter.ElementRequirement
var commandRequirements []*commandRequirement
var eventRequirements []*matter.ElementRequirement
for _, er := range deviceType.ElementRequirements {
for _, er := range ers {
if er.ClusterID.Equals(cr.ClusterID) {
switch er.Element {
case types.EntityTypeFeature:
Expand Down
4 changes: 4 additions & 0 deletions matter/composition.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type DeviceTypeClusterRequirement struct {

ClusterRequirement *ClusterRequirement
Origin RequirementOrigin
InstanceLabel string

DeviceType *DeviceType
DeviceTypeRequirement *DeviceTypeRequirement
Expand All @@ -78,6 +79,7 @@ func (dtcr *DeviceTypeClusterRequirement) Clone() *DeviceTypeClusterRequirement
DeviceTypeName: dtcr.DeviceTypeName,
ClusterRequirement: dtcr.ClusterRequirement,
Origin: dtcr.Origin,
InstanceLabel: dtcr.InstanceLabel,
DeviceType: dtcr.DeviceType,
DeviceTypeRequirement: dtcr.DeviceTypeRequirement,
}
Expand All @@ -90,6 +92,7 @@ type DeviceTypeElementRequirement struct {

ElementRequirement *ElementRequirement
Origin RequirementOrigin
InstanceLabel string

DeviceType *DeviceType
DeviceTypeRequirement *DeviceTypeRequirement
Expand All @@ -105,6 +108,7 @@ func (dter *DeviceTypeElementRequirement) Clone() *DeviceTypeElementRequirement
DeviceTypeName: dter.DeviceTypeName,
ElementRequirement: dter.ElementRequirement,
Origin: dter.Origin,
InstanceLabel: dter.InstanceLabel,
DeviceType: dter.DeviceType,
DeviceTypeRequirement: dter.DeviceTypeRequirement,
}
Expand Down
2 changes: 2 additions & 0 deletions matter/devicetype.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ const (
DeviceTypeRequirementLocationChildEndpoint
DeviceTypeRequirementLocationRootEndpoint
DeviceTypeRequirementLocationDescendantEndpoint
DeviceTypeRequirementLocationAnywhere
)

var (
Expand All @@ -237,6 +238,7 @@ var (
DeviceTypeRequirementLocationChildEndpoint: "childEndpoint",
DeviceTypeRequirementLocationRootEndpoint: "rootEndpoint",
DeviceTypeRequirementLocationDescendantEndpoint: "descendantEndpoint",
DeviceTypeRequirementLocationAnywhere: "anyEndpoint",
}
)

Expand Down
7 changes: 5 additions & 2 deletions matter/spec/requirements.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,15 @@ func (library *Library) toComposedDeviceTypeClusterRequirements(reader asciidoc.
}
return
}
for row := range ti.ContentRows() {
for row, category := range ti.CategorizedRows(reader) {
var cr *matter.ClusterRequirement
cr, err = library.toClusterRequirement(reader, deviceType, ti, row)
if err != nil {
return
}

dtcr := matter.NewDeviceTypeClusterRequirement(deviceType, cr, row)
dtcr.InstanceLabel = category
dtcr.DeviceTypeID, err = ti.ReadID(reader, row, matter.TableColumnDeviceID)
if err != nil {
return
Expand Down Expand Up @@ -230,13 +231,14 @@ func (library *Library) toComposedDeviceTypeElementRequirements(reader asciidoc.
}
return
}
for row := range ti.ContentRows() {
for row, category := range ti.CategorizedRows(reader) {
var er matter.ElementRequirement
er, err = library.toElementRequirement(reader, d, ti, row, deviceType)
if err != nil {
return
}
dter := matter.NewDeviceTypeElementRequirement(deviceType, &er, row)
dter.InstanceLabel = category
dter.DeviceTypeID, err = ti.ReadID(reader, row, matter.TableColumnDeviceID)
if err != nil {
return
Expand All @@ -259,6 +261,7 @@ func (library *Library) toComposedDeviceTypeElementRequirements(reader asciidoc.
dtcr := matter.NewDeviceTypeClusterRequirement(deviceType, cr, row)
dtcr.DeviceTypeID = dter.DeviceTypeID
dtcr.DeviceTypeName = dter.DeviceTypeName
dtcr.InstanceLabel = category
if len(dter.ElementRequirement.Conformance) > 0 {
dtcr.ClusterRequirement.Conformance = dter.ElementRequirement.Conformance.CloneSet()
}
Expand Down
Loading
Loading