diff --git a/Project.toml b/Project.toml index 37c6efb..588b90d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,10 @@ name = "FMICore" uuid = "8af89139-c281-408e-bce2-3005eb87462f" authors = ["TT ", "LM ", "JK "] -version = "0.16.1" +version = "0.17.0" + +[deps] +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" [compat] julia = "1.6" diff --git a/src/FMI2/cfunc.jl b/src/FMI2/cfunc.jl index 25e967e..53cbf81 100644 --- a/src/FMI2/cfunc.jl +++ b/src/FMI2/cfunc.jl @@ -76,7 +76,7 @@ The function controls debug logging that is output via the logger function callb function fmi2SetDebugLogging(cfunc::Ptr{Nothing}, c::fmi2Component, logginOn::fmi2Boolean, nCategories::Csize_t, categories::Union{Ptr{fmi2String}, AbstractArray{fmi2String}}) status = ccall(cfunc, fmi2Status, - (fmi2Component, fmi2Component, Csize_t, Ptr{fmi2String}), + (fmi2Component, fmi2Boolean, Csize_t, Ptr{fmi2String}), c, logginOn, nCategories, categories) @debug "fmi2SetDebugLogging(c: $(c), logginOn: $(loggingOn), nCategories: $(nCategories), categories: $(categories)) → $(status)" return status diff --git a/src/FMI2/ctype.jl b/src/FMI2/ctype.jl index eb1b326..6122923 100644 --- a/src/FMI2/ctype.jl +++ b/src/FMI2/ctype.jl @@ -3,6 +3,8 @@ # Licensed under the MIT license. See LICENSE file in the project root for details. # +import Dates: DateTime + """ Source: FMISpec2.0.2[p.84]: 3.2.2 Evaluation of Model Equations @@ -298,6 +300,32 @@ mutable struct fmi2EnumerationAttributesExt <: fmi2AttributesExt end end +""" +Custom helper, not part of the FMI-Spec. + +Source: 2.2.3 Definition of Types (TypeDefinitions) +""" +mutable struct fmi2ModelDescriptionEnumerationItem + + # mandatory + name::Union{String,Nothing} + value::Union{Integer,Nothing} + + # optional + description::Union{String,Nothing} + + # Constructor + function fmi2ModelDescriptionEnumerationItem() + inst = new() + inst.name = nothing + inst.value = nothing + + inst.description = nothing + + return inst + end +end + """ ToDo. @@ -307,8 +335,8 @@ mutable struct fmi2EnumerationAttributes <: fmi2Attributes # optional quantity::Union{String, Nothing} - # mandatory - items::Array # ToDo: Parse Items + # mandatory (invisible) + items::Array{fmi2ModelDescriptionEnumerationItem,1} # constructor function fmi2EnumerationAttributes() @@ -319,6 +347,21 @@ mutable struct fmi2EnumerationAttributes <: fmi2Attributes end end +import Base.push! +function push!(attr::fmi2EnumerationAttributes, items...) + push!(attr.items, items...) +end + +import Base.getindex +function getindex(attr::fmi2EnumerationAttributes, keys...) + getindex(attr.items, keys...) +end + +import Base.length +function length(attr::fmi2EnumerationAttributes) + length(attr.items) +end + # mimic existence of properties of `fmi2RealAttributes` in `fmi2RealAttributesExt` (inheritance is not available in Julia, so it is simulated) function Base.setproperty!(var::Union{fmi2RealAttributesExt, fmi2IntegerAttributesExt}, sym::Symbol, value) @@ -497,67 +540,10 @@ mutable struct fmi2ScalarVariable inst = new() inst.name = name inst.valueReference = valueReference - inst.description = nothing # "" + inst.description = nothing inst.causality = causality - if inst.causality == nothing - inst.causality = fmi2CausalityLocal # this is the default according FMI-spec p. 48 - end inst.variability = variability - if inst.variability == nothing - inst.variability = fmi2VariabilityContinuous # this is the default according FMI-spec p. 49 - end inst.initial = initial - if inst.initial == nothing - # setting default value for initial according FMI-spec p. 51 - if inst.causality != nothing && inst.variability != nothing - if inst.causality == fmi2CausalityParameter - if inst.variability == fmi2VariabilityFixed || inst.variability == fmi2VariabilityTunable - inst.initial = fmi2InitialExact - else - @warn "Causality: $(fmi2CausalityToString(inst.causality)) Variability: $(fmi2VariabilityToString(inst.variability)) This combination is not allowed." - end - elseif inst.causality == fmi2CausalityCalculatedParameter - if inst.variability == fmi2VariabilityFixed || inst.variability == fmi2VariabilityTunable - inst.initial = fmi2InitialCalculated - else - @warn "Causality: $(fmi2CausalityToString(inst.causality)) Variability: $(fmi2VariabilityToString(inst.variability)) This combination is not allowed." - end - elseif inst.causality == fmi2CausalityInput - if inst.variability == fmi2VariabilityDiscrete || inst.variability == fmi2VariabilityContinuous - # everything allright, it's not allowed to define `initial` in this case - else - @warn "Causality: $(fmi2CausalityToString(inst.causality)) Variability: $(fmi2VariabilityToString(inst.variability)) This combination is not allowed." - end - elseif inst.causality == fmi2CausalityOutput - if inst.variability == fmi2VariabilityConstant - inst.initial = fmi2InitialExact - elseif inst.variability == fmi2VariabilityDiscrete || inst.variability == fmi2VariabilityContinuous - inst.initial = fmi2InitialCalculated - else - @warn "Causality: $(fmi2CausalityToString(inst.causality)) Variability: $(fmi2VariabilityToString(inst.variability)) This combination is not allowed." - end - elseif inst.causality == fmi2CausalityLocal - if inst.variability == fmi2VariabilityConstant - inst.initial = fmi2InitialExact - elseif inst.variability == fmi2VariabilityFixed || inst.variability == fmi2VariabilityTunable - inst.initial = fmi2InitialCalculated - elseif inst.variability == fmi2VariabilityDiscrete || inst.variability == fmi2VariabilityContinuous - inst.initial = fmi2InitialCalculated - else - @warn "Causality: $(fmi2CausalityToString(inst.causality)) Variability: $(fmi2VariabilityToString(inst.variability)) This combination is not allowed." - end - elseif inst.causality == fmi2CausalityIndependent - if inst.variability == fmi2VariabilityContinuous - # everything allright, it's not allowed to define `initial` in this case - else - @warn "Causality: $(fmi2CausalityToString(inst.causality)) Variability: $(fmi2VariabilityToString(inst.variability)) This combination is not allowed." - end - end - else - @warn "Causality: $(fmi2CausalityToString(inst.causality)) Variability: $(fmi2VariabilityToString(inst.variability)) Cannot pick default value for `initial` if one of them is `nothing`." - end - end - inst.canHandleMultipleSetPerTimeInstant = nothing inst.annotations = nothing inst.attribute = nothing @@ -567,6 +553,74 @@ mutable struct fmi2ScalarVariable end export fmi2ScalarVariable +function getAttributes(sv::fmi2ScalarVariable) + + causality = sv.causality + variability = sv.variability + initial = sv.initial + + if causality == nothing + causality = fmi2CausalityLocal # this is the default according FMI-spec p. 48 + end + + if variability == nothing + variability = fmi2VariabilityContinuous # this is the default according FMI-spec p. 49 + end + + if initial == nothing + # setting default value for initial according FMI-spec p. 51 + if causality != nothing && variability != nothing + if causality == fmi2CausalityParameter + if variability == fmi2VariabilityFixed || variability == fmi2VariabilityTunable + initial = fmi2InitialExact + else + @warn "Causality: $(fmi2CausalityToString(causality)) Variability: $(fmi2VariabilityToString(variability)) This combination is not allowed." + end + elseif causality == fmi2CausalityCalculatedParameter + if variability == fmi2VariabilityFixed || variability == fmi2VariabilityTunable + initial = fmi2InitialCalculated + else + @warn "Causality: $(fmi2CausalityToString(causality)) Variability: $(fmi2VariabilityToString(variability)) This combination is not allowed." + end + elseif causality == fmi2CausalityInput + if variability == fmi2VariabilityDiscrete || variability == fmi2VariabilityContinuous + # everything allright, it's not allowed to define `initial` in this case + else + @warn "Causality: $(fmi2CausalityToString(causality)) Variability: $(fmi2VariabilityToString(variability)) This combination is not allowed." + end + elseif causality == fmi2CausalityOutput + if variability == fmi2VariabilityConstant + initial = fmi2InitialExact + elseif variability == fmi2VariabilityDiscrete || variability == fmi2VariabilityContinuous + initial = fmi2InitialCalculated + else + @warn "Causality: $(fmi2CausalityToString(causality)) Variability: $(fmi2VariabilityToString(variability)) This combination is not allowed." + end + elseif causality == fmi2CausalityLocal + if variability == fmi2VariabilityConstant + initial = fmi2InitialExact + elseif variability == fmi2VariabilityFixed || variability == fmi2VariabilityTunable + initial = fmi2InitialCalculated + elseif variability == fmi2VariabilityDiscrete || variability == fmi2VariabilityContinuous + initial = fmi2InitialCalculated + else + @warn "Causality: $(fmi2CausalityToString(causality)) Variability: $(fmi2VariabilityToString(variability)) This combination is not allowed." + end + elseif causality == fmi2CausalityIndependent + if variability == fmi2VariabilityContinuous + # everything allright, it's not allowed to define `initial` in this case + else + @warn "Causality: $(fmi2CausalityToString(causality)) Variability: $(fmi2VariabilityToString(variability)) This combination is not allowed." + end + end + else + @warn "Causality: $(fmi2CausalityToString(causality)) Variability: $(fmi2VariabilityToString(variability)) Cannot pick default value for `initial` if one of them is `nothing`." + end + end + + return causality, variability, initial +end + """ Overload the Base.show() function for custom printing of the fmi2ScalarVariable. """ @@ -780,63 +834,76 @@ mutable struct fmi2ModelDescription copyright::Union{String, Nothing} license::Union{String, Nothing} generationTool::Union{String, Nothing} - generationDateAndTime # DateTime + generationDateAndTime::Union{DateTime, String, Nothing} variableNamingConvention::Union{fmi2VariableNamingConvention, Nothing} numberOfEventIndicators::Union{UInt, Nothing} - unitDefinitions::Array{fmi2Unit, 1} - typeDefinitions::Array{fmi2SimpleType, 1} - logCategories::Array # ToDo: Array type + unitDefinitions::Union{Array{fmi2Unit, 1}, Nothing} + typeDefinitions::Union{Array{fmi2SimpleType, 1}, Nothing} + logCategories::Union{Array, Nothing} # ToDo: Array type defaultExperiment::Union{fmi2ModelDescriptionDefaultExperiment, Nothing} + modelExchange::Union{fmi2ModelDescriptionModelExchange, Nothing} + coSimulation::Union{fmi2ModelDescriptionCoSimulation, Nothing} - vendorAnnotations::Array # ToDo: Array type + vendorAnnotations::Union{Array, Nothing} # ToDo: Array type modelVariables::Array{fmi2ScalarVariable, 1} modelStructure::fmi2ModelDescriptionModelStructure - modelExchange::Union{fmi2ModelDescriptionModelExchange, Nothing} - coSimulation::Union{fmi2ModelDescriptionCoSimulation, Nothing} - # additionals valueReferences::Array{fmi2ValueReference} inputValueReferences::Array{fmi2ValueReference} outputValueReferences::Array{fmi2ValueReference} stateValueReferences::Array{fmi2ValueReference} + discreteStateValueReferences::Union{Array{fmi2ValueReference}} derivativeValueReferences::Array{fmi2ValueReference} parameterValueReferences::Array{fmi2ValueReference} - stringValueReferences::Dict{String, fmi2ValueReference} # String-ValueReference pairs of MD - - # ToDo: from here on refactoring is needed - - enumerations::fmi2Enum - # additional fields (non-FMI-specific) + stringValueReferences::Dict{String, fmi2ValueReference} # String-ValueReference pairs of MD valueReferenceIndicies::Dict{UInt, UInt} # Constructor for uninitialized struct function fmi2ModelDescription() inst = new() + inst.fmiVersion = "" inst.modelName = "" inst.guid = "" + inst.description = nothing + inst.author = nothing + inst.version = nothing + inst.copyright = nothing + inst.license = nothing + inst.generationTool = nothing + inst.generationDateAndTime = nothing + inst.variableNamingConvention = nothing + inst.numberOfEventIndicators = nothing + + inst.unitDefinitions = nothing + inst.typeDefinitions = nothing + inst.logCategories = nothing + + inst.defaultExperiment = nothing inst.modelExchange = nothing inst.coSimulation = nothing - inst.defaultExperiment = nothing - + + inst.vendorAnnotations = nothing inst.modelVariables = Array{fmi2ScalarVariable, 1}() inst.modelStructure = fmi2ModelDescriptionModelStructure() - inst.numberOfEventIndicators = nothing - inst.enumerations = [] inst.valueReferences = [] inst.inputValueReferences = [] inst.outputValueReferences = [] inst.stateValueReferences = [] + inst.discreteStateValueReferences = [] inst.derivativeValueReferences = [] inst.parameterValueReferences = [] + inst.stringValueReferences = Dict{String, fmi2ValueReference}() + inst.valueReferenceIndicies = Dict{UInt, UInt}() + return inst end end diff --git a/src/FMI2/struct.jl b/src/FMI2/struct.jl index 4b18b1f..40d52bd 100644 --- a/src/FMI2/struct.jl +++ b/src/FMI2/struct.jl @@ -183,7 +183,8 @@ mutable struct FMU2Component{F} solution::FMU2Solution force::Bool - loggingOn::Bool + loggingOn::fmi2Boolean + visible::fmi2Boolean callbackFunctions::fmi2CallbackFunctions instanceName::String continuousStatesChanged::fmi2Boolean @@ -200,7 +201,7 @@ mutable struct FMU2Component{F} z::Union{Array{fmi2Real, 1}, Nothing} # the system event indicators z_prev::Union{Array{fmi2Real, 1}, Nothing} # the last system event indicators - realValues::Dict + values::Dict{fmi2ValueReference, Union{fmi2Real, fmi2Integer, fmi2Boolean}} x_vrs::Array{fmi2ValueReference, 1} # the system state value references ẋ_vrs::Array{fmi2ValueReference, 1} # the system state derivative value references @@ -215,6 +216,7 @@ mutable struct FMU2Component{F} D::Union{FMUJacobian, Nothing} # deprecated + realValues::Dict senseFunc::Symbol jac_ẋy_x::Union{Matrix{fmi2Real}, Nothing} jac_ẋy_u::Union{Matrix{fmi2Real}, Nothing} @@ -235,7 +237,8 @@ mutable struct FMU2Component{F} inst.problem = nothing inst.type = nothing - inst.loggingOn = false + inst.loggingOn = fmi2False + inst.visible = fmi2False inst.instanceName = "" inst.continuousStatesChanged = fmi2False @@ -245,7 +248,7 @@ mutable struct FMU2Component{F} inst.z = nothing inst.z_prev = nothing - inst.realValues = Dict() + inst.values = Dict{fmi2ValueReference, Union{fmi2Real, fmi2Integer, fmi2Boolean}}() inst.x_vrs = Array{fmi2ValueReference, 1}() inst.ẋ_vrs = Array{fmi2ValueReference, 1}() inst.u_vrs = Array{fmi2ValueReference, 1}() @@ -263,6 +266,7 @@ mutable struct FMU2Component{F} # deprecated inst.senseFunc = :auto + inst.realValues = Dict() inst.jac_x = Array{fmi2Real, 1}() inst.jac_u = nothing inst.jac_t = -1.0 @@ -272,6 +276,13 @@ mutable struct FMU2Component{F} return inst end + function FMU2Component(fmu::F) where {F} + inst = FMU2Component{F}() + inst.fmu = fmu + + return inst + end + function FMU2Component(compAddr::fmi2Component, fmu::F) where {F} inst = FMU2Component{F}() inst.compAddr = compAddr diff --git a/src/FMICore.jl b/src/FMICore.jl index 32cd89f..7b08c4b 100644 --- a/src/FMICore.jl +++ b/src/FMICore.jl @@ -52,4 +52,46 @@ include("FMI3/cfunc.jl") include("FMI3/convert.jl") include("FMI3/struct.jl") +# finds the FMU2Component for a given address (fmi2Component) +function dereferenceComponent(fmu::FMU, addr::fmi2Component) + for component in fmu.components + if addr == component.compAddr + return component + end + end + + @warn "Unknown fmi2Component at $(addr)." + return nothing +end + +# Function for logging an info (if logging is available) +function logInfo(::Nothing, message, status::fmi2Status=fmi2StatusOK) + @warn "logInfo(::Nothing, $(message), $(status))" +end +function logInfo(component::FMU2Component, message, status::fmi2Status=fmi2StatusOK) + if component.loggingOn == fmi2True + ccall(component.callbackFunctions.logger, Cvoid, (fmi2ComponentEnvironment, fmi2String, fmi2Status, fmi2String, fmi2String), component.callbackFunctions.componentEnvironment, component.instanceName, status, "info", message * "\n") + end +end + +# Function for logging a warning (if logging is available) +function logWarning(::Nothing, message, status::fmi2Status=fmi2StatusWarning) + @warn "logWarning(::Nothing, $(message), $(status))" +end +function logWarning(component::FMU2Component, message, status::fmi2Status=fmi2StatusWarning) + if component.loggingOn == fmi2True + ccall(component.callbackFunctions.logger, Cvoid, (fmi2ComponentEnvironment, fmi2String, fmi2Status, fmi2String, fmi2String), component.callbackFunctions.componentEnvironment, component.instanceName, status, "warning", message * "\n") + end +end + +# Function for logging an error (if logging is available) +function logError(::Nothing, message, status::fmi2Status=fmi2StatusError) + @warn "logError(::Nothing, $(message), $(status))" +end +function logError(component::FMU2Component, message, status::fmi2Status=fmi2StatusError) + if component.loggingOn == fmi2True + ccall(component.callbackFunctions.logger, Cvoid, (fmi2ComponentEnvironment, fmi2String, fmi2Status, fmi2String, fmi2String), component.callbackFunctions.componentEnvironment, component.instanceName, status, "error", message * "\n") + end +end + end # module diff --git a/src/logging.jl b/src/logging.jl index e5a187a..3c467a0 100644 --- a/src/logging.jl +++ b/src/logging.jl @@ -16,7 +16,7 @@ export FMULogLevel, FMULogLevelNone, FMULogLevelInfo, FMULogLevelWarn, FMULogLev """ Logs a message with level `info` if the log level allows it. """ -function logInfo(fmu, message) +function logInfo(fmu::FMU, message) if fmu.logLevel <= FMULogLevelInfo @info message end @@ -26,19 +26,26 @@ export logInfo """ Logs a message with level `warn` if the log level allows it. """ -function logWarn(fmu, message) +function logWarning(fmu::FMU, message) if fmu.logLevel <= FMULogLevelWarn @warn message end end +export logWarning + +function logWarn(fmu::FMU, message) + @warn "logWarn is deprecated, use logWarning." + logWarning(fmu, message) +end export logWarn """ Logs a message with level `error` if the log level allows it. """ -function logError(fmu, message) +function logError(fmu::FMU, message) if fmu.logLevel <= FMULogLevelError @error message end end -export logError \ No newline at end of file +export logError +