Added project schema feature

This commit is contained in:
Martino Ferrari
2026-01-21 18:26:43 +01:00
parent 92dfa38294
commit 970b5697bd
16 changed files with 327 additions and 33 deletions

View File

@@ -137,6 +137,7 @@ type TextEdit struct {
var tree = index.NewProjectTree()
var documents = make(map[string]string)
var projectRoot string
func RunServer() {
reader := bufio.NewReader(os.Stdin)
@@ -193,6 +194,7 @@ func handleMessage(msg *JsonRpcMessage) {
}
if root != "" {
projectRoot = root
logger.Printf("Scanning workspace: %s\n", root)
tree.ScanDirectory(root)
tree.ResolveReferences()
@@ -323,7 +325,7 @@ func handleFormatting(params DocumentFormattingParams) []TextEdit {
}
func runValidation(uri string) {
v := validator.NewValidator(tree)
v := validator.NewValidator(tree, projectRoot)
v.ValidateProject()
v.CheckUnused()

View File

@@ -114,11 +114,32 @@
"BaseLib2GAM": { "fields": [] },
"ConversionGAM": { "fields": [] },
"DoubleHandshakeGAM": { "fields": [] },
"FilterGAM": { "fields": [] },
"HistogramGAM": { "fields": [] },
"FilterGAM": {
"fields": [
{"name": "Num", "type": "array", "mandatory": true},
{"name": "Den", "type": "array", "mandatory": true},
{"name": "ResetInEachState", "type": "any", "mandatory": false},
{"name": "InputSignals", "type": "node", "mandatory": false},
{"name": "OutputSignals", "type": "node", "mandatory": false}
]
},
"HistogramGAM": {
"fields": [
{"name": "BeginCycleNumber", "type": "int", "mandatory": false},
{"name": "StateChangeResetName", "type": "string", "mandatory": false},
{"name": "InputSignals", "type": "node", "mandatory": false},
{"name": "OutputSignals", "type": "node", "mandatory": false}
]
},
"Interleaved2FlatGAM": { "fields": [] },
"FlattenedStructIOGAM": { "fields": [] },
"MathExpressionGAM": { "fields": [] },
"MathExpressionGAM": {
"fields": [
{"name": "Expression", "type": "string", "mandatory": true},
{"name": "InputSignals", "type": "node", "mandatory": false},
{"name": "OutputSignals", "type": "node", "mandatory": false}
]
},
"MessageGAM": { "fields": [] },
"MuxGAM": { "fields": [] },
"SimulinkWrapperGAM": { "fields": [] },
@@ -128,10 +149,42 @@
"TriggeredIOGAM": { "fields": [] },
"WaveformGAM": { "fields": [] },
"DAN": { "fields": [] },
"LinuxTimer": { "fields": [] },
"LinuxTimer": {
"fields": [
{"name": "ExecutionMode", "type": "string", "mandatory": false},
{"name": "SleepNature", "type": "string", "mandatory": false},
{"name": "SleepPercentage", "type": "any", "mandatory": false},
{"name": "Phase", "type": "int", "mandatory": false},
{"name": "CPUMask", "type": "int", "mandatory": false},
{"name": "TimeProvider", "type": "node", "mandatory": false},
{"name": "Signals", "type": "node", "mandatory": true}
]
},
"LinkDataSource": { "fields": [] },
"MDSReader": { "fields": [] },
"MDSWriter": { "fields": [] },
"MDSReader": {
"fields": [
{"name": "TreeName", "type": "string", "mandatory": true},
{"name": "ShotNumber", "type": "int", "mandatory": true},
{"name": "Frequency", "type": "float", "mandatory": true},
{"name": "Signals", "type": "node", "mandatory": true}
]
},
"MDSWriter": {
"fields": [
{"name": "NumberOfBuffers", "type": "int", "mandatory": true},
{"name": "CPUMask", "type": "int", "mandatory": true},
{"name": "StackSize", "type": "int", "mandatory": true},
{"name": "TreeName", "type": "string", "mandatory": true},
{"name": "PulseNumber", "type": "int", "mandatory": false},
{"name": "StoreOnTrigger", "type": "int", "mandatory": true},
{"name": "EventName", "type": "string", "mandatory": true},
{"name": "TimeRefresh", "type": "float", "mandatory": true},
{"name": "NumberOfPreTriggers", "type": "int", "mandatory": false},
{"name": "NumberOfPostTriggers", "type": "int", "mandatory": false},
{"name": "Signals", "type": "node", "mandatory": true},
{"name": "Messages", "type": "node", "mandatory": false}
]
},
"NI1588TimeStamp": { "fields": [] },
"NI6259ADC": { "fields": [] },
"NI6259DAC": { "fields": [] },
@@ -153,4 +206,4 @@
"OPCUA": { "fields": [] },
"SysLogger": { "fields": [] }
}
}
}

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"os"
"path/filepath"
)
//go:embed marte.json
@@ -45,11 +46,89 @@ func LoadSchema(path string) (*Schema, error) {
return &s, nil
}
// DefaultSchema returns a built-in schema with core MARTe classes
// DefaultSchema returns the built-in embedded schema
func DefaultSchema() *Schema {
var s Schema
if err := json.Unmarshal(defaultSchemaJSON, &s); err != nil {
panic(fmt.Sprintf("failed to parse default embedded schema: %v", err))
}
if s.Classes == nil {
s.Classes = make(map[string]ClassDefinition)
}
return &s
}
// Merge adds rules from 'other' to 's'.
// Rules for the same class are merged (new fields added, existing fields updated).
func (s *Schema) Merge(other *Schema) {
if other == nil {
return
}
for className, classDef := range other.Classes {
if existingClass, ok := s.Classes[className]; ok {
// Merge fields
fieldMap := make(map[string]FieldDefinition)
for _, f := range classDef.Fields {
fieldMap[f.Name] = f
}
var mergedFields []FieldDefinition
seen := make(map[string]bool)
// Keep existing fields, update if present in other
for _, f := range existingClass.Fields {
if newF, ok := fieldMap[f.Name]; ok {
mergedFields = append(mergedFields, newF)
} else {
mergedFields = append(mergedFields, f)
}
seen[f.Name] = true
}
// Append new fields
for _, f := range classDef.Fields {
if !seen[f.Name] {
mergedFields = append(mergedFields, f)
}
}
existingClass.Fields = mergedFields
if classDef.Ordered {
existingClass.Ordered = true
}
s.Classes[className] = existingClass
} else {
s.Classes[className] = classDef
}
}
}
func LoadFullSchema(projectRoot string) *Schema {
s := DefaultSchema()
// 1. System Paths
sysPaths := []string{
"/usr/share/mdt/marte_schema.json",
}
home, err := os.UserHomeDir()
if err == nil {
sysPaths = append(sysPaths, filepath.Join(home, ".local/share/mdt/marte_schema.json"))
}
for _, path := range sysPaths {
if sysSchema, err := LoadSchema(path); err == nil {
s.Merge(sysSchema)
}
}
// 2. Project Path
if projectRoot != "" {
projectSchemaPath := filepath.Join(projectRoot, ".marte_schema.json")
if projSchema, err := LoadSchema(projectSchemaPath); err == nil {
s.Merge(projSchema)
}
}
return s
}

View File

@@ -27,10 +27,10 @@ type Validator struct {
Schema *schema.Schema
}
func NewValidator(tree *index.ProjectTree) *Validator {
func NewValidator(tree *index.ProjectTree, projectRoot string) *Validator {
return &Validator{
Tree: tree,
Schema: schema.DefaultSchema(),
Schema: schema.LoadFullSchema(projectRoot),
}
}