diff --git a/Makefile b/Makefile index d7f48bcf..1a45f922 100644 --- a/Makefile +++ b/Makefile @@ -86,6 +86,7 @@ INSTALL_PREFIX = ./.install endif LIB_API_HEADER_FILES = hal/inc/hal_time.h +LIB_API_HEADER_FILES += hal/inc/hal_base.h LIB_API_HEADER_FILES += hal/inc/hal_thread.h LIB_API_HEADER_FILES += hal/inc/hal_filesystem.h LIB_API_HEADER_FILES += hal/inc/tls_config.h @@ -102,6 +103,9 @@ LIB_API_HEADER_FILES += src/iec61850/inc/iec61850_dynamic_model.h LIB_API_HEADER_FILES += src/iec61850/inc/iec61850_config_file_parser.h LIB_API_HEADER_FILES += src/mms/inc/mms_value.h LIB_API_HEADER_FILES += src/mms/inc/mms_common.h +LIB_API_HEADER_FILES += src/mms/inc/mms_server.h +LIB_API_HEADER_FILES += src/mms/inc/mms_device_model.h +LIB_API_HEADER_FILES += src/mms/inc/mms_named_variable_list.h LIB_API_HEADER_FILES += src/mms/inc/mms_types.h LIB_API_HEADER_FILES += src/mms/inc/mms_type_spec.h LIB_API_HEADER_FILES += src/mms/inc/mms_client_connection.h diff --git a/javaiec61850/IEC61850Client.java b/javaiec61850/IEC61850Client.java new file mode 100644 index 00000000..e3a7c53c --- /dev/null +++ b/javaiec61850/IEC61850Client.java @@ -0,0 +1,87 @@ +/* + * Copyright Waterford Institute of Technology 2017, + * Telecommunications Software and Systems Group (TSSG), + * Author Miguel Ponce de Leon . + * +*/ + +public class IEC61850Client { + + private int tcpPort = 8102; + private SWIGTYPE_p_sIedConnection con; + + // List of int values for Client Side errors from iec61850_client.h + private int[] error = {0, 1, 2,3, 4, 5, 10, 11, 12, 13, 21, 22, 23, 24,25,26, 27, 28, 29, 30, 31, 32, 33, 98, 99}; + private IedConnectionState conState = null; + private String theVal = null; + private FunctionalConstraint theValType = null; + private float temperatureValue; + private float newValue; + private LastApplError err = null; + private int theExpectedError = 21; + private float temperatureSetpoint; + + IEC61850Client() { + + if (con == null) { + con = iec61850.IedConnection_create(); + } + + iec61850.IedConnection_connect(con, error, "localhost", tcpPort); + + conState = iec61850.IedConnection_getState(con); + + if ( conState.toString() == "IED_STATE_CONNECTED" || conState.toString() == "IED_STATE_IDLE") { + System.out.println("IEC61850Client has connected to the IEC61850Server" ); + + //Accessing to SAV values + theVal = "testmodelSENSORS/TTMP1.TmpSv.instMag.f"; + theValType = FunctionalConstraint.IEC61850_FC_MX; + + temperatureValue = iec61850.IedConnection_readFloatValue(con, error, theVal, theValType); + + if (temperatureValue == 0) { + System.out.println("IEC61850Client has read testmodelSENSORS" ); + } + + newValue = temperatureValue + 10; + + iec61850.IedConnection_writeFloatValue(con, error, theVal, theValType, newValue); + + err = iec61850.IedConnection_getLastApplError(con); + + if (err.getError() == theExpectedError) { + System.out.println("IEC61850Client caught the expected client error = " + err.getError() ); + } else { + System.out.println("IEC61850Client caught a error = " + err.getError() ); + } + + // Accessing to ASG values + theVal = "testmodelSENSORS/TTMP1.TmpSp.setMag.f"; + theValType = FunctionalConstraint.IEC61850_FC_SP; + temperatureSetpoint = iec61850.IedConnection_readFloatValue(con, error, theVal, theValType); + System.out.println("IEC61850Client read a temperature set point of = " + temperatureSetpoint ); + + newValue = temperatureValue + 10; + iec61850.IedConnection_writeFloatValue(con, error, theVal, theValType, newValue); + + temperatureSetpoint = iec61850.IedConnection_readFloatValue(con, error, theVal, theValType); + + if (temperatureSetpoint == newValue) { + System.out.println("IEC61850Client read a temperature set point of = " + temperatureSetpoint ); + } + + //Must close the connection at the end of the test. + iec61850.IedConnection_close(con); + + } else { + System.out.println("IEC61850Client connection error " + conState ); + + iec61850.IedConnection_close(con); + + } + //Destroy the connection at the end of the test. + iec61850.IedConnection_destroy(con); + + } +} diff --git a/javaiec61850/IEC61850Server.java b/javaiec61850/IEC61850Server.java new file mode 100644 index 00000000..7f805627 --- /dev/null +++ b/javaiec61850/IEC61850Server.java @@ -0,0 +1,68 @@ +/* + * Copyright Waterford Institute of Technology 2017, + * Telecommunications Software and Systems Group (TSSG), + * Author Miguel Ponce de Leon . + * +*/ + +public class IEC61850Server extends Thread { + + private sIedModel model = null; + private SWIGTYPE_p_sIedServer iedServer = null; + private int tcpPort = 8102; + + @Override + public void interrupt() { + try { + iec61850.IedServer_stop(iedServer); + iec61850.IedServer_destroy(iedServer); + iec61850.IedModel_destroy(model); + System.out.println("IEC61850Server has been Interrupted and STOPPED" ); + } catch (Exception ignored) { + } finally { + super.interrupt(); + } + } + + @Override + public void run() { + + System.out.println("MyThread for IEC61850Server has STARTED" ); + //Create a iec61850.IedModel + model = iec61850.IedModel_create("testmodel"); + + //Create a logical device + sLogicalDevice lDevice1 = iec61850.LogicalDevice_create("SENSORS", model); + + //Create nodes off this logical device + sLogicalNode lln0 = iec61850.LogicalNode_create("LLN0", lDevice1); + sLogicalNode ttmp1 = iec61850.LogicalNode_create("TTMP1", lDevice1); + + sDataObject cdc_sav = iec61850.CDC_SAV_create("TmpSv", iec61850.toModelNode(ttmp1), 0, false); + sDataObject cdc_asg = iec61850.CDC_ASG_create("TmpSp", iec61850.toModelNode(ttmp1), 0, false); + + // Create a iec61850.IedServer based on a test IedModel node + iedServer = iec61850.IedServer_create(model); + iec61850.IedServer_start(iedServer, tcpPort); + + if (!iec61850.IedServer_isRunning(iedServer)) { + System.out.println("Starting server failed! Exit.\n"); + iec61850.IedServer_destroy(iedServer); + iec61850.IedModel_destroy(model); + System.exit(0); + } + + System.out.println("IEC61850Server has STARTED" ); + + while (true) { + try { + Thread.sleep((long) 100); + } catch (InterruptedException e) { + break; + } + } + + + + } + } diff --git a/javaiec61850/iec61850.i b/javaiec61850/iec61850.i new file mode 100644 index 00000000..eebe5e79 --- /dev/null +++ b/javaiec61850/iec61850.i @@ -0,0 +1,41 @@ +/* File : iec61850.i */ +%module iec61850 +%ignore ControlObjectClient_setTestMode(ControlObjectClient self); +%ignore CDA_OperBoolean(ModelNode* parent, bool isTImeActivated); +%ignore LogicalNode_hasBufferedReports(LogicalNode* node); +%ignore LogicalNode_hasUnbufferedReports(LogicalNode* node); +%ignore MmsConnection_setIsoConnectionParameters(MmsConnection self, IsoConnectionParameters* params); +%include "stdint.i" +%include "typemaps.i" +%{ +#include +#include +#include +ModelNode* toModelNode(LogicalNode * ln) +{ + return (ModelNode*) ln; +} +char* toCharP(void * v) +{ + return (char *) v; +} +DataAttribute* toDataAttribute(ModelNode * MN) +{ return (DataAttribute*)MN;} +%} +%apply int *OUTPUT {IedClientError* error}; + +%include "libiec61850_common_api.h" +%include "iec61850_client.h" +%include "iso_connection_parameters.h" +%include "mms_client_connection.h" +%include "iso_connection_parameters.h" +%include "iec61850_common.h" +%include "mms_value.h" +%include "iec61850_model.h" +%include "iec61850_server.h" +%include "iec61850_dynamic_model.h" +%include "iec61850_cdc.h" +%include "linked_list.h" +ModelNode* toModelNode(LogicalNode *); +DataAttribute* toDataAttribute(ModelNode *); +char* toCharP(void *); diff --git a/javaiec61850/test_javaiec61850.java b/javaiec61850/test_javaiec61850.java new file mode 100644 index 00000000..adf35d6c --- /dev/null +++ b/javaiec61850/test_javaiec61850.java @@ -0,0 +1,55 @@ +/* + * Copyright Waterford Institute of Technology 2017, + * Telecommunications Software and Systems Group (TSSG), + * Author Miguel Ponce de Leon . + * +*/ + +import java.lang.System; +import java.lang.Throwable; +import java.lang.Thread; +import java.lang.String; +import java.io.*; + +/** + * This test programme should mirror the functionality laid out in the Python + * equivalent. + * + * @author Miguel Ponce de Leon + */ + +public class test_javaiec61850 { + + static { + try { + //Using loadLibrary means that LD_LIBRARY_PATH must be set + System.load("/opt/libiec61850/javaiec61850/libiec61850.so"); + } catch (UnsatisfiedLinkError e) { + System.err.println("Native code library libiec61850 failed to load.\n" + e); + System.exit(1); + } + } + + /** + * main is the main entry point into this tester application. IEC61850Server runs + * as a server in a waiting Thread and then a IEC61850Client interacts with that + * servers model. + */ + + public static void main (String[] args) throws Throwable { + + IEC61850Server iec61850Server = new IEC61850Server(); + + iec61850Server.start(); + + Thread.sleep((long) 200); + //Run the test IEC Client + IEC61850Client testIECClient = new IEC61850Client(); + + //Stop the iec61850Server + if (iec61850Server != null) { + iec61850Server.interrupt(); + } + } + +} diff --git a/javaiec61850/tutorial.md b/javaiec61850/tutorial.md new file mode 100644 index 00000000..bf7fbc4d --- /dev/null +++ b/javaiec61850/tutorial.md @@ -0,0 +1,79 @@ +# Building +Before building you should install swig and java. + +From the root directory of the libiec61850 project you have to build some of the dependant libraries + +> cd .. +> +> make clean +> +> make INSTALL_PREFIX=/usr/local install +> +> make clean +> +> make INSTALL_PREFIX=/usr/local dynlib + +Now change into the Java project subdirectory + +> cd javaiec61850 +> +> swig -java -I/usr/local/include iec61850.i +> +> gcc -fpic -I/usr/lib/jvm/default-java/include -I/usr/lib/jvm/default-java/include/linux -c iec61850_wrap.c +> +> ld -shared ../build/libiec61850.so iec61850_wrap.o -o libiec61850.so +> +> javac test_javaiec61850.java +> +> java test_javaiec61850 + +This test programme should show an out put of + +```sh +MyThread for IEC61850Server has STARTED +IEC61850Server has STARTED +IEC61850Client has connected to the IEC61850Server +IEC61850Client has read testmodelSENSORS +IEC61850Client caught a error = 0 +IEC61850Client read a temperature set point of = 0.0 +IEC61850Client read a temperature set point of = 10.0 +IEC61850Server has been Interrupted and STOPPED + +``` + +The CMakeLists file does not exist yet, however it would be a nice to have a build of the java bindings by turning on a BUILD\_JAVA\_BINDINGS flag in CMake from cmake-gui or in command line: +```sh +$ cmake -DBUILD_JAVA_BINDINGS=ON . +``` +Then compile the library and install it. + +javaiec61850 library is to be imported calling +```java +import iec61850 +``` + +# Client tutorial + +The java bindings works similarly to the python and basic C library ones. + +Have a look at the Client, Server and test programme. + +For example to create a connection, call: + +```java + if (con == null) { + con = iec61850.IedConnection_create(); + } + + iec61850.IedConnection_connect(con, error, "localhost", tcpPort); + + conState = iec61850.IedConnection_getState(con); + + if ( conState.toString() == "IED_STATE_CONNECTED" || conState.toString() == "IED_STATE_IDLE") { + System.out.println("IEC61850Client has connected to the IEC61850Server" ); + } + +``` + +Copyright Waterford Institute of Technology 2017~2019, Telecommunications Software and Systems Group (TSSG), Author Miguel Ponce de Leon +This work is supported by European Union’s Horizon 2020 research and innovation programme under grant agreement No 727481, project RE-SERVE (Re- newables in a Stable Electric Grid) diff --git a/src/mms/inc/mms_device_model.h b/src/mms/inc/mms_device_model.h new file mode 100644 index 00000000..f4c9be1d --- /dev/null +++ b/src/mms/inc/mms_device_model.h @@ -0,0 +1,216 @@ +/* + * mms_model.h + * + * Copyright 2013 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ + +#ifndef MMS_DEVICE_MODEL_H_ +#define MMS_DEVICE_MODEL_H_ + +#include "libiec61850_common_api.h" + +#include "mms_type_spec.h" +#include "mms_common.h" +#include "mms_named_variable_list.h" +#include "logging_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char* deviceName; + + /* MMS VMD scope variables support */ + int namedVariablesCount; + MmsVariableSpecification** namedVariables; + + /* MMS VMD scope named variables list support */ + LinkedList /**/ namedVariableLists; + + /* MMS domain support */ + int domainCount; + MmsDomain** domains; +} MmsDevice; + + +struct sMmsJournal { + char* name; + LogStorage logStorage; +}; + +typedef struct sMmsJournal* MmsJournal; + +/* + * Server side data structure to hold information of an MMS domain (Logical Device) + */ +struct sMmsDomain { + char* domainName; + int namedVariablesCount; + MmsVariableSpecification** namedVariables; + LinkedList /**/ namedVariableLists; + LinkedList /* */ journals; +}; + +/** + * @defgroup mms_server_api_group MMS server API (for IEC 61850 use IEC 61850 server API instead!) + * + * @{ + */ + +/** + * \brief Create a new MmsDomain instance + * + * This method should not be invoked by client code! + * + * \param domainName the name of the MMS domain + * + * \return the new MmsDomain instance + */ +MmsDomain* +MmsDomain_create(char* domainName); + +char* +MmsDomain_getName(MmsDomain* self); + +void +MmsDomain_addJournal(MmsDomain* self, const char* name); + +MmsJournal +MmsDomain_getJournal(MmsDomain* self, const char* name); + +/** + * Delete a MmsDomain instance + * + * This method should not be invoked by client code! + */ +void +MmsDomain_destroy(MmsDomain* self); + +/** + * \brief Add a new MMS Named Variable List (Data set) to a MmsDomain instance + * + * The passed MmsNamedVariableList instance will be handled by the MmsDomain instance + * and will be destroyed if the MmsDomain_destroy function on this MmsDomain instance + * is called. + * + * \param self instance of MmsDomain to operate on + * \param variableList new named variable list that will be added to this MmsDomain + * + * \return true if operation was successful. + */ +bool +MmsDomain_addNamedVariableList(MmsDomain* self, MmsNamedVariableList variableList); + +/** + * \brief Delete a MMS Named Variable List from this MmsDomain instance + * + * A call to this function will also destroy the MmsNamedVariableList instance. + * + * \param self instance of MmsDomain to operate on + * \param variableListName the name of the variable list to delete. + * + */ +void +MmsDomain_deleteNamedVariableList(MmsDomain* self, char* variableListName); + +MmsNamedVariableList +MmsDomain_getNamedVariableList(MmsDomain* self, const char* variableListName); + +LinkedList +MmsDomain_getNamedVariableLists(MmsDomain* self); + +LinkedList +MmsDomain_getNamedVariableListValues(MmsDomain* self, char* variableListName); + +LinkedList +MmsDomain_createNamedVariableListValues(MmsDomain* self, char* variableListName); + +/** + * \brief Get the MmsTypeSpecification instance of a MMS named variable + * + * \param self instance of MmsDomain to operate on + * \param nameId name of the named variable + * + * \return MmsTypeSpecification instance of the named variable + */ +MmsVariableSpecification* +MmsDomain_getNamedVariable(MmsDomain* self, char* nameId); + +/** + * \brief Create a new MmsDevice instance. + * + * MmsDevice objects are the root objects of the address space of an MMS server. + * + * An MmsDevice object can contain one or more MmsDomain instances. + * + * \param deviceName the name of the MMS device or NULL if the device has no name. + * + * \return the new MmsDevice instance + */ +MmsDevice* +MmsDevice_create(char* deviceName); + +/** + * \brief Delete the MmsDevice instance + */ +void +MmsDevice_destroy(MmsDevice* self); + +/** + * \brief Get the MmsDomain object with the specified MMS domain name. + * + * \param deviceName the name of the MMS device or NULL if the device has no name. + * + * \return the new MmsDevice instance + */ +MmsDomain* +MmsDevice_getDomain(MmsDevice* self, char* domainId); + +/** + * \brief Get the MmsTypeSpecification instance of a MMS named variable of VMD scope + * + * \param self instance of MmsDevice to operate on + * \param variableName name of the named variable + * + * \return MmsTypeSpecification instance of the named variable + */ +MmsVariableSpecification* +MmsDevice_getNamedVariable(MmsDevice* self, char* variableName); + +LinkedList +MmsDevice_getNamedVariableLists(MmsDevice* self); + +MmsNamedVariableList +MmsDevice_getNamedVariableListWithName(MmsDevice* self, const char* variableListName); + +MmsJournal +MmsJournal_create(const char* name); + +void +MmsJournal_destroy(MmsJournal self); + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* MMS_DEVICE_MODEL_H_ */ diff --git a/src/mms/inc/mms_named_variable_list.h b/src/mms/inc/mms_named_variable_list.h new file mode 100644 index 00000000..15e7d16a --- /dev/null +++ b/src/mms/inc/mms_named_variable_list.h @@ -0,0 +1,85 @@ +/* + * mms_named_variable_list.h + * + * Copyright 2013 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ + +#ifndef MMS_NAMED_VARIABLE_LIST_H_ +#define MMS_NAMED_VARIABLE_LIST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \addtogroup mms_server_api_group + * @{ + */ + +#include "libiec61850_common_api.h" +#include "linked_list.h" +#include "mms_common.h" + +struct sMmsNamedVariableList { + bool deletable; + MmsDomain* domain; + char* name; + LinkedList listOfVariables; +}; + +MmsNamedVariableListEntry +MmsNamedVariableListEntry_create(MmsAccessSpecifier accessSpecifier); + +void +MmsNamedVariableListEntry_destroy(MmsNamedVariableListEntry self); + +MmsDomain* +MmsNamedVariableListEntry_getDomain(MmsNamedVariableListEntry self); + +char* +MmsNamedVariableListEntry_getVariableName(MmsNamedVariableListEntry self); + +MmsNamedVariableList +MmsNamedVariableList_create(MmsDomain* domain, char* name, bool deletable); + +char* +MmsNamedVariableList_getName(MmsNamedVariableList self); + +MmsDomain* +MmsNamedVariableList_getDomain(MmsNamedVariableList self); + +bool +MmsNamedVariableList_isDeletable(MmsNamedVariableList self); + +void +MmsNamedVariableList_addVariable(MmsNamedVariableList self, MmsNamedVariableListEntry variable); + +LinkedList +MmsNamedVariableList_getVariableList(MmsNamedVariableList self); + +void +MmsNamedVariableList_destroy(MmsNamedVariableList self); + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif /* MMS_NAMED_VARIABLE_LIST_H_ */