Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Board identification improvements #1674

Merged
merged 10 commits into from
Oct 18, 2022
127 changes: 88 additions & 39 deletions arduino/cores/board.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ type Board struct {
BoardID string
Properties *properties.Map `json:"-"`
PlatformRelease *PlatformRelease `json:"-"`
configOptions *properties.Map
configOptionValues map[string]*properties.Map
configOptionProperties map[string]*properties.Map
defaultConfig *properties.Map
identificationProperties []*properties.Map
}

Expand Down Expand Up @@ -64,66 +68,74 @@ func (b *Board) String() string {
return b.FQBN()
}

func (b *Board) buildConfigOptionsStructures() {
if b.configOptions != nil {
return
}

b.configOptions = properties.NewMap()
allConfigs := b.Properties.SubTree("menu")
for _, option := range allConfigs.FirstLevelKeys() {
b.configOptions.Set(option, b.PlatformRelease.Menus.Get(option))
}

b.configOptionValues = map[string]*properties.Map{}
b.configOptionProperties = map[string]*properties.Map{}
b.defaultConfig = properties.NewMap()
for option, optionProps := range allConfigs.FirstLevelOf() {
b.configOptionValues[option] = properties.NewMap()
values := optionProps.FirstLevelKeys()
b.defaultConfig.Set(option, values[0])
for _, value := range values {
if label, ok := optionProps.GetOk(value); ok {
b.configOptionValues[option].Set(value, label)
b.configOptionProperties[option+"="+value] = optionProps.SubTree(value)
}
}
}
}

// GetConfigOptions returns an OrderedMap of configuration options for this board.
// The returned map will have key and value as option id and option name, respectively.
func (b *Board) GetConfigOptions() *properties.Map {
res := properties.NewMap()
menu := b.Properties.SubTree("menu")
for _, option := range menu.FirstLevelKeys() {
res.Set(option, b.PlatformRelease.Menus.Get(option))
}
return res
b.buildConfigOptionsStructures()
return b.configOptions
}

// GetConfigOptionValues returns an OrderedMap of possible values for a specific configuratio options
// for this board. The returned map will have key and value as option value and option value name,
// respectively.
func (b *Board) GetConfigOptionValues(option string) *properties.Map {
res := properties.NewMap()
menu := b.Properties.SubTree("menu").SubTree(option)
for _, value := range menu.FirstLevelKeys() {
if label, ok := menu.GetOk(value); ok {
res.Set(value, label)
}
}
return res
b.buildConfigOptionsStructures()
return b.configOptionValues[option]
}

// GetBuildProperties returns the build properties and the build
// platform for the Board with the configuration passed as parameter.
func (b *Board) GetBuildProperties(userConfigs *properties.Map) (*properties.Map, error) {
// Clone user configs because they are destroyed during iteration
userConfigs = userConfigs.Clone()
b.buildConfigOptionsStructures()

// Override default configs with user configs
config := b.defaultConfig.Clone()
config.Merge(userConfigs)

// Start with board's base properties
buildProperties := b.Properties.Clone()

// Add all sub-configurations one by one (a config is: option=value)
menu := b.Properties.SubTree("menu")
for _, option := range menu.FirstLevelKeys() {
optionMenu := menu.SubTree(option)
userValue, haveUserValue := userConfigs.GetOk(option)
if haveUserValue {
userConfigs.Remove(option)
if !optionMenu.ContainsKey(userValue) {
return nil, fmt.Errorf(tr("invalid value '%[1]s' for option '%[2]s'"), userValue, option)
}
} else {
// apply default
userValue = optionMenu.FirstLevelKeys()[0]
}

optionsConf := optionMenu.SubTree(userValue)
buildProperties.Merge(optionsConf)
}

// Check for residual invalid options...
if invalidKeys := userConfigs.Keys(); len(invalidKeys) > 0 {
invalidOption := invalidKeys[0]
if invalidOption == "" {
for option, value := range config.AsMap() {
if option == "" {
return nil, fmt.Errorf(tr("invalid empty option found"))
}
return nil, fmt.Errorf(tr("invalid option '%s'"), invalidOption)
if _, ok := b.configOptions.GetOk(option); !ok {
return nil, fmt.Errorf(tr("invalid option '%s'"), option)
}
optionsConf, ok := b.configOptionProperties[option+"="+value]
if !ok {
return nil, fmt.Errorf(tr("invalid value '%[1]s' for option '%[2]s'"), value, option)
}
buildProperties.Merge(optionsConf)
}

return buildProperties, nil
Expand Down Expand Up @@ -153,7 +165,7 @@ func (b *Board) GetIdentificationProperties() []*properties.Map {
}

// IsBoardMatchingIDProperties returns true if the board match the given
// identification properties
// upload port identification properties
func (b *Board) IsBoardMatchingIDProperties(query *properties.Map) bool {
// check checks if the given set of properties p match the "query"
check := func(p *properties.Map) bool {
Expand All @@ -179,3 +191,40 @@ func (b *Board) IsBoardMatchingIDProperties(query *properties.Map) bool {
func GetMonitorSettings(protocol string, boardProperties *properties.Map) *properties.Map {
return boardProperties.SubTree("monitor_port." + protocol)
}

// IdentifyBoardConfiguration returns the configuration of the board that can be
// deduced from the given upload port identification properties
func (b *Board) IdentifyBoardConfiguration(query *properties.Map) *properties.Map {
// check checks if the given set of properties p match the "query"
check := func(p *properties.Map) bool {
for k, v := range p.AsMap() {
if !strings.EqualFold(query.Get(k), v) {
return false
}
}
return true
}
checkAll := func(allP []*properties.Map) bool {
for _, p := range allP {
if check(p) {
return true
}
}
return false
}

res := properties.NewMap()
for _, option := range b.GetConfigOptions().Keys() {
values := b.GetConfigOptionValues(option).Keys()

for _, value := range values {
config := option + "=" + value
configProps := b.configOptionProperties[config]

if checkAll(configProps.ExtractSubIndexSets("upload_port")) {
res.Set(option, value)
}
}
}
return res
}
Loading