Skip to content

Commit 0fedcd1

Browse files
committed
Introduce --controller-clusters for zap regen that generates controller-clusters.matter based on the spec
1 parent 9b51142 commit 0fedcd1

2 files changed

Lines changed: 93 additions & 12 deletions

File tree

cmd/cli/zap.go

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package cli
22

33
import (
4+
"fmt"
5+
"path/filepath"
6+
"slices"
7+
48
"github.com/project-chip/alchemy/cmd/common"
59
"github.com/project-chip/alchemy/internal/files"
610
"github.com/project-chip/alchemy/internal/pipeline"
@@ -220,10 +224,10 @@ type ZAPRegen struct {
220224
spec.FilterOptions `embed:""`
221225
sdk.SDKOptions `embed:""`
222226
render.TemplateOptions `embed:""`
227+
ControllerClusters bool `name:"controller-clusters" help:"Generate controller-clusters.matter from all spec clusters"`
223228
}
224229

225230
func (z *ZAPRegen) Run(cc *Context) (err error) {
226-
zapTargeter := regen.Targeter(z.SdkRoot)
227231

228232
var specification *spec.Specification
229233
specification, _, err = spec.Parse(cc, z.ParserOptions, z.ProcessingOptions, []spec.BuilderOption{spec.PatchForSdk(true)}, z.ASCIIDocAttributes.ToList())
@@ -236,6 +240,12 @@ func (z *ZAPRegen) Run(cc *Context) (err error) {
236240
return
237241
}
238242

243+
if z.ControllerClusters {
244+
return z.runControllerClusters(cc, specification)
245+
}
246+
247+
zapTargeter := regen.Targeter(z.SdkRoot)
248+
239249
var zapPaths pipeline.Paths
240250
zapPaths, err = pipeline.Start(cc, zapTargeter)
241251
if err != nil {
@@ -272,3 +282,66 @@ func (z *ZAPRegen) Run(cc *Context) (err error) {
272282

273283
return
274284
}
285+
286+
func (z *ZAPRegen) runControllerClusters(cc *Context, specification *spec.Specification) error {
287+
var clusterRefs []zap.ClusterRef
288+
for code, cluster := range specification.ClustersByID {
289+
clusterRefs = append(clusterRefs, zap.ClusterRef{
290+
Code: int(code),
291+
Name: cluster.Name,
292+
Side: "server",
293+
})
294+
}
295+
296+
slices.SortFunc(clusterRefs, func(a, b zap.ClusterRef) int {
297+
return a.Code - b.Code
298+
})
299+
300+
var validDeviceTypeCode uint64
301+
for id := range specification.DeviceTypesByID {
302+
validDeviceTypeCode = id
303+
break
304+
}
305+
306+
syntheticFile := &zap.File{
307+
EndpointTypes: []zap.EndpointType{
308+
{
309+
ID: 0,
310+
Name: "Synthetic Endpoint",
311+
DeviceTypeCode: int(validDeviceTypeCode),
312+
Clusters: clusterRefs,
313+
},
314+
},
315+
Endpoints: []zap.Endpoint{
316+
{
317+
EndpointId: 0,
318+
EndpointTypeIndex: 0,
319+
},
320+
},
321+
}
322+
323+
renderer, err := regen.NewIdlRenderer(specification)
324+
if err != nil {
325+
return err
326+
}
327+
renderer.SuppressEndpoints = true
328+
329+
zapPath := filepath.Join(z.SdkRoot, "src/controller/data_model/controller-clusters.zap")
330+
zapData := pipeline.NewData[*zap.File](zapPath, syntheticFile)
331+
332+
outputs, _, err := renderer.Process(cc, zapData, 0, 1)
333+
if err != nil {
334+
return err
335+
}
336+
337+
if len(outputs) == 0 {
338+
return fmt.Errorf("no output generated")
339+
}
340+
341+
writer := files.NewWriter[string]("Writing controller-clusters.matter", z.OutputOptions)
342+
343+
outputSet := pipeline.StringSet(pipeline.NewMap[string, *pipeline.Data[string]]())
344+
outputSet.Store(outputs[0].Path, outputs[0])
345+
346+
return writer.Write(cc, outputSet, z.ProcessingOptions)
347+
}

zap/regen/render.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/project-chip/alchemy/matter/conformance"
1717
"github.com/project-chip/alchemy/matter/spec"
1818
"github.com/project-chip/alchemy/matter/types"
19+
"github.com/project-chip/alchemy/provisional"
1920
"github.com/project-chip/alchemy/zap"
2021
)
2122

@@ -26,6 +27,8 @@ type IdlRenderer struct {
2627
spec *spec.Specification
2728

2829
commonAttributes matter.FieldSet
30+
31+
SuppressEndpoints bool
2932
}
3033

3134
func NewIdlRenderer(spec *spec.Specification) (IdlRenderer, error) {
@@ -138,16 +141,7 @@ func (p IdlRenderer) Process(cxt context.Context, input *pipeline.Data[*zap.File
138141
namespaces := make(map[string]*matter.Namespace)
139142

140143
spec.TraverseEntities(p.spec, func(parentCluster *matter.Cluster, parent, entity types.Entity) parse.SearchShould {
141-
if parentCluster == nil {
142-
switch entity := entity.(type) {
143-
case *matter.Bitmap, *matter.Command, *matter.Enum, *matter.Struct:
144-
_, ok := globalEntities[entity]
145-
if ok {
146-
globalEntities[entity] = true
147-
}
148-
}
149-
return parse.SearchShouldContinue
150-
}
144+
151145
ce, ok := clusterEntities[parentCluster]
152146
if !ok {
153147
return parse.SearchShouldSkip
@@ -172,6 +166,10 @@ func (p IdlRenderer) Process(cxt context.Context, input *pipeline.Data[*zap.File
172166
return parse.SearchShouldSkip
173167
}
174168
}
169+
if _, isGlobal := p.spec.GlobalObjects[entity]; isGlobal {
170+
globalEntities[entity] = true
171+
return parse.SearchShouldContinue
172+
}
175173
ce[entity] = struct{}{}
176174
globalEntities[entity] = false
177175
return parse.SearchShouldContinue
@@ -181,6 +179,9 @@ func (p IdlRenderer) Process(cxt context.Context, input *pipeline.Data[*zap.File
181179
if !isGlobal {
182180
continue
183181
}
182+
if provisional.Check(p.spec, entity, entity) == provisional.StateUnreferenced {
183+
continue
184+
}
184185
switch entity := entity.(type) {
185186
case *matter.Bitmap:
186187
globalBitmaps = append(globalBitmaps, entity)
@@ -196,7 +197,11 @@ func (p IdlRenderer) Process(cxt context.Context, input *pipeline.Data[*zap.File
196197

197198
for fieldName, ns := range namespaces {
198199
en := matter.NewEnum(ns.Source(), ns.Parent())
199-
en.Name = fieldName + "Tag"
200+
if strings.HasSuffix(fieldName, "Tag") {
201+
en.Name = fieldName
202+
} else {
203+
en.Name = fieldName + "Tag"
204+
}
200205
en.Type = types.NewDataType(types.BaseDataTypeEnum8, types.DataTypeRankScalar)
201206
for _, tag := range ns.SemanticTags {
202207
nst := matter.NewEnumValue(tag.Source(), en)
@@ -235,6 +240,9 @@ func (p IdlRenderer) Process(cxt context.Context, input *pipeline.Data[*zap.File
235240
"clusters": clusterList,
236241
"endpoints": endpoints,
237242
}
243+
if p.SuppressEndpoints {
244+
tc["endpoints"] = nil
245+
}
238246

239247
var t *raymond.Template
240248
t, err = p.loadTemplate(p.spec)

0 commit comments

Comments
 (0)