Commit 527454f5 authored by Dominique Marcadet's avatar Dominique Marcadet

Merge branch '11-complete-validation-by-nsd' into 'master'

Resolve "complete validation by NSD"

NSD validation is not completely done, but it is enough advanced so that next steps will be done in individual issue/merge request

Closes #11

See merge request !11
parents 1047212f 214af1c6
Pipeline #7687 passed with stages
in 24 seconds
......@@ -22,4 +22,5 @@ Require-Bundle: org.eclipse.core.runtime,
fr.centralesupelec.edf.riseclipse.iec61850.nsd,
fr.centralesupelec.edf.riseclipse.iec61850.nsd.edit,
fr.centralesupelec.edf.riseclipse.iec61850.nsd.utilities,
org.eclipse.ocl.xtext.oclinecore
org.eclipse.ocl.xtext.oclinecore,
org.apache.commons.lang3
/**
* Copyright (c) 2019 CentraleSupélec & EDF.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* This file is part of the RiseClipse tool
*
* Contributors:
* Computer Science Department, CentraleSupélec
* EDF R&D
* Contacts:
* dominique.marcadet@centralesupelec.fr
* aurelie.dehouck-neveu@edf.fr
* Web site:
* http://wdi.supelec.fr/software/RiseClipse/
*/
package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator;
import java.util.HashMap;
import java.util.HashSet;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;
import fr.centralesupelec.edf.riseclipse.iec61850.nsd.AbstractLNClass;
import fr.centralesupelec.edf.riseclipse.iec61850.nsd.DataObject;
import fr.centralesupelec.edf.riseclipse.iec61850.nsd.LNClass;
import fr.centralesupelec.edf.riseclipse.iec61850.nsd.AnyLNClass;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.AnyLN;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.DOI;
import fr.centralesupelec.edf.riseclipse.util.AbstractRiseClipseConsole;
public class AnyLNValidator {
private String lnClass;
private HashMap< String, DataObject > doMap;
private HashMap< String, DOIValidator > cdcMap;
public AnyLNValidator( LNClass lnClass ) {
this.lnClass = lnClass.getName();
this.doMap = new HashMap<>(); // link between DOI (name) and its respective DataObject
this.cdcMap = new HashMap<>(); // link between CDC (name) and its respective DOIValidator
generateValidators( doMap, cdcMap, lnClass );
// LNClass hierarchy taken into account
AbstractLNClass parent = lnClass.getRefersToAbstractLNClass();
while( parent != null ) {
generateValidators( doMap, cdcMap, parent );
parent = parent.getRefersToAbstractLNClass();
}
}
public void generateValidators( HashMap< String, DataObject > doMap, HashMap< String, DOIValidator > cdcMap, AnyLNClass lnClass ) {
for( DataObject dObj : lnClass.getDataObject() ) {
doMap.put( dObj.getName(), dObj );
if( dObj.getRefersToCDC() != null ) {
if( ! cdcMap.containsKey( dObj.getRefersToCDC().getName() )) {
cdcMap.put( dObj.getRefersToCDC().getName(), new DOIValidator( dObj.getRefersToCDC() ));
}
}
}
}
public boolean validateLN( AnyLN ln, DiagnosticChain diagnostics ) {
boolean res = true;
HashSet< String > checkedDO = new HashSet<>();
for( DOI doi : ln.getDOI() ) {
AbstractRiseClipseConsole.getConsole().verbose( "validateDOI( " + doi.getName() + " )" );
// Test if DOI is a possible DOI in this LN
if( ! doMap.containsKey( doi.getName() ) ) {
diagnostics.add( new BasicDiagnostic(
Diagnostic.ERROR,
RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
0,
"DO " + doi.getName() + " in LN at line " + ln.getLineNumber() + " not found in LNClass " + ln.getLnClass(),
new Object[] { ln } ));
continue;
}
// Control of DOI presence in LN
updateCompulsory( doi, checkedDO, diagnostics );
// Validation of DOI content
if( ! validateDOI( doi, diagnostics ) ) {
res = false;
}
}
// Verify all necessary DOI were present
if( ! doMap.values().stream()
.map( x -> checkCompulsory( ln, x, checkedDO, diagnostics ))
.reduce( ( a, b ) -> a && b ).get() ) {
diagnostics.add( new BasicDiagnostic(
Diagnostic.ERROR,
RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
0,
"LN at line " + ln.getLineNumber() + " does not contain all mandatory DO from class " + ln.getLnClass(),
new Object[] { ln } ));
res = false;
}
return res;
}
public boolean checkCompulsory( AnyLN ln, DataObject dataObject, HashSet< String > checkedDO, DiagnosticChain diagnostics ) {
switch( dataObject.getPresCond() ) {
case "M":
if( ! checkedDO.contains( dataObject.getName() ) ) {
diagnostics.add( new BasicDiagnostic(
Diagnostic.ERROR,
RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
0,
"DO " + dataObject.getName() + " is missing in LN at line " + ln.getLineNumber(),
new Object[] { ln } ));
return false;
}
}
return true;
}
public boolean updateCompulsory( DOI doi, HashSet< String > checkedDO, DiagnosticChain diagnostics ) {
switch( doMap.get( doi.getName() ).getPresCond() ) {
case "M":
case "O":
if( checkedDO.contains( doi.getName() )) {
diagnostics.add( new BasicDiagnostic(
Diagnostic.ERROR,
RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
0,
"DO " + doi + " cannot appear more than once in LN at line " + doi.getParentAnyLN().getLineNumber(),
new Object[] { doi } ));
return false;
}
checkedDO.add( doi.getName() );
break;
case "F":
diagnostics.add( new BasicDiagnostic(
Diagnostic.ERROR,
RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
0,
"DO " + doi + " is forbidden in LN at line " + doi.getParentAnyLN().getLineNumber(),
new Object[] { doi } ));
return false;
}
return true;
}
public boolean validateDOI( DOI doi, DiagnosticChain diagnostics ) {
AbstractRiseClipseConsole.getConsole().verbose( "found DO " + doi.getName() + " in LNClass " + lnClass );
// DOIValidator validates DOI content
String cdc = doMap.get( doi.getName() ).getRefersToCDC().getName();
return cdcMap.get( cdc ).validateDOI( doi, diagnostics );
}
}
/**
* Copyright (c) 2019 CentraleSupélec & EDF.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* This file is part of the RiseClipse tool
*
* Contributors:
* Computer Science Department, CentraleSupélec
* EDF R&D
* Contacts:
* dominique.marcadet@centralesupelec.fr
* aurelie.dehouck-neveu@edf.fr
* Web site:
* http://wdi.supelec.fr/software/RiseClipse/
*/
package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EValidator;
import fr.centralesupelec.edf.riseclipse.iec61850.nsd.LNClass;
import fr.centralesupelec.edf.riseclipse.iec61850.nsd.util.NsdResourceSetImpl;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.AnyLN;
import fr.centralesupelec.edf.riseclipse.util.AbstractRiseClipseConsole;
public class NsdEObjectValidator implements EValidator {
private NsdResourceSetImpl nsdResourceSet;
private HashMap< String, AnyLNValidator > lnMap;
public NsdEObjectValidator( NsdResourceSetImpl nsdResourceSet ) {
this.nsdResourceSet = nsdResourceSet;
}
public void initializeValidationData() {
this.lnMap = this.nsdResourceSet.getLNClassStream()
.map( lnClass -> generateValidators( lnClass ) )
.reduce( ( a, b ) -> {
a.putAll( b );
return a;
} ).get();
}
private HashMap< String, AnyLNValidator > generateValidators( LNClass lnClass ) {
HashMap< String, AnyLNValidator > lnMap = new HashMap<>();
lnMap.put( lnClass.getName(), new AnyLNValidator( lnClass ));
return lnMap;
}
@Override
public boolean validate( EObject eObject, DiagnosticChain diagnostics, Map< Object, Object > context ) {
return validate( eObject.eClass(), eObject, diagnostics, context );
}
@Override
public boolean validate( EClass eClass, EObject eObject, DiagnosticChain diagnostics, Map< Object, Object > context ) {
if( this.lnMap == null ) {
this.initializeValidationData();
}
switch( eClass.getName() ) {
case "LN0":
case "LN":
AnyLN ln = ( AnyLN ) eObject;
return validateLN( ln, diagnostics );
default:
AbstractRiseClipseConsole.getConsole().info( "NOT IMPLEMENTED: NsdEObjectValidator.validate( " + eClass.getName() + " )" );
return false;
}
}
@Override
public boolean validate( EDataType eDataType, Object value, DiagnosticChain diagnostics, Map< Object, Object > context ) {
AbstractRiseClipseConsole.getConsole().info( "NOT IMPLEMENTED: NsdEObjectValidator.validate( " + eDataType.getName() + " )" );
// TODO: use nsdResource to validate value
return true;
}
public boolean validateLN( AnyLN ln, DiagnosticChain diagnostics ) {
AbstractRiseClipseConsole.getConsole().verbose( "" );
AbstractRiseClipseConsole.getConsole().verbose( "NsdEObjectValidator.validateLN( " + ln.getLnClass() + " )" );
// Check that LN has valid LNClass
if( ! this.lnMap.containsKey( ln.getLnClass() )) {
diagnostics.add( new BasicDiagnostic(
Diagnostic.ERROR,
RiseClipseValidatorSCL.DIAGNOSTIC_SOURCE,
0,
"LNClass " + ln.getLnClass() + " not found in NSD files for LN at line " + ln.getLineNumber(),
new Object[] { ln } ));
return false;
}
AbstractRiseClipseConsole.getConsole().verbose( "found LNClass " + ln.getLnClass() + " in NSD files for LN at line " + ln.getLineNumber() );
// AnyLNValidator validates LN content
return lnMap.get( ln.getLnClass() ).validateLN( ln, diagnostics );
}
}
/**
* Copyright (c) 2019 CentraleSupélec & EDF.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* This file is part of the RiseClipse tool
*
* Contributors:
* Computer Science Department, CentraleSupélec
* EDF R&D
* Contacts:
* dominique.marcadet@centralesupelec.fr
* aurelie.dehouck-neveu@edf.fr
* Web site:
* http://wdi.supelec.fr/software/RiseClipse/
*/
package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.nsd;
import java.util.HashMap;
import java.util.HashSet;
import java.util.stream.Stream;
import org.eclipse.emf.common.util.DiagnosticChain;
import fr.centralesupelec.edf.riseclipse.iec61850.nsd.CDC;
import fr.centralesupelec.edf.riseclipse.iec61850.nsd.DataAttribute;
import fr.centralesupelec.edf.riseclipse.iec61850.nsd.SubDataObject;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.DA;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.DOType;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.SDO;
import fr.centralesupelec.edf.riseclipse.util.AbstractRiseClipseConsole;
public class CDCValidator {
private static HashMap< String, CDCValidator > validators = new HashMap<>();
public static CDCValidator get( String name ) {
return validators.get( name );
}
public static void buildValidators( Stream< CDC > stream ) {
stream
.forEach( cdc -> validators.put( cdc.getName(), new CDCValidator( cdc )));
}
private static HashSet< String > validatedDOType = new HashSet<>();
private DataAttributePresenceConditionValidator dataAttributePresenceConditionValidator;
private SubDataObjectPresenceConditionValidator subDataObjectPresenceConditionValidator;
private HashMap< String, TypeValidator > dataAttributeValidatorMap = new HashMap<>();
private HashMap< String, CDCValidator > subDataObjectValidatorMap = new HashMap<>();
private CDCValidator( CDC cdc ) {
dataAttributePresenceConditionValidator = DataAttributePresenceConditionValidator.get( cdc );
subDataObjectPresenceConditionValidator = SubDataObjectPresenceConditionValidator.get( cdc );
for( DataAttribute da : cdc.getDataAttribute() ) {
TypeValidator validator = TypeValidator.get( da.getType() );
if( validator != null ) {
dataAttributeValidatorMap.put( da.getName(), validator );
}
else {
AbstractRiseClipseConsole.getConsole().warning( "[NSD setup] Type not found for DataAttribute " + da.getName() );
}
}
for( SubDataObject sdo : cdc.getSubDataObject() ) {
CDCValidator validator = CDCValidator.get( sdo.getType() );
if( validator != null ) {
subDataObjectValidatorMap.put( sdo.getName(), validator );
}
else {
AbstractRiseClipseConsole.getConsole().warning( "[NSD setup] CDC not found for SubDataObject " + sdo.getName() );
}
}
}
public boolean validateDOType( DOType doType, DiagnosticChain diagnostics ) {
if( validatedDOType.contains( doType.getId() )) return true;
AbstractRiseClipseConsole.getConsole().verbose( "[NSD validation] CDCValidator.validateDOType( " + doType.getId() + " ) at line " + doType.getLineNumber() );
validatedDOType.add( doType.getId() );
dataAttributePresenceConditionValidator.reset();
doType
.getDA()
.stream()
.forEach( d -> dataAttributePresenceConditionValidator.addModelData( d, d.getName(), diagnostics ));
boolean res = dataAttributePresenceConditionValidator.validate( doType, diagnostics );
subDataObjectPresenceConditionValidator.reset();
doType
.getSDO()
.stream()
.forEach( d -> subDataObjectPresenceConditionValidator.addModelData( d, d.getName(), diagnostics ));
res = subDataObjectPresenceConditionValidator.validate( doType, diagnostics ) && res;
for( DA da : doType.getDA() ) {
TypeValidator validator = dataAttributeValidatorMap.get( da.getName() );
if( validator != null ) {
validator.validateAbstractDataAttribute( da, diagnostics );
}
else {
// DA not allowed, error will be reported by PresenceConditionValidator
//AbstractRiseClipseConsole.getConsole().warning( "[NSD validation] while validating DOType (line " + doType.getLineNumber() + "): validator for DA " + da.getName() + " not found" );
}
}
for( SDO sdo : doType.getSDO() ) {
CDCValidator validator = subDataObjectValidatorMap.get( sdo.getName() );
if( validator != null ) {
if( sdo.getRefersToDOType() != null ) {
res = validator.validateDOType( sdo.getRefersToDOType(), diagnostics ) && res;
}
else {
AbstractRiseClipseConsole.getConsole().warning( "[NSD validation] while validating DOType (line " + doType.getLineNumber() + "): DOType for SDO " + sdo.getName() + " not found" );
}
}
else {
AbstractRiseClipseConsole.getConsole().warning( "[NSD validation] while validating DOType (line " + doType.getLineNumber() + "): validator for SDO " + sdo.getType() + " not found" );
}
}
return res;
}
}
/**
* Copyright (c) 2019 CentraleSupélec & EDF.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* This file is part of the RiseClipse tool
*
* Contributors:
* Computer Science Department, CentraleSupélec
* EDF R&D
* Contacts:
* dominique.marcadet@centralesupelec.fr
* aurelie.dehouck-neveu@edf.fr
* Web site:
* http://wdi.supelec.fr/software/RiseClipse/
*/
package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.nsd;
import java.util.HashMap;
import java.util.HashSet;
import org.eclipse.emf.common.util.DiagnosticChain;
import fr.centralesupelec.edf.riseclipse.iec61850.nsd.ConstructedAttribute;
import fr.centralesupelec.edf.riseclipse.iec61850.nsd.SubDataAttribute;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.AbstractDataAttribute;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.BDA;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.DAType;
import fr.centralesupelec.edf.riseclipse.util.AbstractRiseClipseConsole;
public class ConstructedAttributeValidator extends TypeValidator {
private static HashSet< String > validatedDAType = new HashSet<>();
private SubDataAttributePresenceConditionValidator subDataAttributePresenceConditionValidator;
private HashMap< String, TypeValidator > subDataAttributeValidatorMap = new HashMap<>();
public ConstructedAttributeValidator( ConstructedAttribute contructedAttribute ) {
subDataAttributePresenceConditionValidator = SubDataAttributePresenceConditionValidator.get( contructedAttribute );
for( SubDataAttribute sda : contructedAttribute.getSubDataAttribute() ) {
TypeValidator validator = TypeValidator.get( sda.getType() );
if( validator != null ) {
subDataAttributeValidatorMap.put( sda.getName(), validator );
}
else {
AbstractRiseClipseConsole.getConsole().warning( "[NSD setup] Type not found for DataAttribute " + sda.getName() );
}
}
}
@Override
public boolean validateAbstractDataAttribute( AbstractDataAttribute da, DiagnosticChain diagnostics ) {
AbstractRiseClipseConsole.getConsole().verbose( "[NSD validation] ConstructedAttributeValidator.validateBDA( " + da.getName() + " ) at line " + da.getLineNumber() );
boolean res = true;
if( da.getRefersToDAType() != null ) {
res = validateDAType( da.getRefersToDAType(), diagnostics ) && res;
}
return res;
}
private boolean validateDAType( DAType daType, DiagnosticChain diagnostics ) {
if( validatedDAType.contains( daType.getId() )) return true;
AbstractRiseClipseConsole.getConsole().verbose( "[NSD validation] ConstructedAttributeValidator.validateDAType( " + daType.getId() + " ) at line " + daType.getLineNumber() );
validatedDAType.add( daType.getId() );
subDataAttributePresenceConditionValidator.reset();
daType
.getBDA()
.stream()
.forEach( bda -> subDataAttributePresenceConditionValidator.addModelData( bda, bda.getName(), diagnostics ));
boolean res = subDataAttributePresenceConditionValidator.validate( daType, diagnostics );
for( BDA bda : daType.getBDA() ) {
TypeValidator validator = subDataAttributeValidatorMap.get( bda.getName() );
if( validator != null ) {
validator.validateAbstractDataAttribute( bda, diagnostics );
}
else {
// BDA not allowed, error will be reported by PresenceConditionValidator
//AbstractRiseClipseConsole.getConsole().warning( "[NSD validation] while validating DAType (line " + daType.getLineNumber() + "): validator for BDA " + bda.getName() + " not found" );
}
}
return res;
}
}
/**
* Copyright (c) 2019 CentraleSupélec & EDF.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* This file is part of the RiseClipse tool
*
* Contributors:
* Computer Science Department, CentraleSupélec
* EDF R&D
* Contacts:
* dominique.marcadet@centralesupelec.fr
* aurelie.dehouck-neveu@edf.fr
* Web site:
* http://wdi.supelec.fr/software/RiseClipse/
*/
package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.nsd;
import java.util.HashMap;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.DiagnosticChain;
import org.eclipse.emf.common.util.EList;
import org.eclipse.jdt.annotation.Nullable;
import fr.centralesupelec.edf.riseclipse.iec61850.nsd.CDC;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.AbstractDataObject;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.DA;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.DO;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.DOType;
import fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.RiseClipseValidatorSCL;
public class DataAttributePresenceConditionValidator extends GenericPresenceConditionValidator< CDC, DOType, @Nullable DA >{
private static HashMap< String, DataAttributePresenceConditionValidator > validators = new HashMap<>();
public static DataAttributePresenceConditionValidator get( CDC cdc ) {
if( ! validators.containsKey( cdc.getName() )) {
validators.put( cdc.getName(), new DataAttributePresenceConditionValidator( cdc ));
}
return validators.get( cdc.getName() );
}
private CDC cdc;
public DataAttributePresenceConditionValidator( CDC cdc ) {
super( cdc );
this.cdc = cdc;
}
@Override
protected void createSpecifications( CDC cdc ) {
cdc
.getDataAttribute()
.stream()
.forEach( da -> addSpecification( da.getName(), da.getPresCond(), da.getPresCondArgs(), da.getRefersToPresCondArgsDoc() ));
}
@Override
protected String getPresenceConditionValidatorName() {
return "DataAttributePresenceConditionValidator";
}
@Override
protected String getNsdModelName() {
return cdc.getName();
}
@Override
protected String getNsdModelClassName() {
return "CDC";
}
@Override
protected String getNsdComponentClassName() {
return "DataAttribute";
}
@Override
protected String getSclModelClassName() {
return "DOType";
}
@Override
protected String getSclComponentClassName() {
return "DA";
}
@Override