Compare commits
3 Commits
6781d50ee4
...
31996ae710
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31996ae710 | ||
|
|
776b1fddc3 | ||
|
|
597fd3eddf |
@@ -56,27 +56,44 @@ func (b *Builder) Build(f *os.File) error {
|
|||||||
tree.AddFile(file, config)
|
tree.AddFile(file, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine root node to print
|
||||||
|
rootNode := tree.Root
|
||||||
|
if expectedProject != "" {
|
||||||
|
if child, ok := tree.Root.Children[expectedProject]; ok {
|
||||||
|
rootNode = child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write entire root content (definitions and children) to the single output file
|
// Write entire root content (definitions and children) to the single output file
|
||||||
b.writeNodeContent(f, tree.Root, 0)
|
b.writeNodeBody(f, rootNode, 0)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) writeNodeContent(f *os.File, node *index.ProjectNode, indent int) {
|
func (b *Builder) writeNodeContent(f *os.File, node *index.ProjectNode, indent int) {
|
||||||
// 1. Sort Fragments: Class first
|
|
||||||
sort.SliceStable(node.Fragments, func(i, j int) bool {
|
|
||||||
return hasClass(node.Fragments[i]) && !hasClass(node.Fragments[j])
|
|
||||||
})
|
|
||||||
|
|
||||||
indentStr := strings.Repeat(" ", indent)
|
indentStr := strings.Repeat(" ", indent)
|
||||||
|
|
||||||
// If this node has a RealName (e.g. +App), we print it as an object definition
|
// If this node has a RealName (e.g. +App), we print it as an object definition
|
||||||
if node.RealName != "" {
|
if node.RealName != "" {
|
||||||
fmt.Fprintf(f, "%s%s = {\n", indentStr, node.RealName)
|
fmt.Fprintf(f, "%s%s = {\n", indentStr, node.RealName)
|
||||||
indent++
|
indent++
|
||||||
indentStr = strings.Repeat(" ", indent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.writeNodeBody(f, node, indent)
|
||||||
|
|
||||||
|
if node.RealName != "" {
|
||||||
|
indent--
|
||||||
|
indentStr = strings.Repeat(" ", indent)
|
||||||
|
fmt.Fprintf(f, "%s}\n", indentStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) writeNodeBody(f *os.File, node *index.ProjectNode, indent int) {
|
||||||
|
// 1. Sort Fragments: Class first
|
||||||
|
sort.SliceStable(node.Fragments, func(i, j int) bool {
|
||||||
|
return hasClass(node.Fragments[i]) && !hasClass(node.Fragments[j])
|
||||||
|
})
|
||||||
|
|
||||||
writtenChildren := make(map[string]bool)
|
writtenChildren := make(map[string]bool)
|
||||||
|
|
||||||
// 2. Write definitions from fragments
|
// 2. Write definitions from fragments
|
||||||
@@ -110,12 +127,6 @@ func (b *Builder) writeNodeContent(f *os.File, node *index.ProjectNode, indent i
|
|||||||
child := node.Children[k]
|
child := node.Children[k]
|
||||||
b.writeNodeContent(f, child, indent)
|
b.writeNodeContent(f, child, indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.RealName != "" {
|
|
||||||
indent--
|
|
||||||
indentStr = strings.Repeat(" ", indent)
|
|
||||||
fmt.Fprintf(f, "%s}\n", indentStr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) writeDefinition(f *os.File, def parser.Definition, indent int) {
|
func (b *Builder) writeDefinition(f *os.File, def parser.Definition, indent int) {
|
||||||
|
|||||||
@@ -2,9 +2,32 @@ package schema
|
|||||||
|
|
||||||
#Classes: {
|
#Classes: {
|
||||||
RealTimeApplication: {
|
RealTimeApplication: {
|
||||||
Functions: {...} // type: node
|
Functions!: {
|
||||||
Data!: {...} // type: node
|
Class: "ReferenceContainer"
|
||||||
States!: {...} // type: node
|
[_= !~"^Class$"]: {
|
||||||
|
#meta: type: "gam"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
} // type: node
|
||||||
|
Data!: {
|
||||||
|
Class: "ReferenceContainer"
|
||||||
|
DefaultDataSource: string
|
||||||
|
[_= !~"^(Class|DefaultDataSource)$"]: {
|
||||||
|
#meta: type: "datasource"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
States!: {
|
||||||
|
Class: "ReferenceContainer"
|
||||||
|
[_= !~"^Class$"]: {
|
||||||
|
Class: "RealTimeState"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
} // type: node
|
||||||
|
Scheduler!: {
|
||||||
|
...
|
||||||
|
#meta: type: "scheduler"
|
||||||
|
}
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
Message: {
|
Message: {
|
||||||
@@ -23,7 +46,7 @@ package schema
|
|||||||
Class: "ReferenceContainer"
|
Class: "ReferenceContainer"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
[_ = !~"^(Class|ENTER)$"]: StateMachineEvent
|
[_ = !~"^(Class|ENTER|EXIT)$"]: StateMachineEvent
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
StateMachine: {
|
StateMachine: {
|
||||||
@@ -40,16 +63,19 @@ package schema
|
|||||||
}
|
}
|
||||||
GAMScheduler: {
|
GAMScheduler: {
|
||||||
TimingDataSource: string // type: reference
|
TimingDataSource: string // type: reference
|
||||||
|
#meta: type: "scheduler"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
TimingDataSource: {
|
TimingDataSource: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "IN"
|
#meta: direction: "IN"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
IOGAM: {
|
IOGAM: {
|
||||||
InputSignals?: {...} // type: node
|
InputSignals?: {...} // type: node
|
||||||
OutputSignals?: {...} // type: node
|
OutputSignals?: {...} // type: node
|
||||||
|
#meta: type: "gam"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
ReferenceContainer: {
|
ReferenceContainer: {
|
||||||
@@ -57,11 +83,13 @@ package schema
|
|||||||
}
|
}
|
||||||
ConstantGAM: {
|
ConstantGAM: {
|
||||||
...
|
...
|
||||||
|
#meta: type: "gam"
|
||||||
}
|
}
|
||||||
PIDGAM: {
|
PIDGAM: {
|
||||||
Kp: float | int // type: float (allow int as it promotes)
|
Kp: float | int // type: float (allow int as it promotes)
|
||||||
Ki: float | int
|
Ki: float | int
|
||||||
Kd: float | int
|
Kd: float | int
|
||||||
|
#meta: type: "gam"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
FileDataSource: {
|
FileDataSource: {
|
||||||
@@ -69,45 +97,58 @@ package schema
|
|||||||
Format?: string
|
Format?: string
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "INOUT"
|
#meta: direction: "INOUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
LoggerDataSource: {
|
LoggerDataSource: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "OUT"
|
#meta: direction: "OUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
DANStream: {
|
DANStream: {
|
||||||
Timeout?: int
|
Timeout?: int
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "OUT"
|
#meta: direction: "OUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
EPICSCAInput: {
|
EPICSCAInput: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "IN"
|
#meta: direction: "IN"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
EPICSCAOutput: {
|
EPICSCAOutput: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "OUT"
|
#meta: direction: "OUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
EPICSPVAInput: {
|
EPICSPVAInput: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "IN"
|
#meta: direction: "IN"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
EPICSPVAOutput: {
|
EPICSPVAOutput: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "OUT"
|
#meta: direction: "OUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
SDNSubscriber: {
|
SDNSubscriber: {
|
||||||
Address: string
|
ExecutionMode?: *"IndependentThread" | "RealTimeThread"
|
||||||
Port: int
|
Topic!: string
|
||||||
Interface?: string
|
Address?: string
|
||||||
|
Interface!: string
|
||||||
|
CPUs?: uint32
|
||||||
|
InternalTimeout?: uint32
|
||||||
|
Timeout?: uint32
|
||||||
|
IgnoreTimeoutError?: 0 | 1
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "IN"
|
#meta: direction: "IN"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
SDNPublisher: {
|
SDNPublisher: {
|
||||||
@@ -116,6 +157,7 @@ package schema
|
|||||||
Interface?: string
|
Interface?: string
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "OUT"
|
#meta: direction: "OUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
UDPReceiver: {
|
UDPReceiver: {
|
||||||
@@ -123,12 +165,14 @@ package schema
|
|||||||
Address?: string
|
Address?: string
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "IN"
|
#meta: direction: "IN"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
UDPSender: {
|
UDPSender: {
|
||||||
Destination: string
|
Destination: string
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "OUT"
|
#meta: direction: "OUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
FileReader: {
|
FileReader: {
|
||||||
@@ -137,6 +181,7 @@ package schema
|
|||||||
Interpolate?: string
|
Interpolate?: string
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "IN"
|
#meta: direction: "IN"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
FileWriter: {
|
FileWriter: {
|
||||||
@@ -145,6 +190,7 @@ package schema
|
|||||||
StoreOnTrigger?: int
|
StoreOnTrigger?: int
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "OUT"
|
#meta: direction: "OUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
OrderedClass: {
|
OrderedClass: {
|
||||||
@@ -152,15 +198,25 @@ package schema
|
|||||||
Second: string
|
Second: string
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
BaseLib2GAM: {...}
|
BaseLib2GAM: {
|
||||||
ConversionGAM: {...}
|
#meta: type: "gam"
|
||||||
DoubleHandshakeGAM: {...}
|
...
|
||||||
|
}
|
||||||
|
ConversionGAM: {
|
||||||
|
#meta: type: "gam"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
DoubleHandshakeGAM: {
|
||||||
|
#meta: type: "gam"
|
||||||
|
...
|
||||||
|
}
|
||||||
FilterGAM: {
|
FilterGAM: {
|
||||||
Num: [...]
|
Num: [...]
|
||||||
Den: [...]
|
Den: [...]
|
||||||
ResetInEachState?: _
|
ResetInEachState?: _
|
||||||
InputSignals?: {...}
|
InputSignals?: {...}
|
||||||
OutputSignals?: {...}
|
OutputSignals?: {...}
|
||||||
|
#meta: type: "gam"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
HistogramGAM: {
|
HistogramGAM: {
|
||||||
@@ -168,24 +224,57 @@ package schema
|
|||||||
StateChangeResetName?: string
|
StateChangeResetName?: string
|
||||||
InputSignals?: {...}
|
InputSignals?: {...}
|
||||||
OutputSignals?: {...}
|
OutputSignals?: {...}
|
||||||
|
#meta: type: "gam"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
Interleaved2FlatGAM: {
|
||||||
|
#meta: type: "gam"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
FlattenedStructIOGAM: {
|
||||||
|
#meta: type: "gam"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
Interleaved2FlatGAM: {...}
|
|
||||||
FlattenedStructIOGAM: {...}
|
|
||||||
MathExpressionGAM: {
|
MathExpressionGAM: {
|
||||||
Expression: string
|
Expression: string
|
||||||
InputSignals?: {...}
|
InputSignals?: {...}
|
||||||
OutputSignals?: {...}
|
OutputSignals?: {...}
|
||||||
|
#meta: type: "gam"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
MessageGAM: {
|
||||||
|
#meta: type: "gam"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
MuxGAM: {
|
||||||
|
#meta: type: "gam"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
SimulinkWrapperGAM: {
|
||||||
|
#meta: type: "gam"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
SSMGAM: {
|
||||||
|
#meta: type: "gam"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
StatisticsGAM: {
|
||||||
|
#meta: type: "gam"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
TimeCorrectionGAM: {
|
||||||
|
#meta: type: "gam"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
TriggeredIOGAM: {
|
||||||
|
|
||||||
|
#meta: type: "gam"
|
||||||
|
...
|
||||||
|
}
|
||||||
|
WaveformGAM: {
|
||||||
|
#meta: type: "gam"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
MessageGAM: {...}
|
|
||||||
MuxGAM: {...}
|
|
||||||
SimulinkWrapperGAM: {...}
|
|
||||||
SSMGAM: {...}
|
|
||||||
StatisticsGAM: {...}
|
|
||||||
TimeCorrectionGAM: {...}
|
|
||||||
TriggeredIOGAM: {...}
|
|
||||||
WaveformGAM: {...}
|
|
||||||
DAN: {
|
DAN: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "OUT"
|
#meta: direction: "OUT"
|
||||||
@@ -201,11 +290,13 @@ package schema
|
|||||||
Signals: {...}
|
Signals: {...}
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "IN"
|
#meta: direction: "IN"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
LinkDataSource: {
|
LinkDataSource: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "INOUT"
|
#meta: direction: "INOUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
MDSReader: {
|
MDSReader: {
|
||||||
@@ -215,6 +306,7 @@ package schema
|
|||||||
Signals: {...}
|
Signals: {...}
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "IN"
|
#meta: direction: "IN"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
MDSWriter: {
|
MDSWriter: {
|
||||||
@@ -232,72 +324,86 @@ package schema
|
|||||||
Messages?: {...}
|
Messages?: {...}
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "OUT"
|
#meta: direction: "OUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
NI1588TimeStamp: {
|
NI1588TimeStamp: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "IN"
|
#meta: direction: "IN"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
NI6259ADC: {
|
NI6259ADC: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "IN"
|
#meta: direction: "IN"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
NI6259DAC: {
|
NI6259DAC: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "OUT"
|
#meta: direction: "OUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
NI6259DIO: {
|
NI6259DIO: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "INOUT"
|
#meta: direction: "INOUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
NI6368ADC: {
|
NI6368ADC: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "IN"
|
#meta: direction: "IN"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
NI6368DAC: {
|
NI6368DAC: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "OUT"
|
#meta: direction: "OUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
NI6368DIO: {
|
NI6368DIO: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "INOUT"
|
#meta: direction: "INOUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
NI9157CircularFifoReader: {
|
NI9157CircularFifoReader: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "IN"
|
#meta: direction: "IN"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
NI9157MxiDataSource: {
|
NI9157MxiDataSource: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "INOUT"
|
#meta: direction: "INOUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
OPCUADSInput: {
|
OPCUADSInput: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "IN"
|
#meta: direction: "IN"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
OPCUADSOutput: {
|
OPCUADSOutput: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "OUT"
|
#meta: direction: "OUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
RealTimeThreadAsyncBridge: {
|
RealTimeThreadAsyncBridge: {
|
||||||
#meta: direction: "INOUT"
|
#meta: direction: "INOUT"
|
||||||
#meta: multithreaded: bool | true
|
#meta: multithreaded: bool | true
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
RealTimeThreadSynchronisation: {...}
|
RealTimeThreadSynchronisation: {...}
|
||||||
UARTDataSource: {
|
UARTDataSource: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "INOUT"
|
#meta: direction: "INOUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
BaseLib2Wrapper: {...}
|
BaseLib2Wrapper: {...}
|
||||||
@@ -309,15 +415,23 @@ package schema
|
|||||||
GAMDataSource: {
|
GAMDataSource: {
|
||||||
#meta: multithreaded: bool | *false
|
#meta: multithreaded: bool | *false
|
||||||
#meta: direction: "INOUT"
|
#meta: direction: "INOUT"
|
||||||
|
#meta: type: "datasource"
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#Meta: {
|
||||||
|
direction?: "IN" | "OUT" | "INOUT"
|
||||||
|
multithreaded?: bool
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
// Definition for any Object.
|
// Definition for any Object.
|
||||||
// It must have a Class field.
|
// It must have a Class field.
|
||||||
// Based on Class, it validates against #Classes.
|
// Based on Class, it validates against #Classes.
|
||||||
#Object: {
|
#Object: {
|
||||||
Class: string
|
Class: string
|
||||||
|
"#meta"?: #Meta
|
||||||
// Allow any other field by default (extensibility),
|
// Allow any other field by default (extensibility),
|
||||||
// unless #Classes definition is closed.
|
// unless #Classes definition is closed.
|
||||||
// We allow open structs now.
|
// We allow open structs now.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func TestSDNSubscriberValidation(t *testing.T) {
|
|||||||
+MySDN = {
|
+MySDN = {
|
||||||
Class = SDNSubscriber
|
Class = SDNSubscriber
|
||||||
Address = "239.0.0.1"
|
Address = "239.0.0.1"
|
||||||
// Missing Port
|
// Missing Interface
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
p := parser.NewParser(content)
|
p := parser.NewParser(content)
|
||||||
@@ -32,7 +32,7 @@ func TestSDNSubscriberValidation(t *testing.T) {
|
|||||||
|
|
||||||
found := false
|
found := false
|
||||||
for _, d := range v.Diagnostics {
|
for _, d := range v.Diagnostics {
|
||||||
if strings.Contains(d.Message, "Port: incomplete value") {
|
if strings.Contains(d.Message, "Interface: field is required but not present") {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
79
test/validator_schema_meta_test.go
Normal file
79
test/validator_schema_meta_test.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/marte-community/marte-dev-tools/internal/index"
|
||||||
|
"github.com/marte-community/marte-dev-tools/internal/parser"
|
||||||
|
"github.com/marte-community/marte-dev-tools/internal/validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSchemaMetaValidation(t *testing.T) {
|
||||||
|
// 1. Valid Usage
|
||||||
|
validContent := `
|
||||||
|
+App = {
|
||||||
|
Class = RealTimeApplication
|
||||||
|
Functions = { Class = ReferenceContainer }
|
||||||
|
Data = { Class = ReferenceContainer DefaultDataSource = "DS" }
|
||||||
|
States = { Class = ReferenceContainer }
|
||||||
|
Scheduler = { Class = GAMScheduler TimingDataSource = "DS" }
|
||||||
|
#meta = {
|
||||||
|
multithreaded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
pt := index.NewProjectTree()
|
||||||
|
p := parser.NewParser(validContent)
|
||||||
|
cfg, err := p.Parse()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pt.AddFile("valid.marte", cfg)
|
||||||
|
|
||||||
|
v := validator.NewValidator(pt, "")
|
||||||
|
v.ValidateProject()
|
||||||
|
|
||||||
|
if len(v.Diagnostics) > 0 {
|
||||||
|
for _, d := range v.Diagnostics {
|
||||||
|
t.Logf("Diag: %s", d.Message)
|
||||||
|
}
|
||||||
|
t.Errorf("Expected no errors for valid #meta")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Invalid Usage (Wrong Type)
|
||||||
|
invalidContent := `
|
||||||
|
+App = {
|
||||||
|
Class = RealTimeApplication
|
||||||
|
Functions = { Class = ReferenceContainer }
|
||||||
|
Data = { Class = ReferenceContainer DefaultDataSource = "DS" }
|
||||||
|
States = { Class = ReferenceContainer }
|
||||||
|
Scheduler = { Class = GAMScheduler TimingDataSource = "DS" }
|
||||||
|
#meta = {
|
||||||
|
multithreaded = "yes" // Should be bool
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
pt2 := index.NewProjectTree()
|
||||||
|
p2 := parser.NewParser(invalidContent)
|
||||||
|
cfg2, _ := p2.Parse()
|
||||||
|
pt2.AddFile("invalid.marte", cfg2)
|
||||||
|
|
||||||
|
v2 := validator.NewValidator(pt2, "")
|
||||||
|
v2.ValidateProject()
|
||||||
|
|
||||||
|
foundError := false
|
||||||
|
for _, d := range v2.Diagnostics {
|
||||||
|
// CUE validation error message
|
||||||
|
if strings.Contains(d.Message, "mismatched types") || strings.Contains(d.Message, "conflicting values") {
|
||||||
|
foundError = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !foundError {
|
||||||
|
t.Error("Expected error for invalid #meta type, got nothing")
|
||||||
|
for _, d := range v2.Diagnostics {
|
||||||
|
t.Logf("Diag: %s", d.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user